Merge Birch and m-c
authorPhil Ringnalda <philringnalda@gmail.com>
Fri, 03 May 2013 19:38:56 -0700
changeset 141727 69008b1fd6eba1ed5041c8d8796800ced916d1a3
parent 141726 5140723575f55f434cf237aa1161159317a246da (current diff)
parent 141704 1e00967c5786041e818dc1478de2f1df2ec1e0ae (diff)
child 141728 f5c84fb4167fd3731c64b706eb088c8d476aeade
child 141776 f58eca3886bdc503a3eed66ebd9dee8605b1304a
child 141860 a56432a42a41deeb992fccd8813bbae577a062e7
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone23.0a1
first release with
nightly linux32
69008b1fd6eb / 23.0a1 / 20130504030913 / files
nightly linux64
69008b1fd6eb / 23.0a1 / 20130504030913 / files
nightly mac
69008b1fd6eb / 23.0a1 / 20130504030913 / files
nightly win32
69008b1fd6eb / 23.0a1 / 20130504030913 / files
nightly win64
69008b1fd6eb / 23.0a1 / 20130504030913 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge Birch and m-c
browser/base/content/test/social/browser_social_markButton.js
browser/base/content/test/social/social_mark_image.png
--- a/accessible/public/moz.build
+++ b/accessible/public/moz.build
@@ -5,40 +5,40 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     DIRS += ['msaa', 'ia2']
 
 XPIDL_SOURCES += [
     'nsIAccessible.idl',
     'nsIAccessibleApplication.idl',
+    'nsIAccessibleCaretMoveEvent.idl',
     'nsIAccessibleCursorable.idl',
     'nsIAccessibleDocument.idl',
     'nsIAccessibleEditableText.idl',
-    'nsIAccessibleCaretMoveEvent.idl',
     'nsIAccessibleEvent.idl',
     'nsIAccessibleHideEvent.idl',
-    'nsIAccessibleStateChangeEvent.idl',
-    'nsIAccessibleTableChangeEvent.idl',
-    'nsIAccessibleTextChangeEvent.idl',
-    'nsIAccessibleVirtualCursorChangeEvent.idl',
     'nsIAccessibleHyperLink.idl',
     'nsIAccessibleHyperText.idl',
     'nsIAccessibleImage.idl',
     'nsIAccessiblePivot.idl',
     'nsIAccessibleProvider.idl',
     'nsIAccessibleRelation.idl',
     'nsIAccessibleRetrieval.idl',
     'nsIAccessibleRole.idl',
     'nsIAccessibleSelectable.idl',
+    'nsIAccessibleStateChangeEvent.idl',
     'nsIAccessibleStates.idl',
     'nsIAccessibleTable.idl',
+    'nsIAccessibleTableChangeEvent.idl',
     'nsIAccessibleText.idl',
+    'nsIAccessibleTextChangeEvent.idl',
     'nsIAccessibleTypes.idl',
     'nsIAccessibleValue.idl',
+    'nsIAccessibleVirtualCursorChangeEvent.idl',
     'nsIXBLAccessible.idl',
 ]
 
 MODULE = 'accessibility'
 
 EXPORTS += [
     'nsIAccessibilityService.h',
 ]
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -1073,38 +1073,25 @@ HyperTextAccessible::GetTextAtOffset(int
   EWordMovementType wordMovementType = eDefaultBehavior;
   bool moveForwardThenBack = true;
 
   switch (aBoundaryType) {
     case BOUNDARY_CHAR:
       return GetCharAt(aOffset, eGetAt, aText, aStartOffset, aEndOffset) ?
         NS_OK : NS_ERROR_INVALID_ARG;
 
-    case BOUNDARY_WORD_START: {
-      uint32_t textLen =  CharacterCount();
-      if (offset == textLen) {
-        *aStartOffset = *aEndOffset = textLen;
-        return NS_OK;
-      }
-
+    case BOUNDARY_WORD_START:
       *aEndOffset = FindWordBoundary(offset, eDirNext, eStartWord);
       *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
       return GetText(*aStartOffset, *aEndOffset, aText);
-    }
 
-    case BOUNDARY_WORD_END: {
-      if (offset == 0) {
-        *aStartOffset = *aEndOffset = 0;
-        return NS_OK;
-      }
-
+    case BOUNDARY_WORD_END:
       *aStartOffset = FindWordBoundary(offset, eDirPrevious, eEndWord);
       *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eEndWord);
       return GetText(*aStartOffset, *aEndOffset, aText);
-    }
 
     case BOUNDARY_LINE_START:
     case BOUNDARY_LINE_END:
     case BOUNDARY_ATTRIBUTE_RANGE:
       return GetTextHelper(eGetAt, aBoundaryType, aOffset,
                            aStartOffset, aEndOffset, aText);
 
     default:
--- a/accessible/tests/mochitest/text/test_multiline.html
+++ b/accessible/tests/mochitest/text/test_multiline.html
@@ -330,22 +330,17 @@
                        "divbr", kTodo, kTodo, kTodo,
                        "editable", kOk, kOk, kOk,
                        "editablebr", kTodo, kTodo, kTodo,
                        "textarea", kOk, kOk, kOk);
       testTextAtOffset(9, BOUNDARY_WORD_START, "two ", 9, 13, IDs);
       testTextAtOffset(13, BOUNDARY_WORD_START, "words\n", 13, 19, IDs);
 
       // BOUNDARY_WORD_END
-      testTextAtOffset(0, BOUNDARY_WORD_END, "oneword", 0, 7,
-                       "div", kTodo, kOk, kTodo,
-                       "divbr", kTodo, kOk, kTodo,
-                       "editable", kTodo, kOk, kTodo,
-                       "editablebr", kTodo, kOk, kTodo,
-                       "textarea", kTodo, kOk, kTodo);
+      testTextAtOffset(0, BOUNDARY_WORD_END, "oneword", 0, 7, IDs);
       testTextAtOffset(8, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
                        "div", kOk, kOk, kOk,
                        "divbr", kTodo, kTodo, kTodo,
                        "editable", kOk, kOk, kOk,
                        "editablebr", kTodo, kTodo, kTodo,
                        "textarea", kOk, kOk, kOk);
       testTextAtOffset(9, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
                        "div", kOk, kOk, kOk,
--- a/accessible/tests/mochitest/text/test_singleline.html
+++ b/accessible/tests/mochitest/text/test_singleline.html
@@ -305,24 +305,20 @@
       testTextAtOffset(1, BOUNDARY_WORD_START, "hello ", 0, 6, IDs);
       testTextAtOffset(5, BOUNDARY_WORD_START, "hello ", 0, 6, IDs);
       testTextAtOffset(6, BOUNDARY_WORD_START, "my ", 6, 9, IDs);
       testTextAtOffset(7, BOUNDARY_WORD_START, "my ", 6, 9, IDs);
       testTextAtOffset(8, BOUNDARY_WORD_START, "my ", 6, 9, IDs);
       testTextAtOffset(9, BOUNDARY_WORD_START, "friend", 9, 15, IDs);
       testTextAtOffset(10, BOUNDARY_WORD_START, "friend", 9, 15, IDs);
       testTextAtOffset(14, BOUNDARY_WORD_START, "friend", 9, 15, IDs);
-      testTextAtOffset(15, BOUNDARY_WORD_START, "", 15, 15,
-                       "input", kOk, kOk, kOk,
-                       "div", kOk, kOk, kOk,
-                       "editable", kOk, kOk, kOk,
-                       "textarea", kTodo, kTodo, kOk);
+      testTextAtOffset(15, BOUNDARY_WORD_START, "friend", 9, 15, IDs);
 
       // BOUNDARY_WORD_END
-      testTextAtOffset(0, BOUNDARY_WORD_END, "", 0, 0, IDs);
+      testTextAtOffset(0, BOUNDARY_WORD_END, "hello", 0, 5, IDs);
       testTextAtOffset(1, BOUNDARY_WORD_END, "hello", 0, 5, IDs);
       testTextAtOffset(5, BOUNDARY_WORD_END, "hello", 0, 5, IDs);
       testTextAtOffset(6, BOUNDARY_WORD_END, " my", 5, 8, IDs);
       testTextAtOffset(7, BOUNDARY_WORD_END, " my", 5, 8, IDs);
       testTextAtOffset(8, BOUNDARY_WORD_END, " my", 5, 8, IDs);
       testTextAtOffset(9, BOUNDARY_WORD_END, " friend", 8, 15, IDs);
       testTextAtOffset(10, BOUNDARY_WORD_END, " friend", 8, 15, IDs);
       testTextAtOffset(14, BOUNDARY_WORD_END, " friend", 8, 15, IDs);
--- a/accessible/tests/mochitest/text/test_whitespaces.html
+++ b/accessible/tests/mochitest/text/test_whitespaces.html
@@ -328,24 +328,24 @@
                        "div", kOk, kOk, kOk,
                        "editable", kOk, kOk, kOk,
                        "textarea", kTodo, kOk, kTodo);
       testTextAtOffset(21, BOUNDARY_WORD_START, "ran", 19, 22,
                        "input", kOk, kOk, kOk,
                        "div", kOk, kOk, kOk,
                        "editable", kOk, kOk, kOk,
                        "textarea", kTodo, kOk, kTodo);
-      testTextAtOffset(22, BOUNDARY_WORD_START, "", 22, 22,
+      testTextAtOffset(22, BOUNDARY_WORD_START, "ran", 19, 22,
                        "input", kOk, kOk, kOk,
                        "div", kOk, kOk, kOk,
                        "editable", kOk, kOk, kOk,
-                       "textarea", kTodo, kTodo, kTodo);
+                       "textarea", kTodo, kOk, kTodo);
 
       // BOUNDARY_WORD_END
-      testTextAtOffset(0, BOUNDARY_WORD_END, "", 0, 0, IDs);
+      testTextAtOffset(0, BOUNDARY_WORD_END, "Brave", 0, 5, IDs);
       testTextAtOffset(4, BOUNDARY_WORD_END, "Brave", 0, 5, IDs);
       testTextAtOffset(5, BOUNDARY_WORD_END, "Brave", 0, 5, IDs);
       testTextAtOffset(6, BOUNDARY_WORD_END, " Sir", 5, 9, IDs);
       testTextAtOffset(8, BOUNDARY_WORD_END, " Sir", 5, 9, IDs);
       testTextAtOffset(9, BOUNDARY_WORD_END, " Sir", 5, 9, IDs);
       testTextAtOffset(10, BOUNDARY_WORD_END, "  Robin", 9, 16, IDs);
       testTextAtOffset(11, BOUNDARY_WORD_END, "  Robin", 9, 16, IDs);
       testTextAtOffset(15, BOUNDARY_WORD_END, "  Robin", 9, 16, IDs);
--- a/accessible/tests/mochitest/treeupdate/Makefile.in
+++ b/accessible/tests/mochitest/treeupdate/Makefile.in
@@ -22,16 +22,17 @@ MOCHITEST_A11Y_FILES =\
 		test_gencontent.html \
 		test_hidden.html \
 		test_imagemap.html \
 		test_list_editabledoc.html \
 		test_list.html \
 		test_listbox.xul \
 		test_menu.xul \
 		test_menubutton.xul \
+	test_optgroup.html \
 		test_recreation.html \
 		test_select.html \
 		test_textleaf.html \
 		test_visibility.html \
 		test_whitespace.html \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_optgroup.html
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Add and remove optgroup test</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="../events.js"></script>
+
+  <script type="application/javascript">
+
+    function addOptGroup(aID)
+    {
+      this.selectNode = getNode(aID);
+      this.select = getAccessible(this.selectNode);
+
+      this.invoke = function addOptGroup_invoke()
+      {
+        var optGroup = document.createElement("optgroup");
+        for (i = 0; i < 2; i++) {
+          var opt = document.createElement("option");
+          opt.value = i;
+          opt.text = "Option: Value " + i;
+
+          optGroup.appendChild(opt);
+        }
+
+        this.selectNode.add(optGroup, null);
+        var option = document.createElement("option");
+        this.selectNode.add(option, null);
+      }
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, this.select)
+      ];
+
+      this.finalCheck = function addOptGroup_finalCheck()
+      {
+        var tree =
+          { COMBOBOX: [
+            { COMBOBOX_LIST: [
+              { GROUPING: [
+                { COMBOBOX_OPTION: [
+                  { TEXT_LEAF: [] }
+                ] },
+              { COMBOBOX_OPTION: [
+                  { TEXT_LEAF: [] }
+                ] },
+              ]},
+              { COMBOBOX_OPTION: [] }
+            ] }
+          ] };
+        testAccessibleTree(this.select, tree);
+      }
+
+      this.getID = function addOptGroup_getID()
+      {
+        return "test optgroup's insertion into a select";
+      }
+    }
+
+    function removeOptGroup(aID)
+    {
+      this.selectNode = getNode(aID);
+      this.select = getAccessible(this.selectNode);
+
+      this.invoke = function removeOptGroup_invoke()
+      {
+        this.option1Node = this.selectNode.firstChild.firstChild;
+        this.selectNode.removeChild(this.selectNode.firstChild);
+      }
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, this.select)
+      ];
+
+      this.finalCheck = function removeOptGroup_finalCheck()
+      {
+        var tree =
+          { COMBOBOX: [
+            { COMBOBOX_LIST: [
+              { COMBOBOX_OPTION: [] }
+            ] }
+          ] };
+        testAccessibleTree(this.select, tree);
+        is(isAccessible(this.option1Node), false, "removed option shouldn't be accessible anymore!");
+      }
+
+      this.getID = function removeOptGroup_getID()
+      {
+        return "test optgroup's removal from a select";
+      }
+    }
+
+//    gA11yEventDumpToConsole = true;
+
+    function doTest()
+    {
+      gQueue = new eventQueue();
+
+      gQueue.push(new addOptGroup("select"));
+      gQueue.push(new removeOptGroup("select"));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=616452"
+     title="Bug 616452 - Dynamically inserted select options aren't reflected in accessible tree">
+    Bug 616452</a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <select id="select"></select>
+
+  <div id="debug"/>
+</body>
+</html>
--- a/b2g/chrome/content/forms.js
+++ b/b2g/chrome/content/forms.js
@@ -198,16 +198,18 @@ let FormAssistant = {
   ]),
 
   isKeyboardOpened: false,
   selectionStart: -1,
   selectionEnd: -1,
   scrollIntoViewTimeout: null,
   _focusedElement: null,
   _documentEncoder: null,
+  _editor: null,
+  _editing: false,
 
   get focusedElement() {
     if (this._focusedElement && Cu.isDeadWrapper(this._focusedElement))
       this._focusedElement = null;
 
     return this._focusedElement;
   },
 
@@ -223,32 +225,51 @@ let FormAssistant = {
       this.focusedElement.removeEventListener('mousedown', this);
       this.focusedElement.removeEventListener('mouseup', this);
       if (!element) {
         this.focusedElement.blur();
       }
     }
 
     this._documentEncoder = null;
+    if (this._editor) {
+      this._editor.removeEditorObserver(this);
+      this._editor = null;
+    }
 
     if (element) {
       element.addEventListener('mousedown', this);
       element.addEventListener('mouseup', this);
       if (isContentEditable(element)) {
         this._documentEncoder = getDocumentEncoder(element);
       }
+      this._editor = getPlaintextEditor(element);
+      if (this._editor) {
+        // Add a nsIEditorObserver to monitor the text content of the focused
+        // element.
+        this._editor.addEditorObserver(this);
+      }
     }
 
     this.focusedElement = element;
   },
 
   get documentEncoder() {
     return this._documentEncoder;
   },
 
+  // Implements nsIEditorObserver get notification when the text content of
+  // current input field has changed.
+  EditAction: function fa_editAction() {
+    if (this._editing) {
+      return;
+    }
+    this.sendKeyboardState(this.focusedElement);
+  },
+
   handleEvent: function fa_handleEvent(evt) {
     let target = evt.target;
 
     let range = null;
     switch (evt.type) {
       case "focus":
         if (!target) {
           break;
@@ -326,31 +347,36 @@ let FormAssistant = {
         break;
 
       case "input":
         // When the text content changes, notify the keyboard
         this.updateSelection();
         break;
 
       case "keydown":
+        // Don't monitor the text change resulting from key event.
+        this._editing = true;
+
         // We use 'setTimeout' to wait until the input element accomplishes the
-        // change in selection range
+        // change in selection range or text content.
         content.setTimeout(function() {
           this.updateSelection();
+          this._editing = false;
         }.bind(this), 0);
         break;
     }
   },
 
   receiveMessage: function fa_receiveMessage(msg) {
     let target = this.focusedElement;
     if (!target) {
       return;
     }
 
+    this._editing = true;
     let json = msg.json;
     switch (msg.name) {
       case "Forms:Input:Value": {
         target.value = json.value;
 
         let event = target.ownerDocument.createEvent('HTMLEvents');
         event.initEvent('input', true, false);
         target.dispatchEvent(event);
@@ -391,16 +417,18 @@ let FormAssistant = {
       case "Forms:SetSelectionRange":  {
         let start = json.selectionStart;
         let end =  json.selectionEnd;
         setSelectionRange(target, start, end);
         this.updateSelection();
         break;
       }
     }
+    this._editing = false;
+
   },
 
   showKeyboard: function fa_showKeyboard(target) {
     if (this.isKeyboardOpened)
       return;
 
     if (target instanceof HTMLOptionElement)
       target = target.parentNode;
@@ -520,17 +548,17 @@ function getJSON(element) {
     value = getContentEditableText(element);
   }
 
   // Until the input type=date/datetime/range have been implemented
   // let's return their real type even if the platform returns 'text'
   let attributeType = element.getAttribute("type") || "";
 
   if (attributeType) {
-    var typeLowerCase = attributeType.toLowerCase(); 
+    var typeLowerCase = attributeType.toLowerCase();
     switch (typeLowerCase) {
       case "datetime":
       case "datetime-local":
       case "range":
         type = typeLowerCase;
         break;
     }
   }
@@ -719,8 +747,32 @@ function setSelectionRange(element, star
 
     let selectionLength = end - start;
     while (getContentEditableSelectionLength(element, sel) < selectionLength) {
       sel.modify("extend", "forward", "character");
     }
   }
 }
 
+// Get nsIPlaintextEditor object from an input field
+function getPlaintextEditor(element) {
+  let editor = null;
+  // Get nsIEditor
+  if (element instanceof HTMLInputElement ||
+      element instanceof HTMLTextAreaElement) {
+    // Get from the <input> and <textarea> elements
+    editor = element.QueryInterface(Ci.nsIDOMNSEditableElement).editor;
+  } else if (isContentEditable(element)) {
+    // Get from content editable element
+    let win = element.ownerDocument.defaultView;
+    let editingSession = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsIWebNavigation)
+                            .QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsIEditingSession);
+    if (editingSession) {
+      editor = editingSession.getEditorForWindow(win);
+    }
+  }
+  if (editor) {
+    editor.QueryInterface(Ci.nsIPlaintextEditor);
+  }
+  return editor;
+}
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -32,19 +32,16 @@
                 label="&openLinkInPrivateWindowCmd.label;"
                 accesskey="&openLinkInPrivateWindowCmd.accesskey;"
                 oncommand="gContextMenu.openLinkInPrivateWindow();"/>
       <menuseparator id="context-sep-open"/>
       <menuitem id="context-bookmarklink"
                 label="&bookmarkThisLinkCmd.label;"
                 accesskey="&bookmarkThisLinkCmd.accesskey;"
                 oncommand="gContextMenu.bookmarkLink();"/>
-      <menuitem id="context-marklink"
-                accesskey="&social.marklink.accesskey;"
-                oncommand="gContextMenu.markLink();"/>
       <menuitem id="context-savelink"
                 label="&saveLinkCmd.label;"
                 accesskey="&saveLinkCmd.accesskey;"
                 oncommand="gContextMenu.saveLink();"/>
       <menuitem id="context-copyemail"
                 label="&copyEmailCmd.label;"
                 accesskey="&copyEmailCmd.accesskey;"
                 oncommand="gContextMenu.copyEmail();"/>
@@ -220,19 +217,16 @@
                 label="&stopCmd.label;"
                 accesskey="&stopCmd.accesskey;"
                 command="Browser:Stop"/>
       <menuseparator id="context-sep-stop"/>
       <menuitem id="context-bookmarkpage"
                 label="&bookmarkPageCmd2.label;"
                 accesskey="&bookmarkPageCmd2.accesskey;"
                 oncommand="gContextMenu.bookmarkThisPage();"/>
-      <menuitem id="context-markpage"
-                accesskey="&social.markpage.accesskey;"
-                command="Social:TogglePageMark"/>
       <menuitem id="context-savepage"
                 label="&savePageCmd.label;"
                 accesskey="&savePageCmd.accesskey2;"
                 oncommand="gContextMenu.savePageAs();"/>
       <menuseparator id="context-sep-viewbgimage"/>
       <menuitem id="context-viewbgimage"
                 label="&viewBGImageCmd.label;"
                 accesskey="&viewBGImageCmd.accesskey;"
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -104,17 +104,18 @@
     <command id="Tools:DevToolsConnect" oncommand="gDevToolsBrowser.openConnectScreen(gBrowser)" disabled="true" hidden="true"/>
     <command id="Tools:Sanitize"
      oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
     <command id="Tools:PrivateBrowsing"
       oncommand="OpenBrowserWindow({private: true});"/>
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
     <command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
-    <command id="Social:TogglePageMark" oncommand="SocialMark.togglePageMark();" disabled="true"/>
+    <command id="Social:SharePage" oncommand="SocialShareButton.sharePage();" disabled="true"/>
+    <command id="Social:UnsharePage" oncommand="SocialShareButton.unsharePage();"/>
     <command id="Social:ToggleSidebar" oncommand="Social.toggleSidebar();"/>
     <command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
     <command id="Social:FocusChat" oncommand="SocialChatBar.focus();" hidden="true" disabled="true"/>
     <command id="Social:Toggle" oncommand="Social.toggle();" hidden="true"/>
     <command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
   </commandset>
 
   <commandset id="placesCommands">
@@ -344,18 +345,18 @@
     <key id="manBookmarkKb" key="&bookmarksGtkCmd.commandkey;" command="Browser:ShowAllBookmarks" modifiers="accel,shift"/>
 #endif
     <key id="viewBookmarksSidebarKb" key="&bookmarksCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
 #ifdef XP_WIN
 # Cmd+I is conventially mapped to Info on MacOS X, thus it should not be
 # overridden for other purposes there.
     <key id="viewBookmarksSidebarWinKb" key="&bookmarksWinCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
 #endif
-
-    <key id="markPage" key="&markPageCmd.commandkey;" command="Social:TogglePageMark" modifiers="accel,shift"/>
+    
+    <key id="sharePage" key="&sharePageCmd.commandkey;" command="Social:SharePage" modifiers="accel,shift"/>
     <key id="focusChatBar" key="&social.chatBar.commandkey;" command="Social:FocusChat" modifiers="accel,shift"/>
 
     <key id="key_stop" keycode="VK_ESCAPE" command="Browser:Stop"/>
 
 #ifdef XP_MACOSX
     <key id="key_stop_mac" modifiers="accel" key="&stopCmd.macCommandKey;" command="Browser:Stop"/>
 #endif
 
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -1,17 +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/.
 
 // the "exported" symbols
 let SocialUI,
     SocialChatBar,
     SocialFlyout,
-    SocialMark,
+    SocialShareButton,
     SocialMenu,
     SocialToolbar,
     SocialSidebar;
 
 (function() {
 
 // The minimum sizes for the auto-resize panel code.
 const PANEL_MIN_HEIGHT = 100;
@@ -20,34 +20,34 @@ const PANEL_MIN_WIDTH = 330;
 XPCOMUtils.defineLazyModuleGetter(this, "SharedFrame",
   "resource:///modules/SharedFrame.jsm");
 
 SocialUI = {
   // Called on delayed startup to initialize the UI
   init: function SocialUI_init() {
     Services.obs.addObserver(this, "social:ambient-notification-changed", false);
     Services.obs.addObserver(this, "social:profile-changed", false);
-    Services.obs.addObserver(this, "social:page-mark-config", false);
+    Services.obs.addObserver(this, "social:recommend-info-changed", false);
     Services.obs.addObserver(this, "social:frameworker-error", false);
     Services.obs.addObserver(this, "social:provider-set", false);
     Services.obs.addObserver(this, "social:providers-changed", false);
 
     Services.prefs.addObserver("social.sidebar.open", this, false);
     Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
 
     gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler.bind(this), true, true);
 
     // Called when we enter DOM full-screen mode.
     window.addEventListener("mozfullscreenchange", function () {
       SocialSidebar.update();
       SocialChatBar.update();
     });
 
     SocialChatBar.init();
-    SocialMark.init();
+    SocialShareButton.init();
     SocialMenu.init();
     SocialToolbar.init();
     SocialSidebar.init();
 
     if (!Social.initialized) {
       Social.init();
     } else {
       // social was previously initialized, so it's not going to notify us of
@@ -56,17 +56,17 @@ SocialUI = {
       this.observe(null, "social:provider-set", Social.provider ? Social.provider.origin : null);
     }
   },
 
   // Called on window unload
   uninit: function SocialUI_uninit() {
     Services.obs.removeObserver(this, "social:ambient-notification-changed");
     Services.obs.removeObserver(this, "social:profile-changed");
-    Services.obs.removeObserver(this, "social:page-mark-config");
+    Services.obs.removeObserver(this, "social:recommend-info-changed");
     Services.obs.removeObserver(this, "social:frameworker-error");
     Services.obs.removeObserver(this, "social:provider-set");
     Services.obs.removeObserver(this, "social:providers-changed");
 
     Services.prefs.removeObserver("social.sidebar.open", this);
     Services.prefs.removeObserver("social.toast-notifications.enabled", this);
   },
 
@@ -83,17 +83,17 @@ SocialUI = {
           // Social.provider has changed (possibly to null), update any state
           // which depends on it.
           this._updateActiveUI();
           this._updateMenuItems();
 
           SocialFlyout.unload();
           SocialChatBar.update();
           SocialSidebar.update();
-          SocialMark.update();
+          SocialShareButton.update();
           SocialToolbar.update();
           SocialMenu.populate();
           break;
         case "social:providers-changed":
           // the list of providers changed - this may impact the "active" UI.
           this._updateActiveUI();
           // and the multi-provider menu
           SocialToolbar.populateProviderMenus();
@@ -104,23 +104,23 @@ SocialUI = {
           if (this._matchesCurrentProvider(data)) {
             SocialToolbar.updateButton();
             SocialMenu.populate();
           }
           break;
         case "social:profile-changed":
           if (this._matchesCurrentProvider(data)) {
             SocialToolbar.updateProfile();
-            SocialMark.update();
+            SocialShareButton.update();
             SocialChatBar.update();
           }
           break;
-        case "social:page-mark-config":
+        case "social:recommend-info-changed":
           if (this._matchesCurrentProvider(data)) {
-            SocialMark.updateMarkState();
+            SocialShareButton.updateShareState();
           }
           break;
         case "social:frameworker-error":
           if (this.enabled && Social.provider.origin == data) {
             SocialSidebar.setSidebarErrorMessage();
           }
           break;
 
@@ -561,91 +561,156 @@ SocialFlyout = {
       // that the docShell of this frame is created
       panel.firstChild.clientTop;
       Social.setErrorListener(iframe, this.setFlyoutErrorMessage.bind(this))
     }
     this.yOffset = yOffset;
   }
 }
 
-SocialMark = {
+SocialShareButton = {
   // Called once, after window load, when the Social.provider object is initialized
   init: function SSB_init() {
   },
 
-  get button() {
-    return document.getElementById("social-mark-button");
+  // Called when the Social.provider changes
+  update: function() {
+    this._updateButtonHiddenState();
+    let profileRow = document.getElementById("unsharePopupHeader");
+    let profile = SocialUI.enabled ? Social.provider.profile : null;
+    if (profile && profile.displayName) {
+      profileRow.hidden = false;
+      let portrait = document.getElementById("socialUserPortrait");
+      if (profile.portrait) {
+        portrait.setAttribute("src", profile.portrait);
+      } else {
+        portrait.removeAttribute("src");
+      }
+      let displayName = document.getElementById("socialUserDisplayName");
+      displayName.setAttribute("label", profile.displayName);
+    } else {
+      profileRow.hidden = true;
+    }
   },
 
-  canMarkPage: function SSB_canMarkPage(aURI) {
+  get shareButton() {
+    return document.getElementById("share-button");
+  },
+  get unsharePopup() {
+    return document.getElementById("unsharePopup");
+  },
+
+  dismissUnsharePopup: function SSB_dismissUnsharePopup() {
+    this.unsharePopup.hidePopup();
+  },
+
+  canSharePage: function SSB_canSharePage(aURI) {
     // We only allow sharing of http or https
     return aURI && (aURI.schemeIs('http') || aURI.schemeIs('https'));
   },
 
-  // Called when the Social.provider changes
-  update: function SSB_updateButtonState() {
-    let markButton = this.button;
-    // always show button if provider supports marks
-    markButton.hidden = !SocialUI.enabled || Social.provider.pageMarkInfo == null;
-    markButton.disabled = markButton.hidden || !this.canMarkPage(gBrowser.currentURI);
+  _updateButtonHiddenState: function SSB_updateButtonHiddenState() {
+    let shareButton = this.shareButton;
+    if (shareButton)
+      shareButton.hidden = !SocialUI.enabled || Social.provider.recommendInfo == null ||
+                           !Social.haveLoggedInUser() ||
+                           !this.canSharePage(gBrowser.currentURI);
 
     // also update the relevent command's disabled state so the keyboard
     // shortcut only works when available.
-    let cmd = document.getElementById("Social:TogglePageMark");
-    cmd.setAttribute("disabled", markButton.disabled ? "true" : "false");
+    let cmd = document.getElementById("Social:SharePage");
+    cmd.setAttribute("disabled", shareButton.hidden ? "true" : "false");
+  },
+
+  onClick: function SSB_onClick(aEvent) {
+    if (aEvent.button != 0)
+      return;
+
+    // Don't bubble to the textbox, to avoid unwanted selection of the address.
+    aEvent.stopPropagation();
+
+    this.sharePage();
+  },
+
+  panelShown: function SSB_panelShown(aEvent) {
+    function updateElement(id, attrs) {
+      let el = document.getElementById(id);
+      Object.keys(attrs).forEach(function(attr) {
+        el.setAttribute(attr, attrs[attr]);
+      });
+    }
+    let continueSharingButton = document.getElementById("unsharePopupContinueSharingButton");
+    continueSharingButton.focus();
+    let recommendInfo = Social.provider.recommendInfo;
+    updateElement("unsharePopupContinueSharingButton",
+                  {label: recommendInfo.messages.unshareCancelLabel,
+                   accesskey: recommendInfo.messages.unshareCancelAccessKey});
+    updateElement("unsharePopupStopSharingButton",
+                  {label: recommendInfo.messages.unshareConfirmLabel,
+                  accesskey: recommendInfo.messages.unshareConfirmAccessKey});
+    updateElement("socialUserPortrait",
+                  {"aria-label": recommendInfo.messages.portraitLabel});
+    updateElement("socialUserRecommendedText",
+                  {value: recommendInfo.messages.unshareLabel});
   },
 
-  togglePageMark: function(aCallback) {
-    if (this.button.disabled)
-      return;
-    this.toggleURIMark(gBrowser.currentURI, aCallback)
+  sharePage: function SSB_sharePage() {
+    this.unsharePopup.hidden = false;
+
+    let uri = gBrowser.currentURI;
+    if (!Social.isPageShared(uri)) {
+      Social.sharePage(uri);
+      this.updateShareState();
+    } else {
+      this.unsharePopup.openPopup(this.shareButton, "bottomcenter topright");
+    }
   },
-  
-  toggleURIMark: function(aURI, aCallback) {
-    let update = function(marked) {
-      this._updateMarkState(marked);
-      if (aCallback)
-        aCallback(marked);
-    }.bind(this);
-    Social.isURIMarked(aURI, function(marked) {
-      if (marked) {
-        Social.unmarkURI(aURI, update);
-      } else {
-        Social.markURI(aURI, update);
-      }
-    });
+
+  unsharePage: function SSB_unsharePage() {
+    Social.unsharePage(gBrowser.currentURI);
+    this.updateShareState();
+    this.dismissUnsharePopup();
   },
 
-  updateMarkState: function SSB_updateMarkState() {
-    this.update();
-    Social.isURIMarked(gBrowser.currentURI, this._updateMarkState.bind(this));
-  },
+  updateShareState: function SSB_updateShareState() {
+    this._updateButtonHiddenState();
+
+    let shareButton = this.shareButton;
+    let currentPageShared = shareButton && !shareButton.hidden && Social.isPageShared(gBrowser.currentURI);
 
-  _updateMarkState: function(currentPageMarked) {
-    // callback for isURIMarked
-    let markButton = this.button;
-    let pageMarkInfo = SocialUI.enabled ? Social.provider.pageMarkInfo : null;
+    let recommendInfo = SocialUI.enabled ? Social.provider.recommendInfo : null;
+    // Provide a11y-friendly notification of share.
+    let status = document.getElementById("share-button-status");
+    if (status) {
+      // XXX - this should also be capable of reflecting that the page was
+      // unshared (ie, it needs to manage three-states: (1) nothing done, (2)
+      // shared, (3) shared then unshared)
+      // Note that we *do* have an appropriate string from the provider for
+      // this (recommendInfo.messages.unsharedLabel) but currently lack a way of
+      // tracking this state)
+      let statusString = currentPageShared && recommendInfo ?
+                           recommendInfo.messages.sharedLabel : "";
+      status.setAttribute("value", statusString);
+    }
 
-    // Update the mark button, if present
-    if (!markButton || markButton.hidden || !pageMarkInfo)
+    // Update the share button, if present
+    if (!shareButton || shareButton.hidden)
       return;
 
     let imageURL;
-    if (!markButton.disabled && currentPageMarked) {
-      markButton.setAttribute("marked", "true");
-      markButton.setAttribute("label", pageMarkInfo.messages.markedLabel);
-      markButton.setAttribute("tooltiptext", pageMarkInfo.messages.markedTooltip);
-      imageURL = pageMarkInfo.images.marked;
+    if (currentPageShared) {
+      shareButton.setAttribute("shared", "true");
+      shareButton.setAttribute("tooltiptext", recommendInfo.messages.unshareTooltip);
+      imageURL = recommendInfo.images.unshare;
     } else {
-      markButton.removeAttribute("marked");
-      markButton.setAttribute("label", pageMarkInfo.messages.unmarkedLabel);
-      markButton.setAttribute("tooltiptext", pageMarkInfo.messages.unmarkedTooltip);
-      imageURL = pageMarkInfo.images.unmarked;
+      shareButton.removeAttribute("shared");
+      shareButton.setAttribute("tooltiptext", recommendInfo.messages.shareTooltip);
+      imageURL = recommendInfo.images.share;
     }
-    markButton.style.listStyleImage = "url(" + imageURL + ")";
+    shareButton.src = imageURL;
   }
 };
 
 SocialMenu = {
   init: function SocialMenu_init() {
   },
 
   populate: function SocialMenu_populate() {
@@ -724,22 +789,18 @@ SocialToolbar = {
       while (parent.hasChildNodes()) {
         let frame = parent.firstChild;
         SharedFrame.forgetGroup(frame.id);
         parent.removeChild(frame);
       }
 
       let tbi = document.getElementById("social-toolbar-item");
       if (tbi) {
-        // SocialMark is the last button allways
-        let next = SocialMark.button.previousSibling;
-        while (next != tbi.firstChild) {
-          tbi.removeChild(next);
-          next = SocialMark.button.previousSibling;
-        }
+        while (tbi.lastChild != tbi.firstChild)
+          tbi.removeChild(tbi.lastChild);
       }
     }
   },
 
   updateProfile: function SocialToolbar_updateProfile() {
     // Profile may not have been initialized yet, since it depends on a worker
     // response. In that case we'll be called again when it's available, via
     // social:profile-changed
@@ -877,17 +938,17 @@ SocialToolbar = {
       let ariaLabel = icon.label;
       // if there is a badge value, we must use a localizable string to insert it.
       if (badge)
         ariaLabel = gNavigatorBundle.getFormattedString("social.aria.toolbarButtonBadgeText",
                                                         [ariaLabel, badge]);
       toolbarButton.setAttribute("aria-label", ariaLabel);
     }
     let socialToolbarItem = document.getElementById("social-toolbar-item");
-    socialToolbarItem.insertBefore(toolbarButtons, SocialMark.button);
+    socialToolbarItem.appendChild(toolbarButtons);
 
     for (let frame of createdFrames) {
       if (frame.socialErrorListener) {
         frame.socialErrorListener.remove();
       }
       if (frame.docShell) {
         frame.docShell.isActive = false;
         Social.setErrorListener(frame, this.setPanelErrorMessage.bind(this));
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3400,17 +3400,17 @@ function BrowserToolboxCustomizeDone(aTo
   UpdateUrlbarSearchSplitterState();
   setUrlAndSearchBarWidthForConditionalForwardButton();
 
   // Update the urlbar
   if (gURLBar) {
     URLBarSetURI();
     XULBrowserWindow.asyncUpdateUI();
     BookmarksMenuButton.updateStarState();
-    SocialMark.updateMarkState();
+    SocialShareButton.updateShareState();
   }
 
   TabsInTitlebar.allowedBy("customizing-toolbars", true);
 
   // Re-enable parts of the UI we disabled during the dialog
   var menubar = document.getElementById("main-menubar");
   for (let childNode of menubar.childNodes)
     childNode.setAttribute("disabled", false);
@@ -3874,17 +3874,17 @@ var XULBrowserWindow = {
         this.reloadCommand.removeAttribute("disabled");
       }
 
       if (gURLBar) {
         URLBarSetURI(aLocationURI);
 
         // Update starring UI
         BookmarksMenuButton.updateStarState();
-        SocialMark.updateMarkState();
+        SocialShareButton.updateShareState();
       }
 
       // Show or hide browser chrome based on the whitelist
       if (this.hideChromeForLocation(location)) {
         document.documentElement.setAttribute("disablechrome", "true");
       } else {
         let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
         if (ss.getTabValue(gBrowser.selectedTab, "appOrigin"))
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -200,16 +200,62 @@
                   autofocus="autofocus"
                   label="&social.ok.label;"
                   accesskey="&social.ok.accesskey;"
                   oncommand="SocialUI.activationPanel.hidePopup();"/>
         </hbox>
       </vbox>
     </panel>
 
+    <panel id="unsharePopup"
+           type="arrow"
+           orient="vertical"
+           ignorekeys="true"
+           hidden="true"
+           onpopupshown="SocialShareButton.panelShown(event);"
+           consumeoutsideclicks="true"
+           level="top">
+      <!-- Note that 'label', 'accesskey', 'value' and 'aria-label' attributes
+           for many of these elements are supplied by the provider and filled
+           in at runtime
+      -->
+      <row id="unsharePopupHeader" align="center">
+        <vbox align="center">
+          <image id="socialUserPortrait" onclick="SocialUI.showProfile();"/>
+        </vbox>
+        <vbox id="unsharePopupText">
+          <button id="socialUserDisplayName" pack="start"
+                  oncommand="SocialUI.showProfile();"/>
+          <spacer flex="1"/>
+          <label id="socialUserRecommendedText"/>
+        </vbox>
+      </row>
+      <hbox id="unsharePopupBottomButtons" pack="end">
+#ifdef XP_UNIX
+        <button id="unsharePopupStopSharingButton"
+                class="unsharePopupBottomButton"
+                command="Social:UnsharePage"/>
+        <button id="unsharePopupContinueSharingButton"
+                class="unsharePopupBottomButton"
+                default="true"
+                autofocus="autofocus"
+                oncommand="SocialShareButton.dismissUnsharePopup();"/>
+#else
+        <button id="unsharePopupContinueSharingButton"
+                class="unsharePopupBottomButton"
+                default="true"
+                autofocus="autofocus"
+                oncommand="SocialShareButton.dismissUnsharePopup();"/>
+        <button id="unsharePopupStopSharingButton"
+                class="unsharePopupBottomButton"
+                command="Social:UnsharePage"/>
+#endif
+      </hbox>
+    </panel>
+
     <panel id="social-notification-panel"
            class="social-panel"
            type="arrow"
            hidden="true"
            noautofocus="true"/>
     <panel id="social-flyout-panel"
            class="social-panel"
            onpopupshown="SocialFlyout.onShown()"
@@ -562,16 +608,23 @@
             <label id="urlbar-display" value="&urlbar.switchToTab.label;"/>
           </box>
           <hbox id="urlbar-icons">
             <image id="page-report-button"
                    class="urlbar-icon"
                    hidden="true"
                    tooltiptext="&pageReportIcon.tooltip;"
                    onclick="gPopupBlockerObserver.onReportButtonClick(event);"/>
+
+            <label id="share-button-status" collapsed="true" role="status"/>
+            <image id="share-button"
+                   class="urlbar-icon"
+                   hidden="true"
+                   onclick="SocialShareButton.onClick(event);"/>
+
             <image id="go-button"
                    class="urlbar-icon"
                    tooltiptext="&goEndCap.tooltip;"
                    onclick="gURLBar.handleCommand(event);"/>
           </hbox>
           <toolbarbutton id="urlbar-go-button"
                          class="chromeclass-toolbar-additional"
                          onclick="gURLBar.handleCommand(event);"
@@ -744,19 +797,16 @@
             <menuseparator class="social-provider-menu" hidden="true"/>
             <menuitem class="social-addons-menuitem" command="Social:Addons"
                       label="&social.addons.label;"/>
             <menuitem label="&social.learnMore.label;"
                       accesskey="&social.learnMore.accesskey;"
                       oncommand="SocialUI.showLearnMore();"/>
           </menupopup>
         </toolbarbutton>
-        <toolbarbutton id="social-mark-button"
-                       class="toolbarbutton-1"
-                       command="Social:TogglePageMark"/>
       </toolbaritem>
 
       <hbox id="window-controls" hidden="true" pack="end">
         <toolbarbutton id="minimize-button"
                        tooltiptext="&fullScreenMinimize.tooltip;"
                        oncommand="window.minimize();"/>
 
         <toolbarbutton id="restore-button"
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -287,42 +287,16 @@ nsContextMenu.prototype = {
     }
 
     // BiDi UI
     this.showItem("context-sep-bidi", top.gBidiUI);
     this.showItem("context-bidi-text-direction-toggle",
                   this.onTextInput && top.gBidiUI);
     this.showItem("context-bidi-page-direction-toggle",
                   !this.onTextInput && top.gBidiUI);
-    
-    // SocialMarks
-    let marksEnabled = SocialUI.enabled && Social.provider.pageMarkInfo;
-    let enablePageMark = marksEnabled && !(this.isContentSelected ||
-                            this.onTextInput || this.onLink || this.onImage ||
-                            this.onVideo || this.onAudio || this.onSocial);
-    let enableLinkMark = marksEnabled && ((this.onLink && !this.onMailtoLink &&
-                                           !this.onSocial) || this.onPlainTextLink);
-    if (enablePageMark) {
-      Social.isURIMarked(gBrowser.currentURI, function(marked) {
-        let label = marked ? "social.unmarkpage.label" : "social.markpage.label";
-        let provider = Social.provider || Social.defaultProvider;
-        let menuLabel = gNavigatorBundle.getFormattedString(label, [provider.name]);
-        this.setItemAttr("context-markpage", "label", menuLabel);
-      }.bind(this));
-    }
-    this.showItem("context-markpage", enablePageMark);
-    if (enableLinkMark) {
-      Social.isURIMarked(this.linkURI, function(marked) {
-        let label = marked ? "social.unmarklink.label" : "social.marklink.label";
-        let provider = Social.provider || Social.defaultProvider;
-        let menuLabel = gNavigatorBundle.getFormattedString(label, [provider.name]);
-        this.setItemAttr("context-marklink", "label", menuLabel);
-      }.bind(this));
-    }
-    this.showItem("context-marklink", enableLinkMark);
   },
 
   initSpellingItems: function() {
     var canSpell = InlineSpellCheckerUI.canSpellCheck;
     var onMisspelling = InlineSpellCheckerUI.overMisspelling;
     var showUndo = canSpell && InlineSpellCheckerUI.canUndo();
     this.showItem("spell-check-enabled", canSpell);
     this.showItem("spell-separator", canSpell || this.onEditableArea);
@@ -1488,21 +1462,16 @@ nsContextMenu.prototype = {
     else {
       PlacesUIUtils.showBookmarkDialog({ action: "edit"
                                        , type: "bookmark"
                                        , itemId: itemId
                                        }, window.top);
     }
   },
 
-  markLink: function CM_markLink() {
-    // send link to social
-    SocialMark.toggleURIMark(this.linkURI);
-  },
-
   savePageAs: function CM_savePageAs() {
     saveDocument(this.browser.contentDocument);
   },
 
   printFrame: function CM_printFrame() {
     PrintUtils.print(this.target.ownerDocument.defaultView);
   },
 
--- a/browser/base/content/test/social/Makefile.in
+++ b/browser/base/content/test/social/Makefile.in
@@ -15,30 +15,30 @@ include $(DEPTH)/config/autoconf.mk
 		 blocklist.xml \
 		 blocklistEmpty.xml \
 		 browser_blocklist.js \
 		 browser_defaults.js \
 		 browser_addons.js \
                  browser_social_activation.js \
                  browser_social_perwindowPB.js \
                  browser_social_toolbar.js \
-                 browser_social_markButton.js \
+                 browser_social_shareButton.js \
                  browser_social_sidebar.js \
                  browser_social_flyout.js \
                  browser_social_mozSocial_API.js \
                  browser_social_isVisible.js \
                  browser_social_chatwindow.js \
                  browser_social_chatwindowfocus.js \
                  browser_social_multiprovider.js \
                  browser_social_errorPage.js \
                  browser_social_window.js \
                  social_activate.html \
                  social_activate_iframe.html \
                  social_panel.html \
-                 social_mark_image.png \
+                 social_share_image.png \
                  social_sidebar.html \
                  social_chat.html \
                  social_flyout.html \
                  social_window.html \
                  social_worker.js \
                  $(NULL)
 
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/browser/base/content/test/social/browser_social_markButton.js
+++ /dev/null
@@ -1,210 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-let prefName = "social.enabled",
-    gFinishCB;
-
-function test() {
-  waitForExplicitFinish();
-
-  // Need to load a http/https/ftp/ftps page for the social mark button to appear
-  let tab = gBrowser.selectedTab = gBrowser.addTab("https://example.com", {skipAnimation: true});
-  tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
-    tab.linkedBrowser.removeEventListener("load", tabLoad, true);
-    executeSoon(tabLoaded);
-  }, true);
-
-  registerCleanupFunction(function() {
-    Services.prefs.clearUserPref(prefName);
-    gBrowser.removeTab(tab);
-  });
-}
-
-function tabLoaded() {
-  ok(Social, "Social module loaded");
-
-  let manifest = { // normal provider
-    name: "provider 1",
-    origin: "https://example.com",
-    sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
-    workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
-    iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
-  };
-  runSocialTestWithProvider(manifest, function (finishcb) {
-    gFinishCB = finishcb;
-    testInitial();
-  });
-}
-
-function testInitial(finishcb) {
-  ok(Social.provider, "Social provider is active");
-  ok(Social.provider.enabled, "Social provider is enabled");
-  let port = Social.provider.getWorkerPort();
-  ok(port, "Social provider has a port to its FrameWorker");
-  port.close();
-
-  let markButton = SocialMark.button;
-  ok(markButton, "mark button exists");
-
-  // ensure the worker initialization and handshakes are all done and we
-  // have a profile and the worker has sent a page-mark-config msg.
-  waitForCondition(function() Social.provider.pageMarkInfo != null, function() {
-    is(markButton.hasAttribute("marked"), false, "SocialMark button should not have 'marked' attribute before mark button is clicked");
-    // Check the strings from our worker actually ended up on the button.
-    is(markButton.getAttribute("tooltiptext"), "Mark this page", "check tooltip text is correct");
-    // Check the relative URL was resolved correctly (note this image has offsets of zero...)
-    is(markButton.style.listStyleImage, 'url("https://example.com/browser/browser/base/content/test/social/social_mark_image.png")', "check image url is correct");
-
-    // Test the mark button command handler
-    SocialMark.togglePageMark(function() {
-      is(markButton.hasAttribute("marked"), true, "mark button should have 'marked' attribute after mark button is clicked");
-      is(markButton.getAttribute("tooltiptext"), "Unmark this page", "check tooltip text is correct");
-      // Check the URL and offsets were applied correctly
-      is(markButton.style.listStyleImage, 'url("https://example.com/browser/browser/base/content/test/social/social_mark_image.png")', "check image url is correct");
-      SocialMark.togglePageMark(function() {
-        is(markButton.hasAttribute("marked"), false, "mark button should not be marked");
-        executeSoon(function() {
-          testStillMarkedIn2Tabs();
-        });
-      });
-    });
-    markButton.click();
-  }, "provider didn't provide page-mark-config");
-}
-
-function testStillMarkedIn2Tabs() {
-  let toMark = "http://example.com";
-  let markUri = Services.io.newURI(toMark, null, null);
-  let markButton = SocialMark.button;
-  let initialTab = gBrowser.selectedTab;
-  is(markButton.hasAttribute("marked"), false, "SocialMark button should not have 'marked' for the initial tab");
-  let tab1 = gBrowser.selectedTab = gBrowser.addTab(toMark);
-  let tab1b = gBrowser.getBrowserForTab(tab1);
-
-  tab1b.addEventListener("load", function tabLoad(event) {
-    tab1b.removeEventListener("load", tabLoad, true);
-    let tab2 = gBrowser.selectedTab = gBrowser.addTab(toMark);
-    let tab2b = gBrowser.getBrowserForTab(tab2);
-    tab2b.addEventListener("load", function tabLoad(event) {
-      tab2b.removeEventListener("load", tabLoad, true);
-      // should start without either page being marked.
-      is(markButton.hasAttribute("marked"), false, "SocialMark button should not have 'marked' before we've done anything");
-      Social.isURIMarked(markUri, function(marked) {
-        ok(!marked, "page is unmarked in annotations");
-        markButton.click();
-        waitForCondition(function() markButton.hasAttribute("marked"), function() {
-          Social.isURIMarked(markUri, function(marked) {
-            ok(marked, "page is marked in annotations");
-            // and switching to the first tab (with the same URL) should still reflect marked.
-            gBrowser.selectedTab = tab1;
-            is(markButton.hasAttribute("marked"), true, "SocialMark button should reflect the marked state");
-            // but switching back the initial one should reflect not marked.
-            gBrowser.selectedTab = initialTab;
-            waitForCondition(function() !markButton.hasAttribute("marked"), function() {
-              gBrowser.selectedTab = tab1;
-    
-              SocialMark.togglePageMark(function() {
-                Social.isURIMarked(gBrowser.currentURI, function(marked) {
-                  ok(!marked, "page is unmarked in annotations");
-                  is(markButton.hasAttribute("marked"), false, "mark button should not be marked");
-                  gBrowser.removeTab(tab1);
-                  gBrowser.removeTab(tab2);
-                  executeSoon(testStillMarkedAfterReopen);
-                });
-              });
-            }, "button has been unmarked");
-          });
-        }, "button has been marked");
-      });
-
-    }, true);
-  }, true);
-}
-
-function testStillMarkedAfterReopen() {
-  let toMark = "http://example.com";
-  let markButton = SocialMark.button;
-
-  is(markButton.hasAttribute("marked"), false, "Reopen: SocialMark button should not have 'marked' for the initial tab");
-  let tab = gBrowser.selectedTab = gBrowser.addTab(toMark);
-  let tabb = gBrowser.getBrowserForTab(tab);
-  tabb.addEventListener("load", function tabLoad(event) {
-    tabb.removeEventListener("load", tabLoad, true);
-    SocialMark.togglePageMark(function() {
-      is(markButton.hasAttribute("marked"), true, "SocialMark button should reflect the marked state");
-      gBrowser.removeTab(tab);
-      // should be on the initial unmarked tab now.
-      waitForCondition(function() !markButton.hasAttribute("marked"), function() {
-        // now open the same URL - should be back to Marked.
-        tab = gBrowser.selectedTab = gBrowser.addTab(toMark, {skipAnimation: true});
-        tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
-          tab.linkedBrowser.removeEventListener("load", tabLoad, true);
-          executeSoon(function() {
-            is(markButton.hasAttribute("marked"), true, "New tab to previously marked URL should reflect marked state");
-            SocialMark.togglePageMark(function() {
-              gBrowser.removeTab(tab);
-              executeSoon(testOnlyMarkCertainUrlsTabSwitch);
-            });
-          });
-        }, true);
-      }, "button is now unmarked");
-    });
-  }, true);
-}
-
-function testOnlyMarkCertainUrlsTabSwitch() {
-  let toMark = "http://example.com";
-  let notSharable = "about:blank";
-  let markButton = SocialMark.button;
-  let tab = gBrowser.selectedTab = gBrowser.addTab(toMark);
-  let tabb = gBrowser.getBrowserForTab(tab);
-  tabb.addEventListener("load", function tabLoad(event) {
-    tabb.removeEventListener("load", tabLoad, true);
-    ok(!markButton.hidden, "SocialMark button not hidden for http url");
-    let tab2 = gBrowser.selectedTab = gBrowser.addTab(notSharable);
-    let tabb2 = gBrowser.getBrowserForTab(tab2);
-    tabb2.addEventListener("load", function tabLoad(event) {
-      tabb2.removeEventListener("load", tabLoad, true);
-      ok(markButton.disabled, "SocialMark button disabled for about:blank");
-      gBrowser.selectedTab = tab;
-      ok(!markButton.disabled, "SocialMark button re-shown when switching back to http: url");
-      gBrowser.selectedTab = tab2;
-      ok(markButton.disabled, "SocialMark button re-hidden when switching back to about:blank");
-      gBrowser.removeTab(tab);
-      gBrowser.removeTab(tab2);
-      executeSoon(testOnlyMarkCertainUrlsSameTab);
-    }, true);
-  }, true);
-}
-
-function testOnlyMarkCertainUrlsSameTab() {
-  let toMark = "http://example.com";
-  let notSharable = "about:blank";
-  let markButton = SocialMark.button;
-  let tab = gBrowser.selectedTab = gBrowser.addTab(toMark);
-  let tabb = gBrowser.getBrowserForTab(tab);
-  tabb.addEventListener("load", function tabLoad(event) {
-    tabb.removeEventListener("load", tabLoad, true);
-    ok(!markButton.disabled, "SocialMark button not disabled for http url");
-    tabb.addEventListener("load", function tabLoad(event) {
-      tabb.removeEventListener("load", tabLoad, true);
-      ok(markButton.disabled, "SocialMark button disabled for about:blank");
-      tabb.addEventListener("load", function tabLoad(event) {
-        tabb.removeEventListener("load", tabLoad, true);
-        ok(!markButton.disabled, "SocialMark button re-enabled http url");
-        gBrowser.removeTab(tab);
-        executeSoon(testDisable);
-      }, true);
-      tabb.loadURI(toMark);
-    }, true);
-    tabb.loadURI(notSharable);
-  }, true);
-}
-
-function testDisable() {
-  let markButton = SocialMark.button;
-  Services.prefs.setBoolPref(prefName, false);
-  is(markButton.hidden, true, "SocialMark button should be hidden when pref is disabled");
-  gFinishCB();
-}
--- a/browser/base/content/test/social/browser_social_multiprovider.js
+++ b/browser/base/content/test/social/browser_social_multiprovider.js
@@ -54,16 +54,20 @@ var tests = {
       });
       Social.activateFromOrigin("https://test1.example.com");
     });
   }
 }
 
 function checkUIStateMatchesProvider(provider) {
   let profileData = getExpectedProfileData(provider);
+  // Bug 789863 - share button uses 'displayName', toolbar uses 'userName'
+  // Check the "share button"
+  let displayNameEl = document.getElementById("socialUserDisplayName");
+  is(displayNameEl.getAttribute("label"), profileData.displayName, "display name matches provider profile");
   // The toolbar
   let loginStatus = document.getElementsByClassName("social-statusarea-loggedInStatus");
   for (let label of loginStatus) {
     is(label.value, profileData.userName, "username name matches provider profile");
   }
   // Sidebar
   is(document.getElementById("social-sidebar-browser").getAttribute("src"), provider.sidebarURL, "side bar URL is set");
 }
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/social/browser_social_shareButton.js
@@ -0,0 +1,328 @@
+/* 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/. */
+
+let prefName = "social.enabled",
+    gFinishCB;
+
+function test() {
+  waitForExplicitFinish();
+
+  // Need to load a http/https/ftp/ftps page for the social share button to appear
+  let tab = gBrowser.selectedTab = gBrowser.addTab("https://example.com", {skipAnimation: true});
+  tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
+    tab.linkedBrowser.removeEventListener("load", tabLoad, true);
+    executeSoon(tabLoaded);
+  }, true);
+
+  registerCleanupFunction(function() {
+    Services.prefs.clearUserPref(prefName);
+    gBrowser.removeTab(tab);
+  });
+}
+
+function tabLoaded() {
+  ok(Social, "Social module loaded");
+
+  let manifest = { // normal provider
+    name: "provider 1",
+    origin: "https://example.com",
+    sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
+    workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
+    iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
+  };
+  runSocialTestWithProvider(manifest, function (finishcb) {
+    gFinishCB = finishcb;
+    testInitial();
+  });
+}
+
+function testInitial(finishcb) {
+  ok(Social.provider, "Social provider is active");
+  ok(Social.provider.enabled, "Social provider is enabled");
+  let port = Social.provider.getWorkerPort();
+  ok(port, "Social provider has a port to its FrameWorker");
+  port.close();
+
+  let {shareButton, unsharePopup} = SocialShareButton;
+  ok(shareButton, "share button exists");
+  ok(unsharePopup, "share popup exists");
+
+  let okButton = document.getElementById("unsharePopupContinueSharingButton");
+  let undoButton = document.getElementById("unsharePopupStopSharingButton");
+  let shareStatusLabel = document.getElementById("share-button-status");
+
+  // ensure the worker initialization and handshakes are all done and we
+  // have a profile and the worker has responsed to the recommend-prompt msg.
+  waitForCondition(function() Social.provider.profile && Social.provider.recommendInfo != null, function() {
+    is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' attribute before share button is clicked");
+    // check dom values
+    let profile = Social.provider.profile;
+    let portrait = document.getElementById("socialUserPortrait").getAttribute("src");
+    is(profile.portrait, portrait, "portrait is set");
+    let displayName = document.getElementById("socialUserDisplayName");
+    is(displayName.label, profile.displayName, "display name is set");
+    ok(!document.getElementById("unsharePopupHeader").hidden, "user profile is visible");
+
+    // Check the strings from our worker actually ended up on the button.
+    is(shareButton.getAttribute("tooltiptext"), "Share this page", "check tooltip text is correct");
+    is(shareStatusLabel.getAttribute("value"), "", "check status label text is blank");
+    // Check the relative URL was resolved correctly (note this image has offsets of zero...)
+    is(shareButton.src, 'https://example.com/browser/browser/base/content/test/social/social_share_image.png', "check image url is correct");
+
+    // Test clicking the share button
+    shareButton.addEventListener("click", function listener() {
+      shareButton.removeEventListener("click", listener);
+      is(shareButton.hasAttribute("shared"), true, "Share button should have 'shared' attribute after share button is clicked");
+      is(shareButton.getAttribute("tooltiptext"), "Unshare this page", "check tooltip text is correct");
+      is(shareStatusLabel.getAttribute("value"), "This page has been shared", "check status label text is correct");
+      // Check the URL and offsets were applied correctly
+      is(shareButton.src, 'https://example.com/browser/browser/base/content/test/social/social_share_image.png', "check image url is correct");
+      executeSoon(testSecondClick.bind(window, testPopupOKButton));
+    });
+    shareButton.click();
+  }, "provider didn't provide user-recommend-prompt response");
+}
+
+function testSecondClick(nextTest) {
+  let {shareButton, unsharePopup} = SocialShareButton;
+  unsharePopup.addEventListener("popupshown", function listener() {
+    unsharePopup.removeEventListener("popupshown", listener);
+    ok(true, "popup was shown after second click");
+    executeSoon(nextTest);
+  });
+  shareButton.click();
+}
+
+function testPopupOKButton() {
+  let {shareButton, unsharePopup} = SocialShareButton;
+  let okButton = document.getElementById("unsharePopupContinueSharingButton");
+  unsharePopup.addEventListener("popuphidden", function listener() {
+    unsharePopup.removeEventListener("popuphidden", listener);
+    is(shareButton.hasAttribute("shared"), true, "Share button should still have 'shared' attribute after OK button is clicked");
+    executeSoon(testSecondClick.bind(window, testPopupUndoButton));
+  });
+  okButton.click();
+}
+
+function testPopupUndoButton() {
+  let {shareButton, unsharePopup} = SocialShareButton;
+  let undoButton = document.getElementById("unsharePopupStopSharingButton");
+  unsharePopup.addEventListener("popuphidden", function listener() {
+    unsharePopup.removeEventListener("popuphidden", listener);
+    is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' attribute after Undo button is clicked");
+    executeSoon(testShortcut);
+  });
+  undoButton.click();
+}
+
+function testShortcut() {
+  let keyTarget = window;
+  keyTarget.addEventListener("keyup", function listener() {
+    keyTarget.removeEventListener("keyup", listener);
+    executeSoon(checkShortcutWorked.bind(window, keyTarget));
+  });
+  EventUtils.synthesizeKey("l", {accelKey: true, shiftKey: true}, keyTarget);
+}
+
+function checkShortcutWorked(keyTarget) {
+  let {unsharePopup, shareButton} = SocialShareButton;
+  is(shareButton.hasAttribute("shared"), true, "Share button should be in the 'shared' state after keyboard shortcut is used");
+
+  // Test a second invocation of the shortcut
+  unsharePopup.addEventListener("popupshown", function listener() {
+    unsharePopup.removeEventListener("popupshown", listener);
+    ok(true, "popup was shown after second use of keyboard shortcut");
+    executeSoon(checkOKButton);
+  });
+  EventUtils.synthesizeKey("l", {accelKey: true, shiftKey: true}, keyTarget);
+}
+
+function checkOKButton() {
+  let okButton = document.getElementById("unsharePopupContinueSharingButton");
+  let undoButton = document.getElementById("unsharePopupStopSharingButton");
+  is(document.activeElement, okButton, "ok button should be focused by default");
+
+  // the undo button text, label text, access keys, etc  should be as
+  // specified by the provider.
+  function isEltAttr(eltid, attr, expected) {
+    is(document.getElementById(eltid).getAttribute(attr), expected,
+       "element '" + eltid + "' has correct value for attribute '" + attr + "'");
+  }
+  isEltAttr("socialUserRecommendedText", "value", "You have already shared this page");
+  isEltAttr("unsharePopupContinueSharingButton", "label", "Got it!");
+  isEltAttr("unsharePopupContinueSharingButton", "accesskey", "G");
+  isEltAttr("unsharePopupStopSharingButton", "label", "Unshare it!");
+  isEltAttr("unsharePopupStopSharingButton", "accesskey", "U");
+  isEltAttr("socialUserPortrait", "aria-label", "Your pretty face");
+
+  // This rest of particular test doesn't really apply on Mac, since buttons
+  // aren't focusable by default.
+  if (navigator.platform.contains("Mac")) {
+    executeSoon(testCloseBySpace);
+    return;
+  }
+
+  let displayName = document.getElementById("socialUserDisplayName");
+
+  // Linux has the buttons in the [unshare] [ok] order, so displayName will come first.
+  if (navigator.platform.contains("Linux")) {
+    checkNextInTabOrder(displayName, function () {
+      checkNextInTabOrder(undoButton, function () {
+        checkNextInTabOrder(okButton, testCloseBySpace);
+      });
+    });
+  } else {
+    checkNextInTabOrder(undoButton, function () {
+      checkNextInTabOrder(displayName, function () {
+        checkNextInTabOrder(okButton, testCloseBySpace);
+      });
+    });
+  }
+}
+
+function checkNextInTabOrder(element, next) {
+  function listener() {
+    element.removeEventListener("focus", listener);
+    is(document.activeElement, element, element.id + " should be next in tab order");
+    executeSoon(next);
+  }
+  element.addEventListener("focus", listener);
+  // Register a cleanup function to remove the listener in case this test fails
+  registerCleanupFunction(function () {
+    element.removeEventListener("focus", listener);
+  });
+  EventUtils.synthesizeKey("VK_TAB", {});
+}
+
+function testCloseBySpace() {
+  let unsharePopup = SocialShareButton.unsharePopup;
+  is(document.activeElement.id, "unsharePopupContinueSharingButton", "testCloseBySpace, the ok button should be focused");
+  unsharePopup.addEventListener("popuphidden", function listener() {
+    unsharePopup.removeEventListener("popuphidden", listener);
+    ok(true, "space closed the share popup");
+    executeSoon(testStillSharedIn2Tabs);
+  });
+  EventUtils.synthesizeKey("VK_SPACE", {});
+}
+
+function testStillSharedIn2Tabs() {
+  let toShare = "http://example.com";
+  let {shareButton} = SocialShareButton;
+  let initialTab = gBrowser.selectedTab;
+  if (shareButton.hasAttribute("shared")) {
+    SocialShareButton.unsharePage();
+  }
+  is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' for the initial tab");
+  let tab1 = gBrowser.selectedTab = gBrowser.addTab(toShare);
+  let tab1b = gBrowser.getBrowserForTab(tab1);
+
+  tab1b.addEventListener("load", function tabLoad(event) {
+    tab1b.removeEventListener("load", tabLoad, true);
+    let tab2 = gBrowser.selectedTab = gBrowser.addTab(toShare);
+    let tab2b = gBrowser.getBrowserForTab(tab2);
+    tab2b.addEventListener("load", function tabLoad(event) {
+      tab2b.removeEventListener("load", tabLoad, true);
+      // should start without either page being shared.
+      is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' before we've done anything");
+      shareButton.click();
+      is(shareButton.hasAttribute("shared"), true, "Share button should reflect the share");
+      // and switching to the first tab (with the same URL) should still reflect shared.
+      gBrowser.selectedTab = tab1;
+      is(shareButton.hasAttribute("shared"), true, "Share button should reflect the share");
+      // but switching back the initial one should reflect not shared.
+      gBrowser.selectedTab = initialTab;
+      is(shareButton.hasAttribute("shared"), false, "Initial tab should not reflect shared");
+
+      gBrowser.selectedTab = tab1;
+      SocialShareButton.unsharePage();
+      gBrowser.removeTab(tab1);
+      gBrowser.removeTab(tab2);
+      executeSoon(testStillSharedAfterReopen);
+    }, true);
+  }, true);
+}
+
+function testStillSharedAfterReopen() {
+  let toShare = "http://example.com";
+  let {shareButton} = SocialShareButton;
+
+  is(shareButton.hasAttribute("shared"), false, "Reopen: Share button should not have 'shared' for the initial tab");
+  let tab = gBrowser.selectedTab = gBrowser.addTab(toShare);
+  let tabb = gBrowser.getBrowserForTab(tab);
+  tabb.addEventListener("load", function tabLoad(event) {
+    tabb.removeEventListener("load", tabLoad, true);
+    SocialShareButton.sharePage();
+    is(shareButton.hasAttribute("shared"), true, "Share button should reflect the share");
+    gBrowser.removeTab(tab);
+    // should be on the initial unshared tab now.
+    is(shareButton.hasAttribute("shared"), false, "Initial tab should be selected and be unshared.");
+    // now open the same URL - should be back to shared.
+    tab = gBrowser.selectedTab = gBrowser.addTab(toShare, {skipAnimation: true});
+    tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
+      tab.linkedBrowser.removeEventListener("load", tabLoad, true);
+      executeSoon(function() {
+        is(shareButton.hasAttribute("shared"), true, "New tab to previously shared URL should reflect shared");
+        SocialShareButton.unsharePage();
+        gBrowser.removeTab(tab);
+        executeSoon(testOnlyShareCertainUrlsTabSwitch);
+      });
+    }, true);
+  }, true);
+}
+
+function testOnlyShareCertainUrlsTabSwitch() {
+  let toShare = "http://example.com";
+  let notSharable = "about:blank";
+  let {shareButton} = SocialShareButton;
+  let tab = gBrowser.selectedTab = gBrowser.addTab(toShare);
+  let tabb = gBrowser.getBrowserForTab(tab);
+  tabb.addEventListener("load", function tabLoad(event) {
+    tabb.removeEventListener("load", tabLoad, true);
+    ok(!shareButton.hidden, "share button not hidden for http url");
+    let tab2 = gBrowser.selectedTab = gBrowser.addTab(notSharable);
+    let tabb2 = gBrowser.getBrowserForTab(tab2);
+    tabb2.addEventListener("load", function tabLoad(event) {
+      tabb2.removeEventListener("load", tabLoad, true);
+      ok(shareButton.hidden, "share button hidden for about:blank");
+      gBrowser.selectedTab = tab;
+      ok(!shareButton.hidden, "share button re-shown when switching back to http: url");
+      gBrowser.selectedTab = tab2;
+      ok(shareButton.hidden, "share button re-hidden when switching back to about:blank");
+      gBrowser.removeTab(tab);
+      gBrowser.removeTab(tab2);
+      executeSoon(testOnlyShareCertainUrlsSameTab);
+    }, true);
+  }, true);
+}
+
+function testOnlyShareCertainUrlsSameTab() {
+  let toShare = "http://example.com";
+  let notSharable = "about:blank";
+  let {shareButton} = SocialShareButton;
+  let tab = gBrowser.selectedTab = gBrowser.addTab(toShare);
+  let tabb = gBrowser.getBrowserForTab(tab);
+  tabb.addEventListener("load", function tabLoad(event) {
+    tabb.removeEventListener("load", tabLoad, true);
+    ok(!shareButton.hidden, "share button not hidden for http url");
+    tabb.addEventListener("load", function tabLoad(event) {
+      tabb.removeEventListener("load", tabLoad, true);
+      ok(shareButton.hidden, "share button hidden for about:blank");
+      tabb.addEventListener("load", function tabLoad(event) {
+        tabb.removeEventListener("load", tabLoad, true);
+        ok(!shareButton.hidden, "share button re-enabled http url");
+        gBrowser.removeTab(tab);
+        executeSoon(testDisable);
+      }, true);
+      tabb.loadURI(toShare);
+    }, true);
+    tabb.loadURI(notSharable);
+  }, true);
+}
+
+function testDisable() {
+  let shareButton = SocialShareButton.shareButton;
+  Services.prefs.setBoolPref(prefName, false);
+  is(shareButton.hidden, true, "Share button should be hidden when pref is disabled");
+  gFinishCB();
+}
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -180,35 +180,33 @@ function checkSocialUI(win) {
     is(!!a, !!b, msg);
   }
   isbool(win.SocialSidebar.canShow, enabled, "social sidebar active?");
   if (enabled)
     isbool(win.SocialSidebar.opened, enabled, "social sidebar open?");
   isbool(win.SocialChatBar.isAvailable, enabled && Social.haveLoggedInUser(), "chatbar available?");
   isbool(!win.SocialChatBar.chatbar.hidden, enabled && Social.haveLoggedInUser(), "chatbar visible?");
 
-  let markVisible = enabled && provider.pageMarkInfo;
-  let canMark = markVisible && win.SocialMark.canMarkPage(win.gBrowser.currentURI);
-  isbool(!win.SocialMark.button.hidden, markVisible, "SocialMark button visible?");
-  isbool(!win.SocialMark.button.disabled, canMark, "SocialMark button enabled?");
+  let canShare = enabled && provider.recommendInfo && Social.haveLoggedInUser() && win.SocialShareButton.canSharePage(win.gBrowser.currentURI)
+  isbool(!win.SocialShareButton.shareButton.hidden, canShare, "share button visible?");
   isbool(!doc.getElementById("social-toolbar-item").hidden, active, "toolbar items visible?");
   if (active)
     is(win.SocialToolbar.button.style.listStyleImage, 'url("' + Social.defaultProvider.iconURL + '")', "toolbar button has provider icon");
   // the menus should always have the provider name
   if (provider) {
     for (let id of ["menu_socialSidebar", "menu_socialAmbientMenu"])
       is(document.getElementById(id).getAttribute("label"), Social.provider.name, "element has the provider name");
   }
 
   // and for good measure, check all the social commands.
   isbool(!doc.getElementById("Social:Toggle").hidden, active, "Social:Toggle visible?");
   isbool(!doc.getElementById("Social:ToggleNotifications").hidden, enabled, "Social:ToggleNotifications visible?");
   isbool(!doc.getElementById("Social:FocusChat").hidden, enabled && Social.haveLoggedInUser(), "Social:FocusChat visible?");
   isbool(doc.getElementById("Social:FocusChat").getAttribute("disabled"), enabled ? "false" : "true", "Social:FocusChat disabled?");
-  is(doc.getElementById("Social:TogglePageMark").getAttribute("disabled"), canMark ? "false" : "true", "Social:TogglePageMark enabled?");
+  is(doc.getElementById("Social:SharePage").getAttribute("disabled"), canShare ? "false" : "true", "Social:SharePage visible?");
 
   // broadcasters.
   isbool(!doc.getElementById("socialActiveBroadcaster").hidden, active, "socialActiveBroadcaster hidden?");
 }
 
 // blocklist testing
 function updateBlocklist(aCallback) {
   var blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"]
rename from browser/base/content/test/social/social_mark_image.png
rename to browser/base/content/test/social/social_share_image.png
--- a/browser/base/content/test/social/social_worker.js
+++ b/browser/base/content/test/social/social_worker.js
@@ -95,44 +95,52 @@ onconnect = function(e) {
           profile = {
             portrait: "https://example.com/portrait.jpg",
             userName: "trickster",
             displayName: "Kuma Lisa",
             profileURL: "http://en.wikipedia.org/wiki/Kuma_Lisa"
           };
         }
         port.postMessage({topic: "social.user-profile", data: profile});
-        port.postMessage({
-          topic: "social.page-mark-config",
-          data: {
-            images: {
-              // this one is relative to test we handle relative ones.
-              marked: "/browser/browser/base/content/test/social/social_mark_image.png",
-              // absolute to check we handle them too.
-              unmarked: "https://example.com/browser/browser/base/content/test/social/social_mark_image.png"
-            },
-            messages: {
-              unmarkedTooltip: "Mark this page",
-              markedTooltip: "Unmark this page",
-              unmarkedLabel: "Mark",
-              markedLabel: "Unmark",
-            }
-          }
-        });
         break;
       case "test-ambient-notification":
         let icon = {
           name: "testIcon",
           iconURL: "chrome://browser/skin/Info.png",
           contentPanel: "https://example.com/browser/browser/base/content/test/social/social_panel.html",
           counter: 1
         };
         apiPort.postMessage({topic: "social.ambient-notification", data: icon});
         break;
       case "test-isVisible":
         sidebarPort.postMessage({topic: "test-isVisible"});
         break;
       case "test-isVisible-response":
         testPort.postMessage({topic: "got-isVisible-response", result: event.data.result});
         break;
+      case "social.user-recommend-prompt":
+        port.postMessage({
+          topic: "social.user-recommend-prompt-response",
+          data: {
+            images: {
+              // this one is relative to test we handle relative ones.
+              share: "browser/browser/base/content/test/social/social_share_image.png",
+              // absolute to check we handle them too.
+              unshare: "https://example.com/browser/browser/base/content/test/social/social_share_image.png"
+            },
+            messages: {
+              shareTooltip: "Share this page",
+              unshareTooltip: "Unshare this page",
+              sharedLabel: "This page has been shared",
+              unsharedLabel: "This page is no longer shared",
+              unshareLabel: "You have already shared this page",
+              portraitLabel: "Your pretty face",
+              unshareConfirmLabel: "Unshare it!",
+              unshareConfirmAccessKey: "U",
+              unshareCancelLabel: "Got it!",
+              unshareCancelAccessKey: "G"
+            }
+          }
+        });
+        break;
     }
   }
 }
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -110,17 +110,17 @@ These should match what Safari and other
 
 <!ENTITY closeWindow.label "Close Window">
 <!ENTITY closeWindow.accesskey "d">
 
 <!ENTITY bookmarksMenu.label "Bookmarks">
 <!ENTITY bookmarksMenu.accesskey "B">
 <!ENTITY bookmarkThisPageCmd.label "Bookmark This Page">
 <!ENTITY bookmarkThisPageCmd.commandkey "d">
-<!ENTITY markPageCmd.commandkey "l">
+<!ENTITY sharePageCmd.commandkey "l">
 <!ENTITY subscribeToPageMenupopup.label "Subscribe to This Page">
 <!ENTITY subscribeToPageMenuitem.label "Subscribe to This Page…">
 <!ENTITY addCurPagesCmd.label "Bookmark All Tabs…">
 <!ENTITY showAllBookmarks2.label "Show All Bookmarks">
 <!ENTITY unsortedBookmarksCmd.label "Unsorted Bookmarks">
 <!ENTITY bookmarksToolbarChevron.tooltip "Show more bookmarks">
 
 <!ENTITY backCmd.label                "Back">
@@ -639,20 +639,16 @@ just addresses the organization to follo
 <!ENTITY social.learnMore.label "Learn more…">
 <!ENTITY social.learnMore.accesskey "l">
 <!ENTITY social.closeNotificationItem.label "Not Now">
 
 <!ENTITY social.chatBar.commandkey "c">
 <!ENTITY social.chatBar.label "Focus chats">
 <!ENTITY social.chatBar.accesskey "c">
 
-<!-- labels are set dynamically, see browser.properties -->
-<!ENTITY social.markpage.accesskey "m">
-<!ENTITY social.marklink.accesskey "M">
-
 <!ENTITY getUserMedia.selectCamera.label "Camera to share:">
 <!ENTITY getUserMedia.selectCamera.accesskey "C">
 <!ENTITY getUserMedia.selectMicrophone.label "Microphone to share:">
 <!ENTITY getUserMedia.selectMicrophone.accesskey "M">
 
 <!ENTITY webrtcIndicatorButton.label "Camera / Microphone Access">
 <!ENTITY webrtcIndicatorButton.tooltip "Display sites you are currently sharing your camera or microphone with">
 
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -381,23 +381,16 @@ service.install.learnmore=Learn More…
 
 # LOCALIZATION NOTE (social.turnOff.label): %S is the name of the social provider
 social.turnOff.label=Turn off %S
 social.turnOff.accesskey=T
 # LOCALIZATION NOTE (social.turnOn.label): %S is the name of the social provider
 social.turnOn.label=Turn on %S
 social.turnOn.accesskey=T
 
-# LOCALIZATION NOTE (social.markpage.label): %S is the name of the social provider
-social.markpage.label=Send Page to %S
-social.unmarkpage.label=Remove Page from %S
-# LOCALIZATION NOTE (social.marklink.label): %S is the name of the social provider
-social.marklink.label=Send Link to %S
-social.unmarklink.label=Remove Link from %S
-
 # LOCALIZATION NOTE (social.error.message): %1$S is brandShortName (e.g. Firefox), %2$S is the name of the social provider
 social.error.message=%1$S is unable to connect with %2$S right now.
 social.error.tryAgain.label=Try Again
 social.error.tryAgain.accesskey=T
 social.error.closeSidebar.label=Close This Sidebar
 social.error.closeSidebar.accesskey=C
 
 # LOCALIZATION NOTE: %1$S is the label for the toolbar button, %2$S is the associated badge numbering that the social provider may provide.
--- a/browser/modules/Social.jsm
+++ b/browser/modules/Social.jsm
@@ -10,20 +10,16 @@ const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SocialService",
   "resource://gre/modules/SocialService.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
-  "resource://gre/modules/PlacesUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Promise",
-  "resource://gre/modules/commonjs/sdk/core/promise.js");
 
 // Add a pref observer for the enabled state
 function prefObserver(subject, topic, data) {
   let enable = Services.prefs.getBoolPref("social.enabled");
   if (enable && !Social.provider) {
     Social.provider = Social.defaultProvider;
   } else if (!enable && Social.provider) {
     Social.provider = null;
@@ -31,56 +27,16 @@ function prefObserver(subject, topic, da
 }
 
 Services.prefs.addObserver("social.enabled", prefObserver, false);
 Services.obs.addObserver(function xpcomShutdown() {
   Services.obs.removeObserver(xpcomShutdown, "xpcom-shutdown");
   Services.prefs.removeObserver("social.enabled", prefObserver);
 }, "xpcom-shutdown", false);
 
-function promiseSetAnnotation(aURI, providerList) {
-  let deferred = Promise.defer();
-
-  // Delaying to catch issues with asynchronous behavior while waiting
-  // to implement asynchronous annotations in bug 699844.
-  Services.tm.mainThread.dispatch(function() {
-    try {
-      if (providerList && providerList.length > 0) {
-        PlacesUtils.annotations.setPageAnnotation(
-          aURI, "social/mark", JSON.stringify(providerList), 0,
-          PlacesUtils.annotations.EXPIRE_WITH_HISTORY);
-      } else {
-        PlacesUtils.annotations.removePageAnnotation(aURI, "social/mark");
-      }
-    } catch(e) {
-      Cu.reportError("SocialAnnotation failed: " + e);
-    }
-    deferred.resolve();
-  }, Ci.nsIThread.DISPATCH_NORMAL);
-
-  return deferred.promise;
-}
-
-function promiseGetAnnotation(aURI) {
-  let deferred = Promise.defer();
-
-  // Delaying to catch issues with asynchronous behavior while waiting
-  // to implement asynchronous annotations in bug 699844.
-  Services.tm.mainThread.dispatch(function() {
-    let val = null;
-    try {
-      val = PlacesUtils.annotations.getPageAnnotation(aURI, "social/mark");
-    } catch (ex) { }
-
-    deferred.resolve(val);
-  }, Ci.nsIThread.DISPATCH_NORMAL);
-
-  return deferred.promise;
-}
-
 this.Social = {
   initialized: false,
   lastEventReceived: 0,
   providers: null,
   _disabledForSafeMode: false,
 
   get _currentProviderPref() {
     try {
@@ -252,116 +208,72 @@ this.Social = {
     let oldProvider = this._getProviderFromOrigin(oldOrigin);
     if (!oldProvider && this.providers.length)
       oldProvider = this.providers[0];
     this.provider = oldProvider;
     if (provider)
       SocialService.removeProvider(origin);
   },
 
-  // Page Marking functionality
-  _getMarkablePageUrl: function Social_getMarkablePageUrl(aURI) {
+  // Sharing functionality
+  _getShareablePageUrl: function Social_getShareablePageUrl(aURI) {
     let uri = aURI.clone();
     try {
       // Setting userPass on about:config throws.
       uri.userPass = "";
     } catch (e) {}
     return uri.spec;
   },
 
-  isURIMarked: function(aURI, aCallback) {
-    promiseGetAnnotation(aURI).then(function(val) {
-      if (val) {
-        let providerList = JSON.parse(val);
-        val = providerList.indexOf(this.provider.origin) >= 0;
-      }
-      aCallback(!!val);
-    }.bind(this));
+  isPageShared: function Social_isPageShared(aURI) {
+    let url = this._getShareablePageUrl(aURI);
+    return this._sharedUrls.hasOwnProperty(url);
   },
 
-  markURI: function(aURI, aCallback) {
+  sharePage: function Social_sharePage(aURI) {
     // this should not be called if this.provider or the port is null
     if (!this.provider) {
-      Cu.reportError("Can't mark a page when no provider is current");
+      Cu.reportError("Can't share a page when no provider is current");
       return;
     }
     let port = this.provider.getWorkerPort();
     if (!port) {
-      Cu.reportError("Can't mark page as no provider port is available");
+      Cu.reportError("Can't share page as no provider port is available");
       return;
     }
-
-    // update or set our annotation
-    promiseGetAnnotation(aURI).then(function(val) {
+    let url = this._getShareablePageUrl(aURI);
+    this._sharedUrls[url] = true;
+    port.postMessage({
+      topic: "social.user-recommend",
+      data: { url: url }
+    });
+    port.close();
+  },
 
-      let providerList = val ? JSON.parse(val) : [];
-      let marked = providerList.indexOf(this.provider.origin) >= 0;
-      if (marked)
-        return;
-      providerList.push(this.provider.origin);
-      // we allow marking links in a page that may not have been visited yet.
-      // make sure there is a history entry for the uri, then annotate it.
-      let place = {
-        uri: aURI,
-        visits: [{
-          visitDate: Date.now() + 1000,
-          transitionType: Ci.nsINavHistoryService.TRANSITION_LINK
-        }]
-      };
-      PlacesUtils.asyncHistory.updatePlaces(place, {
-        handleError: function () Cu.reportError("couldn't update history for socialmark annotation"),
-        handleResult: function () {},
-        handleCompletion: function () {
-          promiseSetAnnotation(aURI, providerList).then();
-          // post to the provider
-          let url = this._getMarkablePageUrl(aURI);
-          port.postMessage({
-            topic: "social.page-mark",
-            data: { url: url, 'marked': true }
-          });
-          port.close();
-          if (aCallback)
-            schedule(function() { aCallback(true); } );
-        }.bind(this)
-      });
-    }.bind(this));
-  },
-  
-  unmarkURI: function(aURI, aCallback) {
+  unsharePage: function Social_unsharePage(aURI) {
     // this should not be called if this.provider or the port is null
     if (!this.provider) {
-      Cu.reportError("Can't mark a page when no provider is current");
+      Cu.reportError("Can't unshare a page when no provider is current");
       return;
     }
     let port = this.provider.getWorkerPort();
     if (!port) {
-      Cu.reportError("Can't mark page as no provider port is available");
+      Cu.reportError("Can't unshare page as no provider port is available");
       return;
     }
+    let url = this._getShareablePageUrl(aURI);
+    delete this._sharedUrls[url];
+    port.postMessage({
+      topic: "social.user-unrecommend",
+      data: { url: url }
+    });
+    port.close();
+  },
 
-    // set our annotation
-    promiseGetAnnotation(aURI).then(function(val) {
-      let providerList = val ? JSON.parse(val) : [];
-      let marked = providerList.indexOf(this.provider.origin) >= 0;
-      if (marked) {
-        // remove the annotation
-        providerList.splice(providerList.indexOf(this.provider.origin), 1);
-        promiseSetAnnotation(aURI, providerList).then();
-      }
-      // post to the provider regardless
-      let url = this._getMarkablePageUrl(aURI);
-      port.postMessage({
-        topic: "social.page-mark",
-        data: { url: url, 'marked': false }
-      });
-      port.close();
-      if (aCallback)
-        schedule(function() { aCallback(false); } );
-    }.bind(this));
-  },
+  _sharedUrls: {},
 
   setErrorListener: function(iframe, errorHandler) {
     if (iframe.socialErrorListener)
       return iframe.socialErrorListener;
     return new SocialErrorListener(iframe, errorHandler);
   }
 };
 
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1430,20 +1430,60 @@ richlistitem[type~="action"][actiontype=
 
 /* Popup blocker button */
 #page-report-button {
   list-style-image: url("chrome://browser/skin/Info.png");
 }
 
 /* social recommending panel */
 
-#social-mark-button {
+#share-button {
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
+#socialUserPortrait {
+  width: 48px;
+  height: 48px;
+  list-style-image:url("chrome://global/skin/icons/information-32.png");
+}
+
+#socialUserDisplayName,
+#socialUserPortrait {
+  cursor: pointer;
+}
+
+#socialUserDisplayName {
+  -moz-appearance: none;
+  border: none;
+  background-color: transparent;
+  margin-top: 0;
+  padding-top: 0;
+  font-size: 130%;
+  font-weight: bold;
+}
+
+#socialUserDisplayName > .button-box {
+  -moz-padding-start: 0;
+  padding-top: 0;
+  border-top-width: 0;
+}
+
+#socialUserDisplayName:hover {
+  color: -moz-nativehyperlinktext;
+  text-decoration: underline;
+}
+
+#unsharePopupText {
+  height: 48px;
+}
+
+#unsharePopupBottomButtons {
+  margin-top: 1em;
+}
+
 /* bookmarks menu-button */
 
 #bookmarks-menu-button {
   list-style-image: url("chrome://browser/skin/Toolbar.png");
   -moz-image-region: rect(0px 216px 24px 192px);
 }
 
 #bookmarks-menu-button[starred] {
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -1652,20 +1652,60 @@ window[tabsontop="false"] richlistitem[t
   #page-report-button:hover:active,
   #page-report-button[open="true"] {
     -moz-image-region: rect(0, 64px, 32px, 32px);
   }
 }
 
 /* social recommending panel */
 
-#social-mark-button {
+#share-button {
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
+#socialUserPortrait {
+  width: 48px;
+  height: 48px;
+  list-style-image:url("chrome://global/skin/icons/information-32.png");
+}
+
+#socialUserDisplayName,
+#socialUserPortrait {
+  cursor: pointer;
+}
+
+#socialUserDisplayName {
+  -moz-appearance: none;
+  border: none;
+  background-color: transparent;
+  margin: 1px;
+  padding: 0;
+  font-size: 130%;
+  font-weight: bold;
+}
+
+#socialUserDisplayName > .button-box {
+  -moz-padding-start: 0;
+  padding-top: 0;
+  border-top-width: 0;
+}
+
+#socialUserDisplayName:hover {
+  color: -moz-nativehyperlinktext;
+  text-decoration: underline;
+}
+
+#unsharePopupText {
+  height: 48px;
+}
+
+#unsharePopupBottomButtons {
+  margin-top: 1em;
+}
+
 /* bookmarks menu-button */
 
 #bookmarks-menu-button {
   -moz-image-region: rect(0px 500px 20px 480px);
 }
 
 #bookmarks-menu-button[starred] {
   -moz-image-region: rect(20px 500px 40px 480px);
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1710,20 +1710,60 @@ richlistitem[type~="action"][actiontype=
 
 #page-report-button:hover:active,
 #page-report-button[open="true"] {
   -moz-image-region: rect(0, 48px, 16px, 32px);
 }
 
 /* social recommending panel */
 
-#social-mark-button {
+#share-button {
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
+#socialUserPortrait {
+  width: 48px;
+  height: 48px;
+  list-style-image:url("chrome://global/skin/icons/information-32.png");
+}
+
+#socialUserDisplayName,
+#socialUserPortrait {
+  cursor: pointer;
+}
+
+#socialUserDisplayName {
+  -moz-appearance: none;
+  border: none;
+  background-color: transparent;
+  margin-top: 0;
+  padding-top: 0;
+  font-size: 130%;
+  font-weight: bold;
+}
+
+#socialUserDisplayName > .button-box {
+  -moz-padding-start: 0;
+  padding-top: 0;
+  border-top-width: 0;
+}
+
+#socialUserDisplayName:hover {
+  color: -moz-nativehyperlinktext;
+  text-decoration: underline;
+}
+
+#unsharePopupText {
+  height: 48px;
+}
+
+#unsharePopupBottomButtons {
+  margin-top: 1em;
+}
+
 /* bookmarks menu-button */
 
 #bookmarks-menu-button {
   -moz-image-region: rect(0px 378px 18px 360px);
 }
 
 #bookmarks-menu-button[starred] {
   -moz-image-region: rect(18px 378px 36px 360px);
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -69,17 +69,17 @@ class RemoteAutomation(Automation):
     def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath):
         """ Wait for tests to finish (as evidenced by the process exiting),
             or for maxTime elapse, in which case kill the process regardless.
         """
         # maxTime is used to override the default timeout, we should honor that
         status = proc.wait(timeout = maxTime)
         self.lastTestSeen = proc.getLastTestSeen
 
-        if (status == 1 and self._devicemanager.processExist(proc.procName)):
+        if (status == 1 and self._devicemanager.getTopActivity() == proc.procName):
             # Then we timed out, make sure Fennec is dead
             if maxTime:
                 print "TEST-UNEXPECTED-FAIL | %s | application ran for longer than " \
                       "allowed maximum time of %s seconds" % (self.lastTestSeen, maxTime)
             else:
                 print "TEST-UNEXPECTED-FAIL | %s | application ran for longer than " \
                       "allowed maximum time" % (self.lastTestSeen)
             proc.kill()
@@ -264,17 +264,17 @@ class RemoteAutomation(Automation):
 
         def wait(self, timeout = None):
             timer = 0
             interval = 5
 
             if timeout == None:
                 timeout = self.timeout
 
-            while (self.dm.processExist(self.procName)):
+            while (self.dm.getTopActivity() == self.procName):
                 t = self.stdout
                 if t != '': print t
                 time.sleep(interval)
                 timer += interval
                 if (timer > timeout):
                     break
 
             # Flush anything added to stdout during the sleep
--- a/config/moz.build
+++ b/config/moz.build
@@ -1,15 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 CONFIGURE_SUBST_FILES += [
     'autoconf.mk',
+    'doxygen.cfg',
     'emptyvars.mk',
-    'doxygen.cfg',
     'makefiles/test/Makefile',
     'tests/makefiles/autodeps/Makefile',
     'tests/src-simple/Makefile',
 ]
 
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -529,20 +529,19 @@ public:
 
   /**
    * Create a new presentation shell that will use aContext for its
    * presentation context (presentation contexts <b>must not</b> be
    * shared among multiple presentation shells). The caller of this
    * method is responsible for calling BeginObservingDocument() on the
    * presshell if the presshell should observe document mutations.
    */
-  virtual nsresult CreateShell(nsPresContext* aContext,
-                               nsViewManager* aViewManager,
-                               nsStyleSet* aStyleSet,
-                               nsIPresShell** aInstancePtrResult) = 0;
+  virtual already_AddRefed<nsIPresShell> CreateShell(nsPresContext* aContext,
+                                                     nsViewManager* aViewManager,
+                                                     nsStyleSet* aStyleSet) = 0;
   virtual void DeleteShell() = 0;
 
   nsIPresShell* GetShell() const
   {
     return GetBFCacheEntry() ? nullptr : mPresShell;
   }
 
   void DisallowBFCaching()
--- a/content/base/src/moz.build
+++ b/content/base/src/moz.build
@@ -2,51 +2,51 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'content'
 
 EXPORTS += [
+    'mozAutoDocUpdate.h',
     'nsAtomListUtils.h',
+    'nsAttrAndChildArray.h',
     'nsAttrName.h',
+    'nsAttrValue.h',
+    'nsAttrValueInlines.h',
     'nsContentList.h',
     'nsContentListDeclarations.h',
     'nsContentSink.h',
+    'nsCrossSiteListenerProxy.h',
+    'nsDOMAttributeMap.h',
+    'nsFrameMessageManager.h',
     'nsGkAtomList.h',
     'nsGkAtoms.h',
+    'nsMappedAttributeElement.h',
     'nsNodeInfoManager.h',
     'nsNodeUtils.h',
     'nsPropertyTable.h',
     'nsRange.h',
+    'nsSandboxFlags.h',
     'nsScriptLoader.h',
     'nsStubDocumentObserver.h',
     'nsStubMutationObserver.h',
+    'nsStyledElement.h',
     'nsTextFragment.h',
-    'mozAutoDocUpdate.h',
-    'nsFrameMessageManager.h',
-    'nsAttrAndChildArray.h',
-    'nsAttrValue.h',
-    'nsAttrValueInlines.h',
-    'nsCrossSiteListenerProxy.h',
-    'nsDOMAttributeMap.h',
-    'nsMappedAttributeElement.h',
-    'nsStyledElement.h',
-    'nsSandboxFlags.h',
 ]
 
 if CONFIG['MOZ_WEBRTC']:
     EXPORTS += ['nsDOMDataChannel.h']
 
 EXPORTS.mozilla.dom += [
     'Attr.h',
     'Comment.h',
+    'DOMImplementation.h',
     'DocumentFragment.h',
     'DocumentType.h',
-    'DOMImplementation.h',
     'EventSource.h',
     'Link.h',
     'NodeIterator.h',
     'Text.h',
     'TreeWalker.h',
 ]
 
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -3332,56 +3332,49 @@ nsDocument::TryChannelCharset(nsIChannel
         return;
       } else if (aExecutor && !charsetVal.IsEmpty()) {
         aExecutor->ComplainAboutBogusProtocolCharset(this);
       }
     }
   }
 }
 
-nsresult
+already_AddRefed<nsIPresShell>
 nsDocument::CreateShell(nsPresContext* aContext, nsViewManager* aViewManager,
-                        nsStyleSet* aStyleSet,
-                        nsIPresShell** aInstancePtrResult)
+                        nsStyleSet* aStyleSet)
 {
   // Don't add anything here.  Add it to |doCreateShell| instead.
   // This exists so that subclasses can pass other values for the 4th
   // parameter some of the time.
   return doCreateShell(aContext, aViewManager, aStyleSet,
-                       eCompatibility_FullStandards, aInstancePtrResult);
-}
-
-nsresult
+                       eCompatibility_FullStandards);
+}
+
+already_AddRefed<nsIPresShell>
 nsDocument::doCreateShell(nsPresContext* aContext,
                           nsViewManager* aViewManager, nsStyleSet* aStyleSet,
-                          nsCompatibility aCompatMode,
-                          nsIPresShell** aInstancePtrResult)
-{
-  *aInstancePtrResult = nullptr;
-
+                          nsCompatibility aCompatMode)
+{
   NS_ASSERTION(!mPresShell, "We have a presshell already!");
 
-  NS_ENSURE_FALSE(GetBFCacheEntry(), NS_ERROR_FAILURE);
+  NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
 
   FillStyleSet(aStyleSet);
 
   nsRefPtr<PresShell> shell = new PresShell;
-  nsresult rv = shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode);
-  NS_ENSURE_SUCCESS(rv, rv);
+  shell->Init(this, aContext, aViewManager, aStyleSet, aCompatMode);
 
   // Note: we don't hold a ref to the shell (it holds a ref to us)
   mPresShell = shell;
 
   mExternalResourceMap.ShowViewers();
 
   MaybeRescheduleAnimationFrameNotifications();
 
-  shell.forget(aInstancePtrResult);
-
-  return NS_OK;
+  return shell.forget();
 }
 
 void
 nsDocument::MaybeRescheduleAnimationFrameNotifications()
 {
   if (!mPresShell || !IsEventHandlingEnabled()) {
     // bail out for now, until one of those conditions changes
     return;
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -585,23 +585,22 @@ public:
    * HTML META tags).
    */
   virtual void GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const;
   virtual void SetHeaderData(nsIAtom* aheaderField,
                              const nsAString& aData);
 
   /**
    * Create a new presentation shell that will use aContext for
-   * its presentation context (presentation context's <b>must not</b> be
-   * shared among multiple presentation shell's).
+   * its presentation context (presentation contexts <b>must not</b> be
+   * shared among multiple presentation shells).
    */
-  virtual nsresult CreateShell(nsPresContext* aContext,
-                               nsViewManager* aViewManager,
-                               nsStyleSet* aStyleSet,
-                               nsIPresShell** aInstancePtrResult);
+  virtual already_AddRefed<nsIPresShell> CreateShell(nsPresContext* aContext,
+                                                     nsViewManager* aViewManager,
+                                                     nsStyleSet* aStyleSet) MOZ_OVERRIDE;
   virtual void DeleteShell();
 
   virtual nsresult GetAllowPlugins(bool* aAllowPlugins);
 
   virtual already_AddRefed<mozilla::dom::UndoManager> GetUndoManager();
 
   virtual nsresult SetSubDocumentFor(Element* aContent,
                                      nsIDocument* aSubDoc);
@@ -1136,20 +1135,20 @@ protected:
 public:
   // Get our title
   virtual void GetTitle(nsString& aTitle);
   // Set our title
   virtual void SetTitle(const nsAString& aTitle, mozilla::ErrorResult& rv);
 
   static void XPCOMShutdown();
 protected:
-  nsresult doCreateShell(nsPresContext* aContext,
-                         nsViewManager* aViewManager, nsStyleSet* aStyleSet,
-                         nsCompatibility aCompatMode,
-                         nsIPresShell** aInstancePtrResult);
+  already_AddRefed<nsIPresShell> doCreateShell(nsPresContext* aContext,
+                                               nsViewManager* aViewManager,
+                                               nsStyleSet* aStyleSet,
+                                               nsCompatibility aCompatMode);
 
   void RemoveDocStyleSheetsFromStyleSets();
   void RemoveStyleSheetsFromStyleSets(nsCOMArray<nsIStyleSheet>& aSheets, 
                                       nsStyleSet::sheetType aType);
   nsresult ResetStylesheetsToURI(nsIURI* aURI);
   void FillStyleSet(nsStyleSet* aStyleSet);
 
   // Return whether all the presshells for this document are safe to flush
--- a/content/html/content/src/HTMLInputElement.cpp
+++ b/content/html/content/src/HTMLInputElement.cpp
@@ -728,61 +728,49 @@ HTMLInputElement::Clone(nsINodeInfo* aNo
 
   nsCOMPtr<nsINodeInfo> ni = aNodeInfo;
   nsRefPtr<HTMLInputElement> it =
     new HTMLInputElement(ni.forget(), NOT_FROM_PARSER);
 
   nsresult rv = const_cast<HTMLInputElement*>(this)->CopyInnerTo(it);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  switch (mType) {
-    case NS_FORM_INPUT_EMAIL:
-    case NS_FORM_INPUT_SEARCH:
-    case NS_FORM_INPUT_TEXT:
-    case NS_FORM_INPUT_PASSWORD:
-    case NS_FORM_INPUT_TEL:
-    case NS_FORM_INPUT_URL:
-    case NS_FORM_INPUT_NUMBER:
-    case NS_FORM_INPUT_DATE:
-    case NS_FORM_INPUT_TIME:
-    case NS_FORM_INPUT_RANGE:
+  switch (GetValueMode()) {
+    case VALUE_MODE_VALUE:
       if (mValueChanged) {
         // We don't have our default value anymore.  Set our value on
         // the clone.
         nsAutoString value;
         GetValueInternal(value);
         // SetValueInternal handles setting the VALUE_CHANGED bit for us
         it->SetValueInternal(value, false, true);
       }
       break;
-    case NS_FORM_INPUT_FILE:
+    case VALUE_MODE_FILENAME:
       if (it->OwnerDoc()->IsStaticDocument()) {
         // We're going to be used in print preview.  Since the doc is static
         // we can just grab the pretty string and use it as wallpaper
         GetDisplayFileName(it->mStaticDocFileList);
       } else {
         it->mFiles.Clear();
         it->mFiles.AppendObjects(mFiles);
       }
       break;
-    case NS_FORM_INPUT_RADIO:
-    case NS_FORM_INPUT_CHECKBOX:
+    case VALUE_MODE_DEFAULT_ON:
       if (mCheckedChanged) {
         // We no longer have our original checked state.  Set our
         // checked state on the clone.
         it->DoSetChecked(mChecked, false, true);
       }
       break;
-    case NS_FORM_INPUT_IMAGE:
-      if (it->OwnerDoc()->IsStaticDocument()) {
+    case VALUE_MODE_DEFAULT:
+      if (mType == NS_FORM_INPUT_IMAGE && it->OwnerDoc()->IsStaticDocument()) {
         CreateStaticImageClone(it);
       }
       break;
-    default:
-      break;
   }
 
   it.forget(aResult);
   return NS_OK;
 }
 
 nsresult
 HTMLInputElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
@@ -4410,63 +4398,50 @@ HTMLInputElement::SubmitNamesValues(nsFo
   return aFormSubmission->AddNameValuePair(name, value);
 }
 
 
 NS_IMETHODIMP
 HTMLInputElement::SaveState()
 {
   nsRefPtr<HTMLInputElementState> inputState;
-  switch (mType) {
-    case NS_FORM_INPUT_CHECKBOX:
-    case NS_FORM_INPUT_RADIO:
-      {
-        if (mCheckedChanged) {
-          inputState = new HTMLInputElementState();
-          inputState->SetChecked(mChecked);
-        }
+  switch (GetValueMode()) {
+    case VALUE_MODE_DEFAULT_ON:
+      if (mCheckedChanged) {
+        inputState = new HTMLInputElementState();
+        inputState->SetChecked(mChecked);
+      }
+      break;
+    case VALUE_MODE_FILENAME:
+      if (mFiles.Count()) {
+        inputState = new HTMLInputElementState();
+        inputState->SetFiles(mFiles);
+      }
+      break;
+    case VALUE_MODE_VALUE:
+    case VALUE_MODE_DEFAULT:
+      // VALUE_MODE_DEFAULT shouldn't have their value saved except 'hidden',
+      // mType shouldn't be NS_FORM_INPUT_PASSWORD and value should have changed.
+      if ((GetValueMode() == VALUE_MODE_DEFAULT &&
+           mType != NS_FORM_INPUT_HIDDEN) ||
+          mType == NS_FORM_INPUT_PASSWORD || !mValueChanged) {
         break;
       }
 
-    // Never save passwords in session history
-    case NS_FORM_INPUT_PASSWORD:
+      inputState = new HTMLInputElementState();
+      nsAutoString value;
+      GetValue(value);
+      DebugOnly<nsresult> rv =
+        nsLinebreakConverter::ConvertStringLineBreaks(
+             value,
+             nsLinebreakConverter::eLinebreakPlatform,
+             nsLinebreakConverter::eLinebreakContent);
+      NS_ASSERTION(NS_SUCCEEDED(rv), "Converting linebreaks failed!");
+      inputState->SetValue(value);
       break;
-    case NS_FORM_INPUT_EMAIL:
-    case NS_FORM_INPUT_SEARCH:
-    case NS_FORM_INPUT_TEXT:
-    case NS_FORM_INPUT_TEL:
-    case NS_FORM_INPUT_URL:
-    case NS_FORM_INPUT_HIDDEN:
-    case NS_FORM_INPUT_NUMBER:
-    case NS_FORM_INPUT_DATE:
-    case NS_FORM_INPUT_TIME:
-    case NS_FORM_INPUT_RANGE:
-      {
-        if (mValueChanged) {
-          inputState = new HTMLInputElementState();
-          nsAutoString value;
-          GetValue(value);
-          DebugOnly<nsresult> rv =
-            nsLinebreakConverter::ConvertStringLineBreaks(
-                 value,
-                 nsLinebreakConverter::eLinebreakPlatform,
-                 nsLinebreakConverter::eLinebreakContent);
-          NS_ASSERTION(NS_SUCCEEDED(rv), "Converting linebreaks failed!");
-          inputState->SetValue(value);
-       }
-      break;
-    }
-    case NS_FORM_INPUT_FILE:
-      {
-        if (mFiles.Count()) {
-          inputState = new HTMLInputElementState();
-          inputState->SetFiles(mFiles);
-        }
-        break;
-      }
   }
 
   nsresult rv = NS_OK;
   nsPresState* state = nullptr;
   if (inputState) {
     rv = GetPrimaryPresState(this, &state);
     if (state) {
       state->SetStateProperty(inputState);
@@ -4600,47 +4575,39 @@ bool
 HTMLInputElement::RestoreState(nsPresState* aState)
 {
   bool restoredCheckedState = false;
 
   nsCOMPtr<HTMLInputElementState> inputState
     (do_QueryInterface(aState->GetStateProperty()));
 
   if (inputState) {
-    switch (mType) {
-      case NS_FORM_INPUT_CHECKBOX:
-      case NS_FORM_INPUT_RADIO:
-        {
-          if (inputState->IsCheckedSet()) {
-            restoredCheckedState = true;
-            DoSetChecked(inputState->GetChecked(), true, true);
-          }
-          break;
+    switch (GetValueMode()) {
+      case VALUE_MODE_DEFAULT_ON:
+        if (inputState->IsCheckedSet()) {
+          restoredCheckedState = true;
+          DoSetChecked(inputState->GetChecked(), true, true);
         }
-
-      case NS_FORM_INPUT_EMAIL:
-      case NS_FORM_INPUT_SEARCH:
-      case NS_FORM_INPUT_TEXT:
-      case NS_FORM_INPUT_TEL:
-      case NS_FORM_INPUT_URL:
-      case NS_FORM_INPUT_HIDDEN:
-      case NS_FORM_INPUT_NUMBER:
-      case NS_FORM_INPUT_DATE:
-      case NS_FORM_INPUT_TIME:
-      case NS_FORM_INPUT_RANGE:
-        {
-          SetValueInternal(inputState->GetValue(), false, true);
-          break;
-        }
-      case NS_FORM_INPUT_FILE:
+        break;
+      case VALUE_MODE_FILENAME:
         {
           const nsCOMArray<nsIDOMFile>& files = inputState->GetFiles();
           SetFiles(files, true);
+        }
+        break;
+      case VALUE_MODE_VALUE:
+      case VALUE_MODE_DEFAULT:
+        if (GetValueMode() == VALUE_MODE_DEFAULT &&
+            mType != NS_FORM_INPUT_HIDDEN) {
           break;
         }
+
+        SetValueInternal(inputState->GetValue(), false, true);
+        break;
+        break;
     }
   }
 
   if (aState->IsDisabledSet()) {
     SetDisabled(aState->GetDisabled());
   }
 
   return restoredCheckedState;
--- a/content/html/content/src/moz.build
+++ b/content/html/content/src/moz.build
@@ -3,53 +3,53 @@
 # 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/.
 
 MODULE = 'content'
 
 EXPORTS += [
     'HTMLPropertiesCollection.h',
+    'nsClientRect.h',
     'nsGenericHTMLElement.h',
-    'nsClientRect.h',
     'nsHTMLDNSPrefetch.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'HTMLAnchorElement.h',
     'HTMLAreaElement.h',
+    'HTMLBRElement.h',
     'HTMLBodyElement.h',
-    'HTMLBRElement.h',
     'HTMLButtonElement.h',
     'HTMLDataElement.h',
     'HTMLDataListElement.h',
     'HTMLDivElement.h',
     'HTMLFieldSetElement.h',
     'HTMLFontElement.h',
     'HTMLFrameElement.h',
     'HTMLFrameSetElement.h',
+    'HTMLHRElement.h',
     'HTMLHeadingElement.h',
-    'HTMLHRElement.h',
     'HTMLIFrameElement.h',
     'HTMLImageElement.h',
     'HTMLInputElement.h',
+    'HTMLLIElement.h',
     'HTMLLabelElement.h',
     'HTMLLegendElement.h',
-    'HTMLLIElement.h',
     'HTMLLinkElement.h',
     'HTMLMapElement.h',
     'HTMLMenuElement.h',
     'HTMLMenuItemElement.h',
     'HTMLMetaElement.h',
     'HTMLMeterElement.h',
     'HTMLModElement.h',
     'HTMLObjectElement.h',
+    'HTMLOptGroupElement.h',
     'HTMLOptionElement.h',
     'HTMLOptionsCollection.h',
-    'HTMLOptGroupElement.h',
     'HTMLOutputElement.h',
     'HTMLParagraphElement.h',
     'HTMLPreElement.h',
     'HTMLProgressElement.h',
     'HTMLScriptElement.h',
     'HTMLSelectElement.h',
     'HTMLSharedElement.h',
     'HTMLSharedListElement.h',
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -299,24 +299,22 @@ nsHTMLDocument::ResetToURI(nsIURI *aURI,
   mWyciwygChannel = nullptr;
 
   // Make the content type default to "text/html", we are a HTML
   // document, after all. Once we start getting data, this may be
   // changed.
   SetContentTypeInternal(nsDependentCString("text/html"));
 }
 
-nsresult
+already_AddRefed<nsIPresShell>
 nsHTMLDocument::CreateShell(nsPresContext* aContext,
                             nsViewManager* aViewManager,
-                            nsStyleSet* aStyleSet,
-                            nsIPresShell** aInstancePtrResult)
+                            nsStyleSet* aStyleSet)
 {
-  return doCreateShell(aContext, aViewManager, aStyleSet, mCompatMode,
-                       aInstancePtrResult);
+  return doCreateShell(aContext, aViewManager, aStyleSet, mCompatMode);
 }
 
 void
 nsHTMLDocument::TryHintCharset(nsIMarkupDocumentViewer* aMarkupDV,
                                int32_t& aCharsetSource, nsACString& aCharset)
 {
   if (aMarkupDV) {
     int32_t requestCharsetSource;
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -49,20 +49,19 @@ public:
 
   NS_IMETHOD_(nsrefcnt) AddRef(void);
   NS_IMETHOD_(nsrefcnt) Release(void);
 
   virtual void Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup);
   virtual void ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
                           nsIPrincipal* aPrincipal);
 
-  virtual nsresult CreateShell(nsPresContext* aContext,
-                               nsViewManager* aViewManager,
-                               nsStyleSet* aStyleSet,
-                               nsIPresShell** aInstancePtrResult);
+  virtual already_AddRefed<nsIPresShell> CreateShell(nsPresContext* aContext,
+                                                     nsViewManager* aViewManager,
+                                                     nsStyleSet* aStyleSet) MOZ_OVERRIDE;
 
   virtual nsresult StartDocumentLoad(const char* aCommand,
                                      nsIChannel* aChannel,
                                      nsILoadGroup* aLoadGroup,
                                      nsISupports* aContainer,
                                      nsIStreamListener **aDocListener,
                                      bool aReset = true,
                                      nsIContentSink* aSink = nullptr);
--- a/content/media/AudioNodeEngine.h
+++ b/content/media/AudioNodeEngine.h
@@ -14,21 +14,16 @@ namespace mozilla {
 
 namespace dom {
 class AudioNode;
 struct ThreeDPoint;
 }
 
 class AudioNodeStream;
 
-// We ensure that the graph advances in steps that are multiples of the Web
-// Audio block size
-const uint32_t WEBAUDIO_BLOCK_SIZE_BITS = 7;
-const uint32_t WEBAUDIO_BLOCK_SIZE = 1 << WEBAUDIO_BLOCK_SIZE_BITS;
-
 /**
  * This class holds onto a set of immutable channel buffers. The storage
  * for the buffers must be malloced, but the buffer pointers and the malloc
  * pointers can be different (e.g. if the buffers are contained inside
  * some malloced object).
  */
 class ThreadSharedFloatArrayBufferList : public ThreadSharedObject {
 public:
@@ -149,17 +144,16 @@ AudioBlockPanStereoToStereo(const float 
  * MediaStreamGraph thread.
  */
 class AudioNodeEngine {
 public:
   explicit AudioNodeEngine(dom::AudioNode* aNode)
     : mNode(aNode)
     , mNodeMutex("AudioNodeEngine::mNodeMutex")
   {
-    MOZ_ASSERT(mNode, "The engine is constructed with a null node");
     MOZ_COUNT_CTOR(AudioNodeEngine);
   }
   virtual ~AudioNodeEngine()
   {
     MOZ_ASSERT(!mNode, "The node reference must be already cleared");
     MOZ_COUNT_DTOR(AudioNodeEngine);
   }
 
@@ -205,16 +199,21 @@ public:
                                  AudioChunk* aOutput,
                                  bool* aFinished)
   {
     *aOutput = aInput;
   }
 
   Mutex& NodeMutex() { return mNodeMutex;}
 
+  bool HasNode() const
+  {
+    return !!mNode;
+  }
+
   dom::AudioNode* Node() const
   {
     mNodeMutex.AssertCurrentThreadOwns();
     return mNode;
   }
 
   dom::AudioNode* NodeMainThread() const
   {
--- a/content/media/AudioNodeStream.cpp
+++ b/content/media/AudioNodeStream.cpp
@@ -97,29 +97,29 @@ AudioNodeStream::SetInt32Parameter(uint3
   };
 
   MOZ_ASSERT(this);
   GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
 }
 
 void
 AudioNodeStream::SetTimelineParameter(uint32_t aIndex,
-                                      const AudioEventTimeline<ErrorResult>& aValue)
+                                      const AudioParamTimeline& aValue)
 {
   class Message : public ControlMessage {
   public:
     Message(AudioNodeStream* aStream, uint32_t aIndex,
-            const AudioEventTimeline<ErrorResult>& aValue)
+            const AudioParamTimeline& aValue)
       : ControlMessage(aStream), mValue(aValue), mIndex(aIndex) {}
     virtual void Run()
     {
       static_cast<AudioNodeStream*>(mStream)->Engine()->
           SetTimelineParameter(mIndex, mValue);
     }
-    AudioEventTimeline<ErrorResult> mValue;
+    AudioParamTimeline mValue;
     uint32_t mIndex;
   };
   GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
 }
 
 void
 AudioNodeStream::SetThreeDPointParameter(uint32_t aIndex, const ThreeDPoint& aValue)
 {
@@ -242,17 +242,18 @@ AudioNodeStream::ObtainInputBlock(AudioC
 {
   uint32_t inputCount = mInputs.Length();
   uint32_t outputChannelCount = 1;
   nsAutoTArray<AudioChunk*,250> inputChunks;
   for (uint32_t i = 0; i < inputCount; ++i) {
     MediaStream* s = mInputs[i]->GetSource();
     AudioNodeStream* a = static_cast<AudioNodeStream*>(s);
     MOZ_ASSERT(a == s->AsAudioNodeStream());
-    if (a->IsFinishedOnGraphThread()) {
+    if (a->IsFinishedOnGraphThread() ||
+        a->IsAudioParamStream()) {
       continue;
     }
     AudioChunk* chunk = &a->mLastChunk;
     MOZ_ASSERT(chunk);
     if (chunk->IsNull()) {
       continue;
     }
 
--- a/content/media/AudioNodeStream.h
+++ b/content/media/AudioNodeStream.h
@@ -17,16 +17,17 @@
 #else
 #define LOG(type, msg)
 #endif
 
 namespace mozilla {
 
 namespace dom {
 struct ThreeDPoint;
+class AudioParamTimeline;
 }
 
 class ThreadSharedFloatArrayBufferList;
 
 /**
  * An AudioNodeStream produces one audio track with ID AUDIO_TRACK.
  * The start time of the AudioTrack is aligned to the start time of the
  * AudioContext's destination node stream, plus some multiple of BLOCK_SIZE
@@ -44,17 +45,18 @@ public:
    * Transfers ownership of aEngine to the new AudioNodeStream.
    */
   AudioNodeStream(AudioNodeEngine* aEngine,
                   MediaStreamGraph::AudioNodeStreamKind aKind)
     : ProcessedMediaStream(nullptr),
       mEngine(aEngine),
       mKind(aKind),
       mNumberOfInputChannels(2),
-      mMarkAsFinishedAfterThisBlock(false)
+      mMarkAsFinishedAfterThisBlock(false),
+      mAudioParamStream(false)
   {
     mMixingMode.mChannelCountMode = dom::ChannelCountMode::Max;
     mMixingMode.mChannelInterpretation = dom::ChannelInterpretation::Speakers;
     // AudioNodes are always producing data
     mHasCurrentData = true;
     MOZ_COUNT_CTOR(AudioNodeStream);
   }
   ~AudioNodeStream();
@@ -69,28 +71,41 @@ public:
   void SetDoubleParameter(uint32_t aIndex, double aValue);
   void SetInt32Parameter(uint32_t aIndex, int32_t aValue);
   void SetTimelineParameter(uint32_t aIndex, const dom::AudioParamTimeline& aValue);
   void SetThreeDPointParameter(uint32_t aIndex, const dom::ThreeDPoint& aValue);
   void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer);
   void SetChannelMixingParameters(uint32_t aNumberOfChannels,
                                   dom::ChannelCountMode aChannelCountMoe,
                                   dom::ChannelInterpretation aChannelInterpretation);
+  void SetAudioParamHelperStream()
+  {
+    MOZ_ASSERT(!mAudioParamStream, "Can only do this once");
+    mAudioParamStream = true;
+  }
 
   virtual AudioNodeStream* AsAudioNodeStream() { return this; }
 
   // Graph thread only
   void SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelativeToStream,
                                   double aStreamTime);
   void SetChannelMixingParametersImpl(uint32_t aNumberOfChannels,
                                       dom::ChannelCountMode aChannelCountMoe,
                                       dom::ChannelInterpretation aChannelInterpretation);
   virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo);
   TrackTicks GetCurrentPosition();
   bool AllInputsFinished() const;
+  bool IsAudioParamStream() const
+  {
+    return mAudioParamStream;
+  }
+  const AudioChunk& LastChunk() const
+  {
+    return mLastChunk;
+  }
 
   // Any thread
   AudioNodeEngine* Engine() { return mEngine; }
 
 protected:
   void FinishOutput();
 
   StreamBuffer::Track* EnsureTrack();
@@ -107,13 +122,15 @@ protected:
   // The mixing modes
   struct {
     dom::ChannelCountMode mChannelCountMode : 16;
     dom::ChannelInterpretation mChannelInterpretation : 16;
   } mMixingMode;
   // Whether the stream should be marked as finished as soon
   // as the current time range has been computed block by block.
   bool mMarkAsFinishedAfterThisBlock;
+  // Whether the stream is an AudioParamHelper stream.
+  bool mAudioParamStream;
 };
 
 }
 
 #endif /* MOZILLA_AUDIONODESTREAM_H_ */
--- a/content/media/AudioSegment.h
+++ b/content/media/AudioSegment.h
@@ -15,16 +15,21 @@ namespace mozilla {
 
 class AudioStream;
 
 /**
  * For auto-arrays etc, guess this as the common number of channels.
  */
 const int GUESS_AUDIO_CHANNELS = 2;
 
+// We ensure that the graph advances in steps that are multiples of the Web
+// Audio block size
+const uint32_t WEBAUDIO_BLOCK_SIZE_BITS = 7;
+const uint32_t WEBAUDIO_BLOCK_SIZE = 1 << WEBAUDIO_BLOCK_SIZE_BITS;
+
 /**
  * An AudioChunk represents a multi-channel buffer of audio samples.
  * It references an underlying ThreadSharedObject which manages the lifetime
  * of the buffer. An AudioChunk maintains its own duration and channel data
  * pointers so it can represent a subinterval of a buffer without copying.
  * An AudioChunk can store its individual channels anywhere; it maintains
  * separate pointers to each channel's buffer.
  */
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -2012,16 +2012,18 @@ AudioNodeStream*
 MediaStreamGraph::CreateAudioNodeStream(AudioNodeEngine* aEngine,
                                         AudioNodeStreamKind aKind)
 {
   MOZ_ASSERT(NS_IsMainThread());
   AudioNodeStream* stream = new AudioNodeStream(aEngine, aKind);
   NS_ADDREF(stream);
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
   stream->SetGraphImpl(graph);
-  stream->SetChannelMixingParametersImpl(aEngine->NodeMainThread()->ChannelCount(),
-                                         aEngine->NodeMainThread()->ChannelCountModeValue(),
-                                         aEngine->NodeMainThread()->ChannelInterpretationValue());
+  if (aEngine->HasNode()) {
+    stream->SetChannelMixingParametersImpl(aEngine->NodeMainThread()->ChannelCount(),
+                                           aEngine->NodeMainThread()->ChannelCountModeValue(),
+                                           aEngine->NodeMainThread()->ChannelInterpretationValue());
+  }
   graph->AppendMessage(new CreateMessage(stream));
   return stream;
 }
 
 }
new file mode 100644
--- /dev/null
+++ b/content/media/gstreamer/GStreamerMozVideoBuffer.cpp
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 <string.h>
+#include "GStreamerReader.h"
+#include "GStreamerMozVideoBuffer.h"
+
+namespace mozilla {
+
+static GstMozVideoBuffer *gst_moz_video_buffer_copy(GstMozVideoBuffer* self);
+static void gst_moz_video_buffer_finalize(GstMozVideoBuffer* self);
+
+G_DEFINE_TYPE(GstMozVideoBuffer, gst_moz_video_buffer, GST_TYPE_BUFFER);
+
+static void
+gst_moz_video_buffer_class_init(GstMozVideoBufferClass* klass)
+{
+  g_return_if_fail(GST_IS_MOZ_VIDEO_BUFFER_CLASS(klass));
+
+  GstMiniObjectClass *mo_class = GST_MINI_OBJECT_CLASS(klass);
+
+  mo_class->copy =(GstMiniObjectCopyFunction)gst_moz_video_buffer_copy;
+  mo_class->finalize =(GstMiniObjectFinalizeFunction)gst_moz_video_buffer_finalize;
+}
+
+static void
+gst_moz_video_buffer_init(GstMozVideoBuffer* self)
+{
+  g_return_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self));
+}
+
+static void
+gst_moz_video_buffer_finalize(GstMozVideoBuffer* self)
+{
+  g_return_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self));
+
+  if(self->data)
+    g_boxed_free(GST_TYPE_MOZ_VIDEO_BUFFER_DATA, self->data);
+
+  GST_MINI_OBJECT_CLASS(gst_moz_video_buffer_parent_class)->finalize(GST_MINI_OBJECT(self));
+}
+
+static GstMozVideoBuffer*
+gst_moz_video_buffer_copy(GstMozVideoBuffer* self)
+{
+  GstMozVideoBuffer* copy;
+
+  g_return_val_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self), NULL);
+
+  copy = gst_moz_video_buffer_new();
+
+  /* we simply copy everything from our parent */
+  GST_BUFFER_DATA(GST_BUFFER_CAST(copy)) =
+      (guint8*)g_memdup(GST_BUFFER_DATA(GST_BUFFER_CAST(self)), GST_BUFFER_SIZE(GST_BUFFER_CAST(self)));
+
+  /* make sure it gets freed(even if the parent is subclassed, we return a
+     normal buffer) */
+  GST_BUFFER_MALLOCDATA(GST_BUFFER_CAST(copy)) = GST_BUFFER_DATA(GST_BUFFER_CAST(copy));
+  GST_BUFFER_SIZE(GST_BUFFER_CAST(copy)) = GST_BUFFER_SIZE(GST_BUFFER_CAST(self));
+
+  /* copy metadata */
+  gst_buffer_copy_metadata(GST_BUFFER_CAST(copy),
+                           GST_BUFFER_CAST(self),
+                           (GstBufferCopyFlags)GST_BUFFER_COPY_ALL);
+  /* copy videobuffer */
+  if(self->data)
+    copy->data = (GstMozVideoBufferData*)g_boxed_copy(GST_TYPE_MOZ_VIDEO_BUFFER_DATA, self->data);
+
+  return copy;
+}
+
+GstMozVideoBuffer*
+gst_moz_video_buffer_new(void)
+{
+  GstMozVideoBuffer *self;
+
+  self =(GstMozVideoBuffer*)gst_mini_object_new(GST_TYPE_MOZ_VIDEO_BUFFER);
+  self->data = nullptr;
+
+  return self;
+}
+
+void
+gst_moz_video_buffer_set_data(GstMozVideoBuffer* self, GstMozVideoBufferData* data)
+{
+  g_return_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self));
+
+  self->data = data;
+}
+
+GstMozVideoBufferData*
+gst_moz_video_buffer_get_data(const GstMozVideoBuffer* self)
+{
+  g_return_val_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self), NULL);
+
+  return self->data;
+}
+
+GType
+gst_moz_video_buffer_data_get_type(void)
+{
+  static volatile gsize g_define_type_id__volatile = 0;
+
+  if(g_once_init_enter(&g_define_type_id__volatile)) {
+    GType g_define_type_id =
+        g_boxed_type_register_static(g_intern_static_string("GstMozVideoBufferData"),
+                                     (GBoxedCopyFunc)GstMozVideoBufferData::Copy,
+                                     (GBoxedFreeFunc)GstMozVideoBufferData::Free);
+    g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
+  }
+
+  return g_define_type_id__volatile;
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/content/media/gstreamer/GStreamerMozVideoBuffer.h
@@ -0,0 +1,60 @@
+/* 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 __GST_MOZ_VIDEO_BUFFER_H__
+#define __GST_MOZ_VIDEO_BUFFER_H__
+
+#include <gst/gst.h>
+#include "MediaDecoderReader.h"
+
+namespace mozilla {
+
+#define GST_TYPE_MOZ_VIDEO_BUFFER_DATA            (gst_moz_video_buffer_data_get_type())
+#define GST_IS_MOZ_VIDEO_BUFFER_DATA(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MOZ_VIDEO_BUFFER_DATA))
+
+#define GST_TYPE_MOZ_VIDEO_BUFFER            (gst_moz_video_buffer_get_type())
+#define GST_IS_MOZ_VIDEO_BUFFER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MOZ_VIDEO_BUFFER))
+#define GST_IS_MOZ_VIDEO_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MOZ_VIDEO_BUFFER))
+#define GST_MOZ_VIDEO_BUFFER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MOZ_VIDEO_BUFFER, GstMozVideoBuffer))
+
+typedef struct _GstMozVideoBuffer GstMozVideoBuffer;
+typedef struct _GstMozVideoBufferClass GstMozVideoBufferClass;
+
+class GstMozVideoBufferData;
+
+struct _GstMozVideoBuffer {
+  GstBuffer buffer;
+  GstMozVideoBufferData* data;
+};
+
+struct _GstMozVideoBufferClass {
+  GstBufferClass  buffer_class;
+};
+
+GType gst_moz_video_buffer_get_type(void);
+GstMozVideoBuffer* gst_moz_video_buffer_new(void);
+void gst_moz_video_buffer_set_data(GstMozVideoBuffer* buf, GstMozVideoBufferData* data);
+GstMozVideoBufferData* gst_moz_video_buffer_get_data(const GstMozVideoBuffer* buf);
+
+class GstMozVideoBufferData {
+  public:
+    GstMozVideoBufferData(layers::PlanarYCbCrImage* aImage) : mImage(aImage) {}
+
+    static void* Copy(void* aData) {
+      return new GstMozVideoBufferData(reinterpret_cast<GstMozVideoBufferData*>(aData)->mImage);
+    }
+
+    static void Free(void* aData) {
+      delete reinterpret_cast<GstMozVideoBufferData*>(aData);
+    }
+
+    nsRefPtr<layers::PlanarYCbCrImage> mImage;
+};
+
+GType gst_moz_video_buffer_data_get_type (void);
+
+} // namespace mozilla
+
+#endif /* __GST_MOZ_VIDEO_BUFFER_H__ */
+
--- a/content/media/gstreamer/GStreamerReader.cpp
+++ b/content/media/gstreamer/GStreamerReader.cpp
@@ -5,16 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsError.h"
 #include "MediaDecoderStateMachine.h"
 #include "AbstractMediaDecoder.h"
 #include "MediaResource.h"
 #include "GStreamerReader.h"
 #include "GStreamerFormatHelper.h"
+#include "GStreamerMozVideoBuffer.h"
 #include "VideoUtils.h"
 #include "mozilla/dom/TimeRanges.h"
 #include "mozilla/Preferences.h"
 
 namespace mozilla {
 
 using namespace layers;
 
@@ -34,18 +35,16 @@ IsYV12Format(const VideoData::YCbCrBuffe
              const VideoData::YCbCrBuffer::Plane& aCrPlane);
 
 static const int MAX_CHANNELS = 4;
 // Let the demuxer work in pull mode for short files
 static const int SHORT_FILE_SIZE = 1024 * 1024;
 // The default resource->Read() size when working in push mode
 static const int DEFAULT_SOURCE_READ_SIZE = 50 * 1024;
 
-G_DEFINE_BOXED_TYPE(BufferData, buffer_data, BufferData::Copy, BufferData::Free);
-
 typedef enum {
   GST_PLAY_FLAG_VIDEO         = (1 << 0),
   GST_PLAY_FLAG_AUDIO         = (1 << 1),
   GST_PLAY_FLAG_TEXT          = (1 << 2),
   GST_PLAY_FLAG_VIS           = (1 << 3),
   GST_PLAY_FLAG_SOFT_VOLUME   = (1 << 4),
   GST_PLAY_FLAG_NATIVE_AUDIO  = (1 << 5),
   GST_PLAY_FLAG_NATIVE_VIDEO  = (1 << 6),
@@ -134,19 +133,17 @@ nsresult GStreamerReader::Init(MediaDeco
   mVideoAppSink = GST_APP_SINK(gst_bin_get_by_name(GST_BIN(mVideoSink),
         "videosink"));
   gst_app_sink_set_callbacks(mVideoAppSink, &mSinkCallbacks,
       (gpointer) this, nullptr);
   GstPad* sinkpad = gst_element_get_pad(GST_ELEMENT(mVideoAppSink), "sink");
   gst_pad_add_event_probe(sinkpad,
       G_CALLBACK(&GStreamerReader::EventProbeCb), this);
   gst_object_unref(sinkpad);
-#if GST_VERSION_MICRO >= 36
   gst_pad_set_bufferalloc_function(sinkpad, GStreamerReader::AllocateVideoBufferCb);
-#endif
   gst_pad_set_element_private(sinkpad, this);
 
   mAudioSink = gst_parse_bin_from_description("capsfilter name=filter ! "
 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
         "appsink name=audiosink sync=true caps=audio/x-raw-float,"
 #ifdef IS_LITTLE_ENDIAN
         "channels={1,2},width=32,endianness=1234", TRUE, nullptr);
 #else
@@ -527,25 +524,21 @@ bool GStreamerReader::DecodeVideoFrame(b
     break;
   }
 
   if (!buffer)
     /* no more frames */
     return false;
 
   nsRefPtr<PlanarYCbCrImage> image;
-#if GST_VERSION_MICRO >= 36
-  const GstStructure* structure = gst_buffer_get_qdata(buffer,
-      g_quark_from_string("moz-reader-data"));
-  const GValue* value = gst_structure_get_value(structure, "image");
-  if (value) {
-    BufferData* data = reinterpret_cast<BufferData*>(g_value_get_boxed(value));
-    image = data->mImage;
-  }
-#endif
+  GstMozVideoBufferData* bufferdata = reinterpret_cast<GstMozVideoBufferData*>
+      GST_IS_MOZ_VIDEO_BUFFER(buffer)?gst_moz_video_buffer_get_data(GST_MOZ_VIDEO_BUFFER(buffer)):nullptr;
+
+  if(bufferdata)
+    image = bufferdata->mImage;
 
   if (!image) {
     /* Ugh, upstream is not calling gst_pad_alloc_buffer(). Fallback to
      * allocating a PlanarYCbCrImage backed GstBuffer here and memcpy.
      */
     GstBuffer* tmp = nullptr;
     AllocateVideoBufferFull(nullptr, GST_BUFFER_OFFSET(buffer),
         GST_BUFFER_SIZE(buffer), nullptr, &tmp, image);
@@ -839,39 +832,28 @@ GstFlowReturn GStreamerReader::AllocateV
 {
   /* allocate an image using the container */
   ImageContainer* container = mDecoder->GetImageContainer();
   ImageFormat format = PLANAR_YCBCR;
   PlanarYCbCrImage* img = reinterpret_cast<PlanarYCbCrImage*>(container->CreateImage(&format, 1).get());
   nsRefPtr<PlanarYCbCrImage> image = dont_AddRef(img);
 
   /* prepare a GstBuffer pointing to the underlying PlanarYCbCrImage buffer */
-  GstBuffer* buf = gst_buffer_new();
+  GstBuffer* buf = GST_BUFFER(gst_moz_video_buffer_new());
   GST_BUFFER_SIZE(buf) = aSize;
   /* allocate the actual YUV buffer */
   GST_BUFFER_DATA(buf) = image->AllocateAndGetNewBuffer(aSize);
 
   aImage = image;
 
-#if GST_VERSION_MICRO >= 36
-  /* create a GBoxed handle to hold the image */
-  BufferData* data = new BufferData(image);
+  /* create a GstMozVideoBufferData to hold the image */
+  GstMozVideoBufferData* bufferdata = new GstMozVideoBufferData(image);
 
-  /* store it in a GValue so we can put it in a GstStructure */
-  GValue value = {0,};
-  g_value_init(&value, buffer_data_get_type());
-  g_value_take_boxed(&value, data);
-
-  /* store the value in the structure */
-  GstStructure* structure = gst_structure_new("moz-reader-data", nullptr);
-  gst_structure_take_value(structure, "image", &value);
-
-  /* and attach the structure to the buffer */
-  gst_buffer_set_qdata(buf, g_quark_from_string("moz-reader-data"), structure);
-#endif
+  /* Attach bufferdata to our GstMozVideoBuffer, it will take care to free it */
+  gst_moz_video_buffer_set_data(GST_MOZ_VIDEO_BUFFER(buf), bufferdata);
 
   *aBuf = buf;
   return GST_FLOW_OK;
 }
 
 GstFlowReturn GStreamerReader::AllocateVideoBufferCb(GstPad* aPad,
                                                      guint64 aOffset,
                                                      guint aSize,
--- a/content/media/gstreamer/GStreamerReader.h
+++ b/content/media/gstreamer/GStreamerReader.h
@@ -148,26 +148,11 @@ private:
   /* offset we've reached reading from the source */
   gint64 mByteOffset;
   /* the last offset we reported with NotifyBytesConsumed */
   gint64 mLastReportedByteOffset;
   int fpsNum;
   int fpsDen;
 };
 
-class BufferData {
-  public:
-    BufferData(layers::PlanarYCbCrImage* aImage) : mImage(aImage) {}
-
-    static void* Copy(void* aData) {
-      return new BufferData(reinterpret_cast<BufferData*>(aData)->mImage);
-    }
-
-    static void Free(void* aData) {
-      delete reinterpret_cast<BufferData*>(aData);
-    }
-
-    nsRefPtr<layers::PlanarYCbCrImage> mImage;
-};
-
 } // namespace mozilla
 
 #endif
--- a/content/media/gstreamer/Makefile.in
+++ b/content/media/gstreamer/Makefile.in
@@ -13,16 +13,17 @@ include $(DEPTH)/config/autoconf.mk
 LIBRARY_NAME	= gkcongstreamer_s
 LIBXUL_LIBRARY 	= 1
 
 
 CPPSRCS		= \
 		GStreamerReader.cpp \
 		GStreamerDecoder.cpp \
 		GStreamerFormatHelper.cpp \
+		GStreamerMozVideoBuffer.cpp \
 		$(NULL)
 
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 CFLAGS		+= $(GSTREAMER_CFLAGS)
 CXXFLAGS	+= $(GSTREAMER_CFLAGS)
--- a/content/media/webaudio/AudioBufferSourceNode.cpp
+++ b/content/media/webaudio/AudioBufferSourceNode.cpp
@@ -16,16 +16,21 @@
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioBufferSourceNode)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBuffer)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlaybackRate)
   if (tmp->Context()) {
+    // AudioNode's Unlink implementation disconnects us from the graph
+    // too, but we need to do this right here to make sure that
+    // UnregisterAudioBufferSourceNode can properly untangle us from
+    // the possibly connected PannerNodes.
+    tmp->DisconnectFromGraph();
     tmp->Context()->UnregisterAudioBufferSourceNode(tmp);
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(AudioNode)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioBufferSourceNode, AudioNode)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBuffer)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaybackRate)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -299,32 +304,32 @@ public:
 
   bool ShouldResample() const
   {
     return !(mPlaybackRate == 1.0 &&
              mDopplerShift == 1.0 &&
              mSampleRate == IdealAudioRate());
   }
 
-  void UpdateSampleRateIfNeeded(AudioNodeStream* aStream)
+  void UpdateSampleRateIfNeeded(AudioNodeStream* aStream, uint32_t aChannels)
   {
     if (mPlaybackRateTimeline.HasSimpleValue()) {
       mPlaybackRate = mPlaybackRateTimeline.GetValue();
     } else {
-      mPlaybackRate = mPlaybackRateTimeline.GetValueAtTime<TrackTicks>(aStream->GetCurrentPosition());
+      mPlaybackRate = mPlaybackRateTimeline.GetValueAtTime(aStream->GetCurrentPosition());
     }
 
     // Make sure the playback rate if something our resampler can work with.
     if (mPlaybackRate <= 0.0 || mPlaybackRate >= 1024) {
       mPlaybackRate = 1.0;
     }
 
     uint32_t currentOutSampleRate, currentInSampleRate;
     if (ShouldResample()) {
-      SpeexResamplerState* resampler = Resampler(mChannels);
+      SpeexResamplerState* resampler = Resampler(aChannels);
       speex_resampler_get_rate(resampler, &currentInSampleRate, &currentOutSampleRate);
       uint32_t finalSampleRate = ComputeFinalOutSampleRate();
       if (currentOutSampleRate != finalSampleRate) {
         speex_resampler_set_rate(resampler, currentInSampleRate, finalSampleRate);
       }
     }
   }
 
@@ -340,17 +345,17 @@ public:
     if (!channels) {
       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
       return;
     }
 
     // WebKit treats the playbackRate as a k-rate parameter in their code,
     // despite the spec saying that it should be an a-rate parameter. We treat
     // it as k-rate. Spec bug: https://www.w3.org/Bugs/Public/show_bug.cgi?id=21592
-    UpdateSampleRateIfNeeded(aStream);
+    UpdateSampleRateIfNeeded(aStream, channels);
 
     uint32_t written = 0;
     TrackTicks currentPosition = GetPosition(aStream);
     while (written < WEBAUDIO_BLOCK_SIZE) {
       if (mStop != TRACK_TICKS_MAX &&
           currentPosition >= mStop) {
         FillWithZeroes(aOutput, channels, &written, &currentPosition, TRACK_TICKS_MAX);
         continue;
@@ -407,17 +412,16 @@ AudioBufferSourceNode::AudioBufferSource
               2,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
   , mLoopStart(0.0)
   , mLoopEnd(0.0)
   , mOffset(0.0)
   , mDuration(std::numeric_limits<double>::min())
   , mPlaybackRate(new AudioParam(this, SendPlaybackRateToStream, 1.0f))
-  , mPannerNode(nullptr)
   , mLoop(false)
   , mStartCalled(false)
   , mOffsetAndDurationRemembered(false)
 {
   mStream = aContext->Graph()->CreateAudioNodeStream(
       new AudioBufferSourceNodeEngine(this, aContext->Destination()),
       MediaStreamGraph::INTERNAL_STREAM);
   mStream->AddMainThreadListener(this);
--- a/content/media/webaudio/AudioBufferSourceNode.h
+++ b/content/media/webaudio/AudioBufferSourceNode.h
@@ -32,25 +32,16 @@ public:
   virtual uint32_t NumberOfInputs() const MOZ_FINAL MOZ_OVERRIDE
   {
     return 0;
   }
   virtual AudioBufferSourceNode* AsAudioBufferSourceNode() MOZ_OVERRIDE
   {
     return this;
   }
-
-  void UnregisterPannerNode() {
-    mPannerNode = nullptr;
-  }
-
-  void RegisterPannerNode(PannerNode* aPannerNode) {
-    mPannerNode = aPannerNode;
-  }
-
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioBufferSourceNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   void Start(double aWhen, double aOffset,
              const Optional<double>& aDuration, ErrorResult& aRv);
@@ -143,17 +134,16 @@ private:
 
 private:
   double mLoopStart;
   double mLoopEnd;
   double mOffset;
   double mDuration;
   nsRefPtr<AudioBuffer> mBuffer;
   nsRefPtr<AudioParam> mPlaybackRate;
-  PannerNode* mPannerNode;
   SelfReference<AudioBufferSourceNode> mPlayingRef; // a reference to self while playing
   bool mLoop;
   bool mStartCalled;
   bool mOffsetAndDurationRemembered;
 };
 
 }
 }
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -239,48 +239,41 @@ AudioContext::RemoveFromDecodeQueue(WebA
 {
   mDecodeJobs.RemoveElement(aDecodeJob);
 }
 
 void
 AudioContext::UnregisterAudioBufferSourceNode(AudioBufferSourceNode* aNode)
 {
   mAudioBufferSourceNodes.RemoveEntry(aNode);
+  UpdatePannerSource();
 }
 
 void
 AudioContext::UnregisterPannerNode(PannerNode* aNode)
 {
   mPannerNodes.RemoveEntry(aNode);
 }
 
 void
 AudioContext::UnregisterScriptProcessorNode(ScriptProcessorNode* aNode)
 {
   mScriptProcessorNodes.RemoveEntry(aNode);
 }
 
 static PLDHashOperator
-UnregisterPannerNodeOn(nsPtrHashKey<AudioBufferSourceNode>* aEntry, void* aData)
-{
-  aEntry->GetKey()->UnregisterPannerNode();
-  return PL_DHASH_NEXT;
-}
-
-static PLDHashOperator
 FindConnectedSourcesOn(nsPtrHashKey<PannerNode>* aEntry, void* aData)
 {
   aEntry->GetKey()->FindConnectedSources();
   return PL_DHASH_NEXT;
 }
 
 void
 AudioContext::UpdatePannerSource()
 {
-  mAudioBufferSourceNodes.EnumerateEntries(UnregisterPannerNodeOn, nullptr);
   mPannerNodes.EnumerateEntries(FindConnectedSourcesOn, nullptr);
 }
 
 MediaStreamGraph*
 AudioContext::Graph() const
 {
   return Destination()->Stream()->Graph();
 }
--- a/content/media/webaudio/AudioNode.cpp
+++ b/content/media/webaudio/AudioNode.cpp
@@ -8,24 +8,28 @@
 #include "AudioContext.h"
 #include "nsContentUtils.h"
 #include "mozilla/ErrorResult.h"
 #include "AudioNodeStream.h"
 
 namespace mozilla {
 namespace dom {
 
+static const uint32_t INVALID_PORT = 0xffffffff;
+
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AudioNode, nsDOMEventTargetHelper)
   tmp->DisconnectFromGraph();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputNodes)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputParams)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioNode, nsDOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputNodes)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputParams)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_ADDREF_INHERITED(AudioNode, nsDOMEventTargetHelper)
 
 NS_IMETHODIMP_(nsrefcnt)
 AudioNode::Release()
 {
   if (mRefCnt.get() == 1) {
@@ -54,41 +58,44 @@ AudioNode::AudioNode(AudioContext* aCont
   nsDOMEventTargetHelper::BindToOwner(aContext->GetParentObject());
   SetIsDOMBinding();
 }
 
 AudioNode::~AudioNode()
 {
   MOZ_ASSERT(mInputNodes.IsEmpty());
   MOZ_ASSERT(mOutputNodes.IsEmpty());
+  MOZ_ASSERT(mOutputParams.IsEmpty());
 }
 
+template <class InputNode>
 static uint32_t
-FindIndexOfNode(const nsTArray<AudioNode::InputNode>& aInputNodes, const AudioNode* aNode)
+FindIndexOfNode(const nsTArray<InputNode>& aInputNodes, const AudioNode* aNode)
 {
   for (uint32_t i = 0; i < aInputNodes.Length(); ++i) {
     if (aInputNodes[i].mInputNode == aNode) {
       return i;
     }
   }
-  return nsTArray<AudioNode::InputNode>::NoIndex;
+  return nsTArray<InputNode>::NoIndex;
 }
 
+template <class InputNode>
 static uint32_t
-FindIndexOfNodeWithPorts(const nsTArray<AudioNode::InputNode>& aInputNodes, const AudioNode* aNode,
+FindIndexOfNodeWithPorts(const nsTArray<InputNode>& aInputNodes, const AudioNode* aNode,
                          uint32_t aInputPort, uint32_t aOutputPort)
 {
   for (uint32_t i = 0; i < aInputNodes.Length(); ++i) {
     if (aInputNodes[i].mInputNode == aNode &&
         aInputNodes[i].mInputPort == aInputPort &&
         aInputNodes[i].mOutputPort == aOutputPort) {
       return i;
     }
   }
-  return nsTArray<AudioNode::InputNode>::NoIndex;
+  return nsTArray<InputNode>::NoIndex;
 }
 
 void
 AudioNode::DisconnectFromGraph()
 {
   // Addref this temporarily so the refcount bumping below doesn't destroy us
   // prematurely
   nsRefPtr<AudioNode> kungFuDeathGrip = this;
@@ -109,16 +116,26 @@ AudioNode::DisconnectFromGraph()
     nsRefPtr<AudioNode> output = mOutputNodes[i].forget();
     mOutputNodes.RemoveElementAt(i);
     uint32_t inputIndex = FindIndexOfNode(output->mInputNodes, this);
     // It doesn't matter which one we remove, since we're going to remove all
     // entries for this node anyway.
     output->mInputNodes.RemoveElementAt(inputIndex);
   }
 
+  while (!mOutputParams.IsEmpty()) {
+    uint32_t i = mOutputParams.Length() - 1;
+    nsRefPtr<AudioParam> output = mOutputParams[i].forget();
+    mOutputParams.RemoveElementAt(i);
+    uint32_t inputIndex = FindIndexOfNode(output->InputNodes(), this);
+    // It doesn't matter which one we remove, since we're going to remove all
+    // entries for this node anyway.
+    output->RemoveInputNode(inputIndex);
+  }
+
   DestroyMediaStream();
 }
 
 void
 AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput,
                    uint32_t aInput, ErrorResult& aRv)
 {
   if (aOutput >= NumberOfOutputs() ||
@@ -155,16 +172,50 @@ AudioNode::Connect(AudioNode& aDestinati
       ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT);
   }
 
   // This connection may have connected a panner and a source.
   Context()->UpdatePannerSource();
 }
 
 void
+AudioNode::Connect(AudioParam& aDestination, uint32_t aOutput,
+                   ErrorResult& aRv)
+{
+  if (aOutput >= NumberOfOutputs()) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  if (Context() != aDestination.GetParentObject()) {
+    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return;
+  }
+
+  if (FindIndexOfNodeWithPorts(aDestination.InputNodes(), this, INVALID_PORT, aOutput) !=
+      nsTArray<AudioNode::InputNode>::NoIndex) {
+    // connection already exists.
+    return;
+  }
+
+  mOutputParams.AppendElement(&aDestination);
+  InputNode* input = aDestination.AppendInputNode();
+  input->mInputNode = this;
+  input->mInputPort = INVALID_PORT;
+  input->mOutputPort = aOutput;
+
+  MediaStream* stream = aDestination.Stream();
+  MOZ_ASSERT(stream->AsProcessedStream());
+  ProcessedMediaStream* ps = static_cast<ProcessedMediaStream*>(stream);
+
+  // Setup our stream as an input to the AudioParam's stream
+  input->mStreamPort = ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT);
+}
+
+void
 AudioNode::SendDoubleParameterToStream(uint32_t aIndex, double aValue)
 {
   AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
   MOZ_ASSERT(ns, "How come we don't have a stream here?");
   ns->SetDoubleParameter(aIndex, aValue);
 }
 
 void
@@ -219,16 +270,31 @@ AudioNode::Disconnect(uint32_t aOutput, 
         // others, and it's not correct to remove them all since some of them
         // could be for different output ports.
         mOutputNodes.RemoveElementAt(i);
         break;
       }
     }
   }
 
+  for (int32_t i = mOutputParams.Length() - 1; i >= 0; --i) {
+    AudioParam* dest = mOutputParams[i];
+    for (int32_t j = dest->InputNodes().Length() - 1; j >= 0; --j) {
+      const InputNode& input = dest->InputNodes()[j];
+      if (input.mInputNode == this && input.mOutputPort == aOutput) {
+        dest->RemoveInputNode(j);
+        // Remove one instance of 'dest' from mOutputParams. There could be
+        // others, and it's not correct to remove them all since some of them
+        // could be for different output ports.
+        mOutputParams.RemoveElementAt(i);
+        break;
+      }
+    }
+  }
+
   // This disconnection may have disconnected a panner and a source.
   Context()->UpdatePannerSource();
 }
 
 void
 AudioNode::DestroyMediaStream()
 {
   if (mStream) {
@@ -244,10 +310,16 @@ AudioNode::DestroyMediaStream()
       ns->Engine()->ClearNode();
     }
 
     mStream->Destroy();
     mStream = nullptr;
   }
 }
 
+void
+AudioNode::RemoveOutputParam(AudioParam* aParam)
+{
+  mOutputParams.RemoveElement(aParam);
+}
+
 }
 }
--- a/content/media/webaudio/AudioNode.h
+++ b/content/media/webaudio/AudioNode.h
@@ -21,16 +21,17 @@
 struct JSContext;
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
+class AudioParam;
 struct ThreeDPoint;
 
 template<class T>
 class SelfReference {
 public:
   SelfReference() : mHeld(false) {}
   ~SelfReference()
   {
@@ -103,16 +104,19 @@ public:
   AudioContext* Context() const
   {
     return mContext;
   }
 
   virtual void Connect(AudioNode& aDestination, uint32_t aOutput,
                        uint32_t aInput, ErrorResult& aRv);
 
+  virtual void Connect(AudioParam& aDestination, uint32_t aOutput,
+                       ErrorResult& aRv);
+
   virtual void Disconnect(uint32_t aOutput, ErrorResult& aRv);
 
   // The following two virtual methods must be implemented by each node type
   // to provide their number of input and output ports. These numbers are
   // constant for the lifetime of the node. Both default to 1.
   virtual uint32_t NumberOfInputs() const { return 1; }
   virtual uint32_t NumberOfOutputs() const { return 1; }
 
@@ -148,29 +152,33 @@ public:
         mStreamPort->Destroy();
       }
     }
 
     // Weak reference.
     AudioNode* mInputNode;
     nsRefPtr<MediaInputPort> mStreamPort;
     // The index of the input port this node feeds into.
+    // This is not used for connections to AudioParams.
     uint32_t mInputPort;
     // The index of the output port this node comes out of.
     uint32_t mOutputPort;
   };
 
   MediaStream* Stream() { return mStream; }
 
   const nsTArray<InputNode>& InputNodes() const
   {
     return mInputNodes;
   }
 
+  void RemoveOutputParam(AudioParam* aParam);
+
 private:
+  friend class AudioBufferSourceNode;
   // This could possibly delete 'this'.
   void DisconnectFromGraph();
 
 protected:
   static void Callback(AudioNode* aNode) { /* not implemented */ }
 
   // Helpers for sending different value types to streams
   void SendDoubleParameterToStream(uint32_t aIndex, double aValue);
@@ -192,16 +200,22 @@ private:
   // For every InputNode, there is a corresponding entry in mOutputNodes of the
   // InputNode's mInputNode.
   nsTArray<InputNode> mInputNodes;
   // For every mOutputNode entry, there is a corresponding entry in mInputNodes
   // of the mOutputNode entry. We won't necessarily be able to identify the
   // exact matching entry, since mOutputNodes doesn't include the port
   // identifiers and the same node could be connected on multiple ports.
   nsTArray<nsRefPtr<AudioNode> > mOutputNodes;
+  // For every mOutputParams entry, there is a corresponding entry in
+  // AudioParam::mInputNodes of the mOutputParams entry. We won't necessarily be
+  // able to identify the exact matching entry, since mOutputParams doesn't
+  // include the port identifiers and the same node could be connected on
+  // multiple ports.
+  nsTArray<nsRefPtr<AudioParam> > mOutputParams;
   uint32_t mChannelCount;
   ChannelCountMode mChannelCountMode;
   ChannelInterpretation mChannelInterpretation;
 };
 
 }
 }
 
--- a/content/media/webaudio/AudioParam.cpp
+++ b/content/media/webaudio/AudioParam.cpp
@@ -4,21 +4,46 @@
  * 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 "AudioParam.h"
 #include "nsContentUtils.h"
 #include "nsIDOMWindow.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/AudioParamBinding.h"
+#include "AudioNodeEngine.h"
+#include "AudioNodeStream.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(AudioParam, mNode)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioParam)
+  tmp->DisconnectFromGraphAndDestroyStream();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNode)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AudioParam)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNode)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(AudioParam)
+
+NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(AudioParam)
+
+NS_IMETHODIMP_(nsrefcnt)
+AudioParam::Release()
+{
+  if (mRefCnt.get() == 1) {
+    // We are about to be deleted, disconnect the object from the graph before
+    // the derived type is destroyed.
+    DisconnectFromGraphAndDestroyStream();
+  }
+  NS_IMPL_CC_NATIVE_RELEASE_BODY(AudioParam)
+}
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AudioParam, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AudioParam, Release)
 
 AudioParam::AudioParam(AudioNode* aNode,
                        AudioParam::CallbackType aCallback,
                        float aDefaultValue)
   : AudioParamTimeline(aDefaultValue)
@@ -26,19 +51,95 @@ AudioParam::AudioParam(AudioNode* aNode,
   , mCallback(aCallback)
   , mDefaultValue(aDefaultValue)
 {
   SetIsDOMBinding();
 }
 
 AudioParam::~AudioParam()
 {
+  MOZ_ASSERT(mInputNodes.IsEmpty());
 }
 
 JSObject*
 AudioParam::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return AudioParamBinding::Wrap(aCx, aScope, this);
 }
 
+void
+AudioParam::DisconnectFromGraphAndDestroyStream()
+{
+  // Addref this temporarily so the refcount bumping below doesn't destroy us
+  // prematurely
+  nsRefPtr<AudioParam> kungFuDeathGrip = this;
+
+  while (!mInputNodes.IsEmpty()) {
+    uint32_t i = mInputNodes.Length() - 1;
+    nsRefPtr<AudioNode> input = mInputNodes[i].mInputNode;
+    mInputNodes.RemoveElementAt(i);
+    input->RemoveOutputParam(this);
+  }
+
+  if (mNodeStreamPort) {
+    mNodeStreamPort->Destroy();
+    mNodeStreamPort = nullptr;
+  }
+
+  if (mStream) {
+    mStream->Destroy();
+    mStream = nullptr;
+  }
+}
+
+MediaStream*
+AudioParam::Stream()
+{
+  if (mStream) {
+    return mStream;
+  }
+
+  AudioNodeEngine* engine = new AudioNodeEngine(nullptr);
+  nsRefPtr<AudioNodeStream> stream = mNode->Context()->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
+
+  // Force the input to have only one channel, and make it down-mix using
+  // the speaker rules if needed.
+  stream->SetChannelMixingParametersImpl(1, ChannelCountMode::Explicit, ChannelInterpretation::Speakers);
+  // Mark as an AudioParam helper stream
+  stream->SetAudioParamHelperStream();
+
+  mStream = stream.forget();
+
+  // Setup the AudioParam's stream as an input to the owner AudioNode's stream
+  MediaStream* nodeStream = mNode->Stream();
+  MOZ_ASSERT(nodeStream->AsProcessedStream());
+  ProcessedMediaStream* ps = static_cast<ProcessedMediaStream*>(nodeStream);
+  mNodeStreamPort = ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT);
+
+  // Let the MSG's copy of AudioParamTimeline know about the change in the stream
+  mCallback(mNode);
+
+  return mStream;
+}
+
+float
+AudioParamTimeline::AudioNodeInputValue(size_t aCounter) const
+{
+  MOZ_ASSERT(mStream);
+
+  // If we have a chunk produced by the AudioNode inputs to the AudioParam,
+  // get its value now.  We use aCounter to tell us which frame of the last
+  // AudioChunk to look at.
+  float audioNodeInputValue = 0.0f;
+  const AudioChunk& lastAudioNodeChunk =
+    static_cast<AudioNodeStream*>(mStream.get())->LastChunk();
+  if (!lastAudioNodeChunk.IsNull()) {
+    MOZ_ASSERT(lastAudioNodeChunk.GetDuration() == WEBAUDIO_BLOCK_SIZE);
+    audioNodeInputValue =
+      static_cast<const float*>(lastAudioNodeChunk.mChannelData[0])[aCounter];
+  }
+
+  return audioNodeInputValue;
+}
+
 }
 }
 
--- a/content/media/webaudio/AudioParam.h
+++ b/content/media/webaudio/AudioParam.h
@@ -32,17 +32,18 @@ class AudioParam MOZ_FINAL : public nsWr
 public:
   typedef void (*CallbackType)(AudioNode*);
 
   AudioParam(AudioNode* aNode,
              CallbackType aCallback,
              float aDefaultValue);
   virtual ~AudioParam();
 
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AudioParam)
+  NS_IMETHOD_(nsrefcnt) AddRef(void);
+  NS_IMETHOD_(nsrefcnt) Release(void);
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AudioParam)
 
   AudioContext* GetParentObject() const
   {
     return mNode->Context();
   }
 
   virtual JSObject* WrapObject(JSContext* aCx,
@@ -99,19 +100,53 @@ public:
     mCallback(mNode);
   }
 
   float DefaultValue() const
   {
     return mDefaultValue;
   }
 
+  AudioNode* Node() const
+  {
+    return mNode;
+  }
+
+  const nsTArray<AudioNode::InputNode>& InputNodes() const
+  {
+    return mInputNodes;
+  }
+
+  void RemoveInputNode(uint32_t aIndex)
+  {
+    mInputNodes.RemoveElementAt(aIndex);
+  }
+
+  AudioNode::InputNode* AppendInputNode()
+  {
+    return mInputNodes.AppendElement();
+  }
+
+  void DisconnectFromGraphAndDestroyStream();
+
+  // May create the stream if it doesn't exist
+  MediaStream* Stream();
+
+protected:
+  nsCycleCollectingAutoRefCnt mRefCnt;
+  NS_DECL_OWNINGTHREAD
+
 private:
   nsRefPtr<AudioNode> mNode;
+  // For every InputNode, there is a corresponding entry in mOutputParams of the
+  // InputNode's mInputNode.
+  nsTArray<AudioNode::InputNode> mInputNodes;
   CallbackType mCallback;
   const float mDefaultValue;
+  // The input port used to connect the AudioParam's stream to its node's stream
+  nsRefPtr<MediaInputPort> mNodeStreamPort;
 };
 
 }
 }
 
 #endif
 
--- a/content/media/webaudio/AudioParamTimeline.h
+++ b/content/media/webaudio/AudioParamTimeline.h
@@ -2,25 +2,74 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 AudioParamTimeline_h_
 #define AudioParamTimeline_h_
 
-// This header is intended to make it possible to use AudioParamTimeline
-// from multiple places without dealing with #include hell!
-
 #include "AudioEventTimeline.h"
 #include "mozilla/ErrorResult.h"
+#include "nsAutoPtr.h"
+#include "MediaStreamGraph.h"
+#include "AudioSegment.h"
 
 namespace mozilla {
 
 namespace dom {
 
-typedef AudioEventTimeline<ErrorResult> AudioParamTimeline;
+// This helper class is used to represent the part of the AudioParam
+// class that gets sent to AudioNodeEngine instances.  In addition to
+// AudioEventTimeline methods, it holds a pointer to an optional
+// MediaStream which represents the AudioNode inputs to the AudioParam.
+// This MediaStream is managed by the AudioParam subclass on the main
+// thread, and can only be obtained from the AudioNodeEngine instances
+// consuming this class.
+class AudioParamTimeline : public AudioEventTimeline<ErrorResult>
+{
+  typedef AudioEventTimeline<ErrorResult> BaseClass;
+
+public:
+  explicit AudioParamTimeline(float aDefaultValue)
+    : BaseClass(aDefaultValue)
+  {
+  }
+
+  MediaStream* Stream() const
+  {
+    return mStream;
+  }
+
+  bool HasSimpleValue() const
+  {
+    return BaseClass::HasSimpleValue() && !mStream;
+  }
+
+  // Get the value of the AudioParam at time aTime + aCounter.
+  // aCounter here is an offset to aTime if we try to get the value in ticks,
+  // otherwise it should always be zero.  aCounter is meant to be used when
+  // getting the value of an a-rate AudioParam for each tick inside an
+  // AudioNodeEngine implementation.
+  template<class TimeType>
+  float GetValueAtTime(TimeType aTime, size_t aCounter = 0) const
+  {
+    MOZ_ASSERT(aCounter < WEBAUDIO_BLOCK_SIZE);
+    MOZ_ASSERT(!aCounter || !HasSimpleValue());
+
+    // Mix the value of the AudioParam itself with that of the AudioNode inputs.
+    return BaseClass::GetValueAtTime(static_cast<TimeType>(aTime + aCounter)) +
+           (mStream ? AudioNodeInputValue(aCounter) : 0.0f);
+  }
+
+private:
+  float AudioNodeInputValue(size_t aCounter) const;
+
+protected:
+  // This is created lazily when needed.
+  nsRefPtr<MediaStream> mStream;
+};
 
 }
 }
 
 #endif
 
--- a/content/media/webaudio/DelayNode.cpp
+++ b/content/media/webaudio/DelayNode.cpp
@@ -180,17 +180,17 @@ public:
         // has a valid value when we try to change the delay time further below.
         mCurrentDelayTime = delayTime;
       }
     } else {
       // Compute the delay values for the duration of the input AudioChunk
       TrackTicks tick = aStream->GetCurrentPosition();
       for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
         computedDelay[counter] = std::max(0.0, std::min(mMaxDelay,
-                                   double(mDelay.GetValueAtTime<TrackTicks>(tick + counter))));
+                                   double(mDelay.GetValueAtTime(tick, counter))));
       }
     }
 
     for (uint32_t channel = 0; channel < numChannels; ++channel) {
       double currentDelayTime = mCurrentDelayTime;
       uint32_t writeIndex = mWriteIndex;
 
       float* buffer = mBuffer[channel].Elements();
--- a/content/media/webaudio/GainNode.cpp
+++ b/content/media/webaudio/GainNode.cpp
@@ -71,18 +71,18 @@ public:
       // First, compute a vector of gains for each track tick based on the
       // timeline at hand, and then for each channel, multiply the values
       // in the buffer with the gain vector.
 
       // Compute the gain values for the duration of the input AudioChunk
       // XXX we need to add a method to AudioEventTimeline to compute this buffer directly.
       float computedGain[WEBAUDIO_BLOCK_SIZE];
       for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
-        TrackTicks tick = aStream->GetCurrentPosition() + counter;
-        computedGain[counter] = mGain.GetValueAtTime<TrackTicks>(tick) * aInput.mVolume;
+        TrackTicks tick = aStream->GetCurrentPosition();
+        computedGain[counter] = mGain.GetValueAtTime(tick, counter) * aInput.mVolume;
       }
 
       // Apply the gain to the output buffer
       for (size_t channel = 0; channel < aOutput->mChannelData.Length(); ++channel) {
         float* buffer = static_cast<float*> (const_cast<void*>
                           (aOutput->mChannelData[channel]));
         AudioBlockCopyChannelWithScale(buffer, computedGain, buffer);
       }
--- a/content/media/webaudio/PannerNode.cpp
+++ b/content/media/webaudio/PannerNode.cpp
@@ -477,19 +477,16 @@ PannerNode::ComputeDopplerShift()
 }
 
 void
 PannerNode::FindConnectedSources()
 {
   mSources.Clear();
   std::set<AudioNode*> cycleSet;
   FindConnectedSources(this, mSources, cycleSet);
-  for (unsigned i = 0; i < mSources.Length(); i++) {
-    mSources[i]->RegisterPannerNode(this);
-  }
 }
 
 void
 PannerNode::FindConnectedSources(AudioNode* aNode,
                                  nsTArray<AudioBufferSourceNode*>& aSources,
                                  std::set<AudioNode*>& aNodesSeen)
 {
   if (!aNode) {
--- a/content/media/webaudio/ScriptProcessorNode.h
+++ b/content/media/webaudio/ScriptProcessorNode.h
@@ -41,16 +41,25 @@ public:
                        uint32_t aInput, ErrorResult& aRv) MOZ_OVERRIDE
   {
     AudioNode::Connect(aDestination, aOutput, aInput, aRv);
     if (!aRv.Failed()) {
       mPlayingRef.Take(this);
     }
   }
 
+  virtual void Connect(AudioParam& aDestination, uint32_t aOutput,
+                       ErrorResult& aRv) MOZ_OVERRIDE
+  {
+    AudioNode::Connect(aDestination, aOutput, aRv);
+    if (!aRv.Failed()) {
+      mPlayingRef.Take(this);
+    }
+  }
+
   virtual void Disconnect(uint32_t aOutput, ErrorResult& aRv) MOZ_OVERRIDE
   {
     AudioNode::Disconnect(aOutput, aRv);
     if (!aRv.Failed()) {
       mPlayingRef.Drop(this);
     }
   }
 
--- a/content/media/webaudio/test/Makefile.in
+++ b/content/media/webaudio/test/Makefile.in
@@ -15,36 +15,40 @@ MOCHITEST_FILES := \
   test_bug808374.html \
   test_bug827541.html \
   test_bug839753.html \
   test_bug845960.html \
   test_bug856771.html \
   test_bug866570.html \
   test_bug866737.html \
   test_bug867089.html \
+  test_bug867203.html \
   test_analyserNode.html \
   test_AudioBuffer.html \
   test_AudioContext.html \
   test_AudioListener.html \
   test_AudioParam.html \
+  test_audioParamExponentialRamp.html \
+  test_audioParamLinearRamp.html \
   test_audioBufferSourceNode.html \
   test_audioBufferSourceNodeLazyLoopParam.html \
   test_audioBufferSourceNodeLoop.html \
   test_audioBufferSourceNodeLoopStartEnd.html \
   test_audioBufferSourceNodeLoopStartEndSame.html \
   test_audioBufferSourceNodeNullBuffer.html \
   test_badConnect.html \
   test_biquadFilterNode.html \
   test_currentTime.html \
   test_delayNode.html \
   test_delayNodeWithGain.html \
   test_decodeAudioData.html \
   test_dynamicsCompressorNode.html \
   test_gainNode.html \
   test_mixingRules.html \
+  test_nodeToParamConnection.html \
   test_pannerNode.html \
   test_scriptProcessorNode.html \
   test_scriptProcessorNodeChannelCount.html \
   test_singleSourceDest.html \
   ting.ogg \
   ting-expected.wav \
   ting-dualchannel44.1.ogg \
   ting-dualchannel44.1-expected.wav \
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_audioParamExponentialRamp.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test AudioParam.exponentialRampToValue</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="webaudio.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+
+  var context = new AudioContext();
+
+  var V0 = 0.1;
+  var V1 = 0.9;
+  var T0 = 0;
+  var T1 = 2048 / context.sampleRate;
+
+  var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
+  for (var i = 0; i < 2048; ++i) {
+    sourceBuffer.getChannelData(0)[i] = 1;
+  }
+  var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
+  for (var i = 0; i < 2048; ++i) {
+    var t = i / context.sampleRate;
+    expectedBuffer.getChannelData(0)[i] = V0 * Math.pow(V1 / V0, (t - T0) / (T1 - T0));
+  }
+
+  var destination = context.destination;
+
+  var source = context.createBufferSource();
+  source.buffer = sourceBuffer;
+
+  var gain = context.createGain();
+  gain.gain.setValueAtTime(V0, 0);
+  gain.gain.exponentialRampToValueAtTime(V1, 2048/context.sampleRate);
+
+  var sp = context.createScriptProcessor(2048, 1);
+  source.connect(gain);
+  gain.connect(sp);
+  sp.connect(destination);
+
+  source.start(0);
+  sp.onaudioprocess = function(e) {
+    is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
+    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
+
+    sp.onaudioprocess = null;
+
+    SpecialPowers.clearUserPref("media.webaudio.enabled");
+    SimpleTest.finish();
+  };
+});
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_audioParamLinearRamp.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test AudioParam.linearRampToValue</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="webaudio.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+
+  var context = new AudioContext();
+
+  var V0 = 0.1;
+  var V1 = 0.9;
+  var T0 = 0;
+  var T1 = 2048 / context.sampleRate;
+
+  var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
+  for (var i = 0; i < 2048; ++i) {
+    sourceBuffer.getChannelData(0)[i] = 1;
+  }
+  var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
+  for (var i = 0; i < 2048; ++i) {
+    var t = i / context.sampleRate;
+    expectedBuffer.getChannelData(0)[i] = V0 + (V1 - V0) * ((t - T0) / (T1 - T0));
+  }
+
+  var destination = context.destination;
+
+  var source = context.createBufferSource();
+  source.buffer = sourceBuffer;
+
+  var gain = context.createGain();
+  gain.gain.setValueAtTime(V0, 0);
+  gain.gain.linearRampToValueAtTime(V1, 2048/context.sampleRate);
+
+  var sp = context.createScriptProcessor(2048, 1);
+  source.connect(gain);
+  gain.connect(sp);
+  sp.connect(destination);
+
+  source.start(0);
+  sp.onaudioprocess = function(e) {
+    is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
+    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
+
+    sp.onaudioprocess = null;
+
+    SpecialPowers.clearUserPref("media.webaudio.enabled");
+    SimpleTest.finish();
+  };
+});
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_bug867203.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Crashtest for bug 867203</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+  var ctx = new AudioContext();
+
+  var panner1 = ctx.createPanner();
+  panner1.setVelocity(1, 1, 1);
+  ctx.listener.setVelocity(1, 1, 1);
+  (function() {
+    ctx.createBufferSource().connect(panner1);
+  })();
+  SpecialPowers.forceGC();
+  SpecialPowers.forceCC();
+  ctx.createPanner();
+
+  ok(true, "We did not crash.");
+  SpecialPowers.clearUserPref("media.webaudio.enabled");
+  SimpleTest.finish();
+});
+
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_nodeToParamConnection.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test connecting an AudioNode to an AudioParam</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="webaudio.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+
+  var context = new AudioContext();
+  var buffer = context.createBuffer(2, 2048, context.sampleRate);
+  for (var i = 0; i < 2048; ++i) {
+    for (var j = 0; j < 2; ++j) {
+      buffer.getChannelData(j)[i] = Math.sin(440 * 2 * (j + 1) * Math.PI * i / context.sampleRate);
+    }
+  }
+  var sourceBuffer = context.createBuffer(2, 2048, context.sampleRate);
+  for (var i = 0; i < 2048; ++i) {
+    sourceBuffer.getChannelData(0)[i] = 1;
+    sourceBuffer.getChannelData(1)[i] = -1;
+  }
+  var expectedBuffer = context.createBuffer(2, 2048, context.sampleRate);
+  for (var i = 0; i < 2048; ++i) {
+    expectedBuffer.getChannelData(0)[i] = 1 + (buffer.getChannelData(0)[i] + buffer.getChannelData(1)[i]) / 2;
+    expectedBuffer.getChannelData(1)[i] = -(1 + (buffer.getChannelData(0)[i] + buffer.getChannelData(1)[i]) / 2);
+  }
+
+  var destination = context.destination;
+
+  var paramSource = context.createBufferSource();
+  paramSource.buffer = buffer;
+
+  var source = context.createBufferSource();
+  source.buffer = sourceBuffer;
+
+  var gain = context.createGain();
+
+  paramSource.connect(gain.gain);
+  source.connect(gain);
+
+  var sp = context.createScriptProcessor(2048);
+  gain.connect(sp);
+  sp.connect(destination);
+
+  paramSource.start(0);
+  source.start(0);
+  sp.onaudioprocess = function(e) {
+    is(e.inputBuffer.numberOfChannels, 2, "Correct input channel count");
+    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
+    compareBuffers(e.inputBuffer.getChannelData(1), expectedBuffer.getChannelData(1));
+
+    sp.onaudioprocess = null;
+
+    SpecialPowers.clearUserPref("media.webaudio.enabled");
+    SimpleTest.finish();
+  };
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/media/webaudio/test/webaudio.js
+++ b/content/media/webaudio/test/webaudio.js
@@ -19,17 +19,17 @@ function expectTypeError(func) {
   } catch (ex) {
     threw = true;
     ok(ex instanceof TypeError, "Expect a TypeError");
   }
   ok(threw, "The exception was thrown");
 }
 
 function fuzzyCompare(a, b) {
-  return Math.abs(a - b) < 5e-5;
+  return Math.abs(a - b) < 9e-3;
 }
 
 function compareBuffers(buf1, buf2,
                         /*optional*/ offset,
                         /*optional*/ length,
                         /*optional*/ sourceOffset,
                         /*optional*/ destOffset) {
   is(buf1.length, buf2.length, "Buffers must have the same length");
--- a/content/media/webspeech/recognition/moz.build
+++ b/content/media/webspeech/recognition/moz.build
@@ -3,18 +3,18 @@
 # 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/.
 
 MODULE = 'content'
 
 XPIDL_MODULE = 'dom_webspeechrecognition'
 
 XPIDL_SOURCES = [
+  'nsIDOMSpeechRecognitionError.idl',
   'nsIDOMSpeechRecognitionEvent.idl',
-  'nsIDOMSpeechRecognitionError.idl',
   'nsISpeechRecognitionService.idl'
 ]
 
 TEST_DIRS += ['test']
 
 EXPORTS.mozilla.dom += [
     'FakeSpeechRecognitionService.h',
     'SpeechGrammar.h',
--- a/content/svg/content/src/moz.build
+++ b/content/svg/content/src/moz.build
@@ -10,27 +10,26 @@ EXPORTS += [
     'SVGAttrValueWrapper.h',
     'SVGStringList.h',
     'nsSVGClass.h',
     'nsSVGElement.h',
     'nsSVGFeatures.h',
 ]
 
 EXPORTS.mozilla.dom += [
-    'nsSVGAnimatedTransformList.h',
     'SVGAElement.h',
     'SVGAltGlyphElement.h',
     'SVGAngle.h',
+    'SVGAnimateElement.h',
+    'SVGAnimateMotionElement.h',
+    'SVGAnimateTransformElement.h',
     'SVGAnimatedAngle.h',
     'SVGAnimatedBoolean.h',
     'SVGAnimatedLength.h',
     'SVGAnimatedTransformList.h',
-    'SVGAnimateElement.h',
-    'SVGAnimateMotionElement.h',
-    'SVGAnimateTransformElement.h',
     'SVGAnimationElement.h',
     'SVGCircleElement.h',
     'SVGClipPathElement.h',
     'SVGComponentTransferFunctionElement.h',
     'SVGDefsElement.h',
     'SVGDescElement.h',
     'SVGEllipseElement.h',
     'SVGFEBlendElement.h',
@@ -53,43 +52,44 @@ EXPORTS.mozilla.dom += [
     'SVGFESpotLightElement.h',
     'SVGFETileElement.h',
     'SVGFETurbulenceElement.h',
     'SVGFilterElement.h',
     'SVGForeignObjectElement.h',
     'SVGGElement.h',
     'SVGGradientElement.h',
     'SVGGraphicsElement.h',
+    'SVGIRect.h',
     'SVGImageElement.h',
-    'SVGIRect.h',
     'SVGLineElement.h',
+    'SVGMPathElement.h',
     'SVGMarkerElement.h',
     'SVGMaskElement.h',
     'SVGMatrix.h',
     'SVGMetadataElement.h',
-    'SVGMPathElement.h',
     'SVGPathElement.h',
     'SVGPatternElement.h',
     'SVGPolygonElement.h',
     'SVGPolylineElement.h',
+    'SVGRect.h',
     'SVGRectElement.h',
-    'SVGRect.h',
+    'SVGSVGElement.h',
     'SVGScriptElement.h',
     'SVGSetElement.h',
     'SVGStopElement.h',
     'SVGStyleElement.h',
-    'SVGSVGElement.h',
     'SVGSwitchElement.h',
     'SVGSymbolElement.h',
+    'SVGTSpanElement.h',
     'SVGTests.h',
     'SVGTextContentElement.h',
     'SVGTextElement.h',
     'SVGTextPathElement.h',
     'SVGTextPositioningElement.h',
     'SVGTitleElement.h',
+    'SVGTransform.h',
     'SVGTransformableElement.h',
-    'SVGTransform.h',
-    'SVGTSpanElement.h',
     'SVGUseElement.h',
     'SVGViewElement.h',
     'SVGZoomEvent.h',
+    'nsSVGAnimatedTransformList.h',
 ]
 
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1058,20 +1058,20 @@ static const JSClass *sObjectClass = nul
 /**
  * Set our JSClass pointer for the Object class
  */
 static void
 FindObjectClass(JSContext* cx, JSObject* aGlobalObject)
 {
   NS_ASSERTION(!sObjectClass,
                "Double set of sObjectClass");
-  JSObject *obj, *proto = aGlobalObject;
+  JS::Rooted<JSObject*> obj(cx), proto(cx, aGlobalObject);
   do {
     obj = proto;
-    js::GetObjectProto(cx, obj, &proto);
+    js::GetObjectProto(cx, obj, proto.address());
   } while (proto);
 
   sObjectClass = js::GetObjectJSClass(obj);
 }
 
 static void
 PrintWarningOnConsole(JSContext *cx, const char *stringBundleProperty)
 {
@@ -1201,50 +1201,50 @@ WrapNative(JSContext *cx, JSObject *scop
                     aAllowWrapping);
 }
 
 // Used for cases where PreCreate needs to wrap the native parent, and the
 // native parent is likely to have been wrapped already.  |native| must
 // implement nsWrapperCache, and nativeWrapperCache must be |native|'s
 // nsWrapperCache.
 static inline nsresult
-WrapNativeParent(JSContext *cx, JSObject *scope, nsISupports *native,
+WrapNativeParent(JSContext *cx, JS::Handle<JSObject*> scope, nsISupports *native,
                  nsWrapperCache *nativeWrapperCache, JSObject **parentObj)
 {
   // In the common case, |native| is a wrapper cache with an existing wrapper
 #ifdef DEBUG
   nsWrapperCache* cache = nullptr;
   CallQueryInterface(native, &cache);
   NS_PRECONDITION(nativeWrapperCache &&
                   cache == nativeWrapperCache, "What happened here?");
 #endif
 
-  JSObject* obj = nativeWrapperCache->GetWrapper();
+  JS::Rooted<JSObject*> obj(cx, nativeWrapperCache->GetWrapper());
   if (obj) {
 #ifdef DEBUG
     jsval debugVal;
     nsresult rv = WrapNative(cx, scope, native, nativeWrapperCache, false,
                              &debugVal);
     NS_ASSERTION(NS_SUCCEEDED(rv) && JSVAL_TO_OBJECT(debugVal) == obj,
                  "Unexpected object in nsWrapperCache");
 #endif
     *parentObj = obj;
     return NS_OK;
   }
 
-  jsval v;
-  nsresult rv = WrapNative(cx, scope, native, nativeWrapperCache, false, &v);
+  JS::Rooted<JS::Value> v(cx);
+  nsresult rv = WrapNative(cx, scope, native, nativeWrapperCache, false, v.address());
   NS_ENSURE_SUCCESS(rv, rv);
-  *parentObj = JSVAL_TO_OBJECT(v);
+  *parentObj = v.toObjectOrNull();
   return NS_OK;
 }
 
 template<class P>
 static inline nsresult
-WrapNativeParent(JSContext *cx, JSObject *scope, P *parent,
+WrapNativeParent(JSContext *cx, JS::Handle<JSObject*> scope, P *parent,
                  JSObject **parentObj)
 {
   return WrapNativeParent(cx, scope, ToSupports(parent), parent, parentObj);
 }
 
 // Helper to handle torn-down inner windows.
 static inline nsresult
 SetParentToWindow(nsGlobalWindow *win, JSObject **parent)
@@ -2653,21 +2653,21 @@ nsDOMClassInfo::NewEnumerate(nsIXPConnec
 
   return NS_ERROR_UNEXPECTED;
 }
 
 nsresult
 nsDOMClassInfo::ResolveConstructor(JSContext *cx, JSObject *obj,
                                    JSObject **objp)
 {
-  JSObject *global = ::JS_GetGlobalForObject(cx, obj);
-
-  jsval val;
+  JS::Rooted<JSObject*> global(cx, ::JS_GetGlobalForObject(cx, obj));
+
+  JS::Rooted<JS::Value> val(cx);
   JSAutoRequest ar(cx);
-  if (!::JS_LookupProperty(cx, global, mData->mName, &val)) {
+  if (!::JS_LookupProperty(cx, global, mData->mName, val.address())) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!JSVAL_IS_PRIMITIVE(val)) {
     // If val is not an (non-null) object there either is no
     // constructor for this class, or someone messed with
     // window.classname, just fall through and let the JS engine
     // return the Object constructor.
@@ -2726,19 +2726,19 @@ NS_IMETHODIMP
 nsDOMClassInfo::CheckAccess(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                             JSObject *obj, jsid id, uint32_t mode,
                             jsval *vp, bool *_retval)
 {
   uint32_t mode_type = mode & JSACC_TYPEMASK;
 
   if ((mode_type == JSACC_WATCH || mode_type == JSACC_PROTO) && sSecMan) {
     nsresult rv;
-    JSObject *real_obj;
+    JS::Rooted<JSObject*> real_obj(cx);
     if (wrapper) {
-      rv = wrapper->GetJSObject(&real_obj);
+      rv = wrapper->GetJSObject(real_obj.address());
       NS_ENSURE_SUCCESS(rv, rv);
     }
     else {
       real_obj = obj;
     }
 
     rv =
       sSecMan->CheckPropertyAccess(cx, real_obj, mData->mName, id,
@@ -2832,27 +2832,28 @@ ResolvePrototype(nsIXPConnect *aXPConnec
                  JSObject *obj, const PRUnichar *name,
                  const nsDOMClassInfoData *ci_data,
                  const nsGlobalNameStruct *name_struct,
                  nsScriptNameSpaceManager *nameSpaceManager,
                  JSObject *dot_prototype, bool install, bool *did_resolve);
 
 
 NS_IMETHODIMP
-nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * proto)
+nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * aProto)
 {
   uint32_t flags = (mData->mScriptableFlags & DONT_ENUM_STATIC_PROPS)
                    ? 0
                    : JSPROP_ENUMERATE;
 
   uint32_t count = 0;
   while (mData->mInterfaces[count]) {
     count++;
   }
 
+  JS::Rooted<JSObject*> proto(cx, aProto);
   if (!xpc::DOM_DefineQuickStubs(cx, proto, flags, count, mData->mInterfaces)) {
     JS_ClearPendingException(cx);
   }
 
   // This is called before any other location that requires
   // sObjectClass, so compute it here. We assume that nobody has had a
   // chance to monkey around with proto's prototype chain before this.
   if (!sObjectClass) {
@@ -2888,17 +2889,17 @@ nsDOMClassInfo::PostCreatePrototype(JSCo
     }
   }
 #endif
 
   // Make prototype delegation work correctly. Consider if a site sets
   // HTMLElement.prototype.foopy = function () { ... } Now, calling
   // document.body.foopy() needs to ensure that looking up foopy on
   // document.body's prototype will find the right function.
-  JSObject *global = ::JS_GetGlobalForObject(cx, proto);
+  JS::Rooted<JSObject*> global(cx, ::JS_GetGlobalForObject(cx, proto));
 
   // Only do this if the global object is a window.
   // XXX Is there a better way to check this?
   nsISupports *globalNative = XPConnect()->GetNativeOfWrapper(cx, global);
   nsCOMPtr<nsPIDOMWindow> piwin = do_QueryInterface(globalNative);
   if (!piwin) {
     return NS_OK;
   }
@@ -3136,19 +3137,19 @@ ChildWindowGetter(JSContext *cx, JSHandl
   nsGlobalWindow *win = nsGlobalWindow::FromSupports(winSupports);
 
   // Find the child, if it exists.
   nsCOMPtr<nsIDOMWindow> child = win->GetChildWindow(id);
   if (!child)
     return true;
 
   // Wrap the child for JS.
-  jsval v;
+  JS::Rooted<JS::Value> v(cx);
   nsresult rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), child,
-                           /* aAllowWrapping = */ true, &v);
+                           /* aAllowWrapping = */ true, v.address());
   NS_ENSURE_SUCCESS(rv, false);
   vp.set(v);
   return true;
 }
 
 static nsHTMLDocument*
 GetDocument(JSObject *obj)
 {
@@ -3193,18 +3194,18 @@ nsWindowSH::GlobalScopePolluterNewResolv
         return false;
       }
 
       objp.set(obj);
       return true;
     }
   }
 
-  JSObject *proto;
-  if (!::JS_GetPrototype(cx, obj, &proto)) {
+  JS::Rooted<JSObject*> proto(cx);
+  if (!::JS_GetPrototype(cx, obj, proto.address())) {
     return JS_FALSE;
   }
   JSBool hasProp;
 
   if (!proto || !::JS_HasPropertyById(cx, proto, id, &hasProp) ||
       hasProp) {
     // No prototype, or the property exists on the prototype. Do
     // nothing.
@@ -3230,53 +3231,53 @@ nsWindowSH::GlobalScopePolluterNewResolv
     cache = element;
   }
 
   if (!result) {
     result = document->ResolveName(str, &cache);
   }
 
   if (result) {
-    jsval v;
+    JS::Rooted<JS::Value> v(cx);
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-    nsresult rv = WrapNative(cx, obj, result, cache, true, &v,
+    nsresult rv = WrapNative(cx, obj, result, cache, true, v.address(),
                              getter_AddRefs(holder));
     NS_ENSURE_SUCCESS(rv, JS_FALSE);
 
-    if (!JS_WrapValue(cx, &v) ||
+    if (!JS_WrapValue(cx, v.address()) ||
         !JS_DefinePropertyById(cx, obj, id, v, JS_PropertyStub, JS_StrictPropertyStub, 0)) {
       return JS_FALSE;
     }
 
     objp.set(obj);
   }
 
   return JS_TRUE;
 }
 
 // static
 JSBool
 nsWindowSH::InvalidateGlobalScopePolluter(JSContext *cx, JSObject *obj)
 {
-  JSObject *proto;
+  JS::Rooted<JSObject*> proto(cx);
 
   JSAutoRequest ar(cx);
 
   for (;;) {
-    if (!::JS_GetPrototype(cx, obj, &proto)) {
+    if (!::JS_GetPrototype(cx, obj, proto.address())) {
       return JS_FALSE;
     }
     if (!proto) {
       break;
     }
 
     if (JS_GetClass(proto) == &sGlobalScopePolluterClass) {
 
-      JSObject *proto_proto;
-      if (!::JS_GetPrototype(cx, proto, &proto_proto)) {
+      JS::Rooted<JSObject*> proto_proto(cx);
+      if (!::JS_GetPrototype(cx, proto, proto_proto.address())) {
         return JS_FALSE;
       }
 
       // Pull the global scope polluter out of the prototype chain so
       // that it can be freed.
       ::JS_SplicePrototype(cx, obj, proto_proto);
 
       break;
@@ -3285,32 +3286,32 @@ nsWindowSH::InvalidateGlobalScopePollute
     obj = proto;
   }
 
   return JS_TRUE;
 }
 
 // static
 nsresult
-nsWindowSH::InstallGlobalScopePolluter(JSContext *cx, JSObject *obj)
+nsWindowSH::InstallGlobalScopePolluter(JSContext *cx, JS::Handle<JSObject*> obj)
 {
   JSAutoRequest ar(cx);
 
-  JSObject *gsp = ::JS_NewObjectWithUniqueType(cx, &sGlobalScopePolluterClass, nullptr, obj);
+  JS::Rooted<JSObject*> gsp(cx, ::JS_NewObjectWithUniqueType(cx, &sGlobalScopePolluterClass, nullptr, obj));
   if (!gsp) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  JSObject *o = obj, *proto;
+  JS::Rooted<JSObject*> o(cx, obj), proto(cx);
 
   // Find the place in the prototype chain where we want this global
   // scope polluter (right before Object.prototype).
 
   for (;;) {
-    if (!::JS_GetPrototype(cx, o, &proto)) {
+    if (!::JS_GetPrototype(cx, o, proto.address())) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
     if (!proto) {
       break;
     }
     if (JS_GetClass(proto) == sObjectClass) {
       // Set the global scope polluters prototype to Object.prototype
       ::JS_SplicePrototype(cx, gsp, proto);
@@ -3348,18 +3349,19 @@ ResolveGlobalName(const nsAString& aName
     *closure->retval = false;
     return PL_DHASH_STOP;
   }
   return PL_DHASH_NEXT;
 }
 
 NS_IMETHODIMP
 nsWindowSH::Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                      JSObject *obj, bool *_retval)
-{
+                      JSObject *aObj, bool *_retval)
+{
+  JS::Rooted<JSObject*> obj(cx, aObj);
   if (!xpc::WrapperFactory::IsXrayWrapper(obj)) {
     *_retval = JS_EnumerateStandardClasses(cx, obj);
     if (!*_retval) {
       return NS_OK;
     }
 
     // Now resolve everything from the namespace manager
     nsScriptNameSpaceManager *nameSpaceManager =
@@ -3385,18 +3387,19 @@ FindConstructorFunc(const nsDOMClassInfo
     }
   }
   return nullptr;
 }
 
 static nsresult
 BaseStubConstructor(nsIWeakReference* aWeakOwner,
                     const nsGlobalNameStruct *name_struct, JSContext *cx,
-                    JSObject *obj, unsigned argc, jsval *argv, jsval *rval)
-{
+                    JSObject *aObj, unsigned argc, jsval *argv, jsval *rval)
+{
+  JS::Rooted<JSObject*> obj(cx, aObj);
   MOZ_ASSERT(obj);
 
   nsresult rv;
   nsCOMPtr<nsISupports> native;
   if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
     const nsDOMClassInfoData* ci_data =
       &sClassInfoData[name_struct->mDOMClassInfoID];
     nsDOMConstructorFunc func = FindConstructorFunc(ci_data);
@@ -3435,40 +3438,40 @@ BaseStubConstructor(nsIWeakReference* aW
     if (initializer) {
       rv = initializer->Initialize(currentInner, cx, obj, argc, argv);
       if (NS_FAILED(rv)) {
         return rv;
       }
     } else {
       nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = do_QueryInterface(native);
 
-      JSObject* object = nullptr;
-      wrappedJS->GetJSObject(&object);
+      JS::Rooted<JSObject*> object(cx);
+      wrappedJS->GetJSObject(object.address());
       if (!object) {
         return NS_ERROR_UNEXPECTED;
       }
 
       nsCxPusher pusher;
       pusher.Push(cx);
 
       JSAutoRequest ar(cx);
       JSAutoCompartment ac(cx, object);
 
-      JS::Value thisValue = JSVAL_VOID;
-      JS::Value funval;
-      if (!JS_GetProperty(cx, object, "constructor", &funval) || !funval.isObject()) {
+      JS::Rooted<JS::Value> thisValue(cx, JS::UndefinedValue());
+      JS::Rooted<JS::Value> funval(cx);
+      if (!JS_GetProperty(cx, object, "constructor", funval.address()) || !funval.isObject()) {
         return NS_ERROR_UNEXPECTED;
       }
 
       // Check if the object is even callable.
       NS_ENSURE_STATE(JS_ObjectIsCallable(cx, &funval.toObject()));
       thisValue.setObject(*object);
 
       {
-        JSObject* thisObject = &thisValue.toObject();
+        JS::Rooted<JSObject*> thisObject(cx, &thisValue.toObject());
 
         // wrap parameters in the target compartment
         // we also pass in the calling window as the first argument
         ++argc;
         nsAutoArrayPtr<JS::Value> args(new JS::Value[argc]);
         JS::AutoArrayRooter rooter(cx, 0, args);
 
         nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
@@ -3495,17 +3498,17 @@ BaseStubConstructor(nsIWeakReference* aW
       }
     }
   }
 
   return WrapNative(cx, obj, native, true, rval);
 }
 
 static nsresult
-DefineInterfaceConstants(JSContext *cx, JSObject *obj, const nsIID *aIID)
+DefineInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj, const nsIID *aIID)
 {
   nsCOMPtr<nsIInterfaceInfoManager>
     iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID));
   NS_ENSURE_TRUE(iim, NS_ERROR_UNEXPECTED);
 
   nsCOMPtr<nsIInterfaceInfo> if_info;
 
   nsresult rv = iim->GetInfoForIID(aIID, getter_AddRefs(if_info));
@@ -3762,17 +3765,17 @@ public:
       ::JS_DefineUCProperty(cx, target,
                             reinterpret_cast<const jschar *>(mClassName),
                             NS_strlen(mClassName), thisAsVal, JS_PropertyStub,
                             JS_StrictPropertyStub, 0);
 
     return ok ? NS_OK : NS_ERROR_UNEXPECTED;
   }
 
-  nsresult ResolveInterfaceConstants(JSContext *cx, JSObject *obj);
+  nsresult ResolveInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj);
 
 private:
   const nsGlobalNameStruct *GetNameStruct()
   {
     if (!mClassName) {
       NS_ERROR("Can't get name");
       return nullptr;
     }
@@ -3929,17 +3932,17 @@ nsDOMConstructor::HasInstance(nsIXPConne
 
 {
   // No need to look these up in the hash.
   *bp = false;
   if (JSVAL_IS_PRIMITIVE(v)) {
     return NS_OK;
   }
 
-  JSObject *dom_obj = JSVAL_TO_OBJECT(v);
+  JS::Rooted<JSObject*> dom_obj(cx, v.toObjectOrNull());
   NS_ASSERTION(dom_obj, "nsDOMConstructor::HasInstance couldn't get object");
 
   // This might not be the right object, if there are wrappers. Unwrap if we can.
   JSObject *wrapped_obj = js::CheckedUnwrap(dom_obj, /* stopAtOuter = */ false);
   if (wrapped_obj)
       dom_obj = wrapped_obj;
 
   JSClass *dom_class = JS_GetClass(dom_obj);
@@ -3952,30 +3955,30 @@ nsDOMConstructor::HasInstance(nsIXPConne
   nsresult rv = GetNameStruct(NS_ConvertASCIItoUTF16(dom_class->name), &name_struct);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (!name_struct) {
     // This isn't a normal DOM object, see if this constructor lives on its
     // prototype chain.
-    jsval val;
-    if (!JS_GetProperty(cx, obj, "prototype", &val)) {
+    JS::Rooted<JS::Value> val(cx);
+    if (!JS_GetProperty(cx, obj, "prototype", val.address())) {
       return NS_ERROR_UNEXPECTED;
     }
 
     if (JSVAL_IS_PRIMITIVE(val)) {
       return NS_OK;
     }
 
-    JSObject *dot_prototype = JSVAL_TO_OBJECT(val);
-
-    JSObject *proto = dom_obj;
+    JS::Rooted<JSObject*> dot_prototype(cx, val.toObjectOrNull());
+
+    JS::Rooted<JSObject*> proto(cx, dom_obj);
     for (;;) {
-      if (!JS_GetPrototype(cx, proto, &proto)) {
+      if (!JS_GetPrototype(cx, proto, proto.address())) {
         return NS_ERROR_UNEXPECTED;
       }
       if (!proto) {
         break;
       }
       if (proto == dot_prototype) {
         *bp = true;
         break;
@@ -4086,17 +4089,17 @@ nsDOMConstructor::HasInstance(nsIXPConne
       return NS_OK;
     }
   }
 
   return NS_OK;
 }
 
 nsresult
-nsDOMConstructor::ResolveInterfaceConstants(JSContext *cx, JSObject *obj)
+nsDOMConstructor::ResolveInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj)
 {
   const nsGlobalNameStruct *class_name_struct = GetNameStruct();
   if (!class_name_struct)
     return NS_ERROR_UNEXPECTED;
 
   const nsIID *class_iid;
   if (class_name_struct->mType == nsGlobalNameStruct::eTypeInterface ||
       class_name_struct->mType == nsGlobalNameStruct::eTypeClassProto) {
@@ -4183,19 +4186,19 @@ GetXPCProto(nsIXPConnect *aXPConnect, JS
   }
   NS_ENSURE_TRUE(ci, NS_ERROR_UNEXPECTED);
 
   nsresult rv =
     aXPConnect->GetWrappedNativePrototype(cx, aWin->GetGlobalJSObject(), ci,
                                           aProto);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  JSObject *proto_obj;
-  (*aProto)->GetJSObject(&proto_obj);
-  if (!JS_WrapObject(cx, &proto_obj)) {
+  JS::Rooted<JSObject*> proto_obj(cx);
+  (*aProto)->GetJSObject(proto_obj.address());
+  if (!JS_WrapObject(cx, proto_obj.address())) {
     return NS_ERROR_FAILURE;
   }
 
   NS_IF_RELEASE(*aProto);
   return aXPConnect->HoldObject(cx, proto_obj, aProto);
 }
 
 // Either ci_data must be non-null or name_struct must be non-null and of type
@@ -4214,29 +4217,29 @@ ResolvePrototype(nsIXPConnect *aXPConnec
                "Wrong type or missing ci_data!");
 
   nsRefPtr<nsDOMConstructor> constructor;
   nsresult rv = nsDOMConstructor::Create(name, ci_data, name_struct, aWin,
                                          getter_AddRefs(constructor));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-  jsval v;
+  JS::Rooted<JS::Value> v(cx);
 
   rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
-                  false, &v, getter_AddRefs(holder));
+                  false, v.address(), getter_AddRefs(holder));
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (install) {
     rv = constructor->Install(cx, obj, v);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  JSObject *class_obj;
-  holder->GetJSObject(&class_obj);
+  JS::Rooted<JSObject*> class_obj(cx);
+  holder->GetJSObject(class_obj.address());
   NS_ASSERTION(class_obj, "The return value lied");
 
   const nsIID *primary_iid = &NS_GET_IID(nsISupports);
 
   if (!ci_data) {
     primary_iid = &name_struct->mIID;
   }
   else if (ci_data->mProtoChainInterface) {
@@ -4304,50 +4307,50 @@ ResolvePrototype(nsIXPConnect *aXPConnec
 
           parent->GetNameShared(&class_parent_name);
         }
       }
     }
   }
 
   {
-    JSObject *winobj = aWin->FastGetGlobalJSObject();
-
-    JSObject *proto = nullptr;
+    JS::Rooted<JSObject*> winobj(cx, aWin->FastGetGlobalJSObject());
+
+    JS::Rooted<JSObject*> proto(cx);
 
     if (class_parent_name) {
       JSAutoCompartment ac(cx, winobj);
 
-      JS::Value val;
-      if (!JS_LookupProperty(cx, winobj, CutPrefix(class_parent_name), &val)) {
+      JS::Rooted<JS::Value> val(cx);
+      if (!JS_LookupProperty(cx, winobj, CutPrefix(class_parent_name), val.address())) {
         return NS_ERROR_UNEXPECTED;
       }
 
       if (val.isObject()) {
-        if (!JS_LookupProperty(cx, &val.toObject(), "prototype", &val)) {
+        if (!JS_LookupProperty(cx, &val.toObject(), "prototype", val.address())) {
           return NS_ERROR_UNEXPECTED;
         }
 
         if (val.isObject()) {
           proto = &val.toObject();
         }
       }
     }
 
     if (dot_prototype) {
       JSAutoCompartment ac(cx, dot_prototype);
-      JSObject *xpc_proto_proto;
-      if (!::JS_GetPrototype(cx, dot_prototype, &xpc_proto_proto)) {
+      JS::Rooted<JSObject*> xpc_proto_proto(cx);
+      if (!::JS_GetPrototype(cx, dot_prototype, xpc_proto_proto.address())) {
         return NS_ERROR_UNEXPECTED;
       }
 
       if (proto &&
           (!xpc_proto_proto ||
            JS_GetClass(xpc_proto_proto) == sObjectClass)) {
-        if (!JS_WrapObject(cx, &proto) ||
+        if (!JS_WrapObject(cx, proto.address()) ||
             !JS_SetPrototype(cx, dot_prototype, proto)) {
           return NS_ERROR_UNEXPECTED;
         }
       }
     } else {
       JSAutoCompartment ac(cx, winobj);
       if (!proto) {
         proto = JS_GetObjectPrototype(cx, winobj);
@@ -4360,17 +4363,17 @@ ResolvePrototype(nsIXPConnect *aXPConnec
     }
   }
 
   v = OBJECT_TO_JSVAL(dot_prototype);
 
   JSAutoCompartment ac(cx, class_obj);
 
   // Per ECMA, the prototype property is {DontEnum, DontDelete, ReadOnly}
-  if (!JS_WrapValue(cx, &v) ||
+  if (!JS_WrapValue(cx, v.address()) ||
       !JS_DefineProperty(cx, class_obj, "prototype", v,
                          JS_PropertyStub, JS_StrictPropertyStub,
                          JSPROP_PERMANENT | JSPROP_READONLY)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   *did_resolve = true;
 
@@ -4458,26 +4461,26 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
           return NS_ERROR_DOM_SECURITY_ERR;
         }
         ac.construct(cx, global);
       } else {
         global = obj;
       }
 
       bool enabled;
-      JSObject* interfaceObject = define(cx, global, id, &enabled);
+      JS::Rooted<JSObject*> interfaceObject(cx, define(cx, global, id, &enabled));
       if (enabled) {
         if (!interfaceObject) {
           return NS_ERROR_FAILURE;
         }
 
         if (defineOnXray) {
           // This really should be handled by the Xray for the window.
           ac.destroy();
-          if (!JS_WrapObject(cx, &interfaceObject) ||
+          if (!JS_WrapObject(cx, interfaceObject.address()) ||
               !JS_DefinePropertyById(cx, obj, id,
                                      JS::ObjectValue(*interfaceObject), JS_PropertyStub,
                                      JS_StrictPropertyStub, 0)) {
             return NS_ERROR_FAILURE;
           }
         }
 
         *did_resolve = true;
@@ -4494,26 +4497,26 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
     rv = nsDOMConstructor::Create(class_name,
                                   nullptr,
                                   name_struct,
                                   static_cast<nsPIDOMWindow*>(aWin),
                                   getter_AddRefs(constructor));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-    jsval v;
+    JS::Rooted<JS::Value> v(cx);
     rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
-                    false, &v, getter_AddRefs(holder));
+                    false, v.address(), getter_AddRefs(holder));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = constructor->Install(cx, obj, v);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    JSObject *class_obj;
-    holder->GetJSObject(&class_obj);
+    JS::Rooted<JSObject*> class_obj(cx);
+    holder->GetJSObject(class_obj.address());
     NS_ASSERTION(class_obj, "The return value lied");
 
     // ... and define the constants from the DOM interface on that
     // constructor object.
 
     JSAutoCompartment ac(cx, class_obj);
     rv = DefineInterfaceConstants(cx, class_obj, &name_struct->mIID);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -4531,18 +4534,18 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
 
     // Create the XPConnect prototype for our classinfo, PostCreateProto will
     // set up the prototype chain.
     nsCOMPtr<nsIXPConnectJSObjectHolder> proto_holder;
     rv = GetXPCProto(sXPConnect, cx, aWin, name_struct,
                      getter_AddRefs(proto_holder));
 
     if (NS_SUCCEEDED(rv) && obj != aWin->GetGlobalJSObject()) {
-      JSObject* dot_prototype;
-      rv = proto_holder->GetJSObject(&dot_prototype);
+      JS::Rooted<JSObject*> dot_prototype(cx);
+      rv = proto_holder->GetJSObject(dot_prototype.address());
       NS_ENSURE_SUCCESS(rv, rv);
 
       const nsDOMClassInfoData *ci_data;
       if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
         ci_data = &sClassInfoData[name_struct->mDOMClassInfoID];
       } else {
         ci_data = name_struct->mData;
       }
@@ -4598,20 +4601,20 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
 
   if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructor) {
     nsRefPtr<nsDOMConstructor> constructor;
     rv = nsDOMConstructor::Create(class_name, nullptr, name_struct,
                                   static_cast<nsPIDOMWindow*>(aWin),
                                   getter_AddRefs(constructor));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    jsval val;
+    JS::Rooted<JS::Value> val(cx);
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
-                    false, &val, getter_AddRefs(holder));
+                    false, val.address(), getter_AddRefs(holder));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = constructor->Install(cx, obj, val);
     NS_ENSURE_SUCCESS(rv, rv);
 
     JSObject* class_obj;
     holder->GetJSObject(&class_obj);
     NS_ASSERTION(class_obj, "Why didn't we get a JSObject?");
@@ -4623,44 +4626,44 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
 
   if (name_struct->mType == nsGlobalNameStruct::eTypeProperty) {
     if (name_struct->mChromeOnly && !nsContentUtils::IsCallerChrome())
       return NS_OK;
 
     nsCOMPtr<nsISupports> native(do_CreateInstance(name_struct->mCID, &rv));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    jsval prop_val = JSVAL_VOID; // Property value.
+    JS::Rooted<JS::Value> prop_val(cx, JS::UndefinedValue()); // Property value.
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi(do_QueryInterface(native));
     if (gpi) {
-      rv = gpi->Init(aWin, &prop_val);
+      rv = gpi->Init(aWin, prop_val.address());
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     if (JSVAL_IS_PRIMITIVE(prop_val) && !JSVAL_IS_NULL(prop_val)) {
       JSObject *scope;
 
       if (aWin->IsOuterWindow()) {
         nsGlobalWindow *inner = aWin->GetCurrentInnerWindowInternal();
         NS_ENSURE_TRUE(inner, NS_ERROR_UNEXPECTED);
 
         scope = inner->GetGlobalJSObject();
       } else {
         scope = aWin->GetGlobalJSObject();
       }
 
-      rv = WrapNative(cx, scope, native, true, &prop_val,
+      rv = WrapNative(cx, scope, native, true, prop_val.address(),
                       getter_AddRefs(holder));
     }
 
     NS_ENSURE_SUCCESS(rv, rv);
 
-    if (!JS_WrapValue(cx, &prop_val)) {
+    if (!JS_WrapValue(cx, prop_val.address())) {
       return NS_ERROR_UNEXPECTED;
     }
 
     JSBool ok = ::JS_DefinePropertyById(cx, obj, id, prop_val,
                                         JS_PropertyStub, JS_StrictPropertyStub,
                                         JSPROP_ENUMERATE);
 
     *did_resolve = true;
@@ -4712,17 +4715,17 @@ LocationSetterGuts(JSContext *cx, JSObje
   nsCOMPtr<Interface> xpcomObj = do_QueryWrappedNative(wrapper, obj);
   NS_ENSURE_TRUE(xpcomObj, NS_ERROR_UNEXPECTED);
 
   nsCOMPtr<nsIDOMLocation> location;
   nsresult rv = xpcomObj->GetLocation(getter_AddRefs(location));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Grab the value we're being set to before we stomp on |vp|
-  JSString *val = ::JS_ValueToString(cx, *vp);
+  JS::Rooted<JSString*> val(cx, ::JS_ValueToString(cx, *vp));
   NS_ENSURE_TRUE(val, NS_ERROR_UNEXPECTED);
 
   // Make sure |val| stays alive below
   JS::Anchor<JSString *> anchor(val);
 
   // We have to wrap location into vp before null-checking location, to
   // avoid assigning the wrong thing into the slot.
   nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
@@ -4816,41 +4819,41 @@ const InterfaceShimEntry kInterfaceShimM
 
 static nsresult
 DefineComponentsShim(JSContext *cx, JS::HandleObject global)
 {
   // Keep track of how often this happens.
   Telemetry::Accumulate(Telemetry::COMPONENTS_SHIM_ACCESSED_BY_CONTENT, true);
 
   // Create a fake Components object.
-  JSObject *components = JS_NewObject(cx, nullptr, nullptr, global);
+  JS::Rooted<JSObject*> components(cx, JS_NewObject(cx, nullptr, nullptr, global));
   NS_ENSURE_TRUE(components, NS_ERROR_OUT_OF_MEMORY);
   bool ok = JS_DefineProperty(cx, global, "Components", JS::ObjectValue(*components),
                               JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
   NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
 
   // Create a fake interfaces object.
-  JSObject *interfaces = JS_NewObject(cx, nullptr, nullptr, global);
+  JS::Rooted<JSObject*> interfaces(cx, JS_NewObject(cx, nullptr, nullptr, global));
   NS_ENSURE_TRUE(interfaces, NS_ERROR_OUT_OF_MEMORY);
   ok = JS_DefineProperty(cx, components, "interfaces", JS::ObjectValue(*interfaces),
                          JS_PropertyStub, JS_StrictPropertyStub,
                          JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
   NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
 
   // Define a bunch of shims from the Ci.nsIDOMFoo to window.Foo for DOM
   // interfaces with constants.
   for (uint32_t i = 0; i < ArrayLength(kInterfaceShimMap); ++i) {
 
     // Grab the names from the table.
     const char *geckoName = kInterfaceShimMap[i].geckoName;
     const char *domName = kInterfaceShimMap[i].domName;
 
     // Look up the appopriate interface object on the global.
-    JS::Value v = JS::UndefinedValue();
-    ok = JS_GetProperty(cx, global, domName, &v);
+    JS::Rooted<JS::Value> v(cx, JS::UndefinedValue());
+    ok = JS_GetProperty(cx, global, domName, v.address());
     NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
     if (!v.isObject()) {
       NS_WARNING("Unable to find interface object on global");
       continue;
     }
 
     // Define the shim on the interfaces object.
     ok = JS_DefineProperty(cx, interfaces, geckoName, v,
@@ -4885,17 +4888,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
 
   nsIScriptContext *my_context = win->GetContextInternal();
 
   // Don't resolve standard classes on XrayWrappers, only resolve them if we're
   // resolving on the real global object.
   if (!xpc::WrapperFactory::IsXrayWrapper(obj)) {
     JSBool did_resolve = JS_FALSE;
     JSBool ok = JS_TRUE;
-    JS::Value exn = JSVAL_VOID;
+    JS::Rooted<JS::Value> exn(cx, JSVAL_VOID);
 
     {
       // Resolve standard classes on my_context's JSContext (or on cx,
       // if we don't have a my_context yet), in case the two contexts
       // have different origins.  We want lazy standard class
       // initialization to behave as if it were done eagerly, on each
       // window's own context (not on some other window-caller's
       // context).
@@ -4904,17 +4907,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
       JSAutoCompartment ac(my_cx, obj);
 
       ok = JS_ResolveStandardClass(my_cx, obj, id, &did_resolve);
 
       if (!ok) {
         // Trust the JS engine (or the script security manager) to set
         // the exception in the JS engine.
 
-        if (!JS_GetPendingException(my_cx, &exn)) {
+        if (!JS_GetPendingException(my_cx, exn.address())) {
           return NS_ERROR_UNEXPECTED;
         }
 
         // Return NS_OK to avoid stomping over the exception that was passed
         // down from the ResolveStandardClass call.
         // Note that the order of the JS_ClearPendingException and
         // JS_SetPendingException is important in the case that my_cx == cx.
 
@@ -4954,26 +4957,26 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
     // here) since we must define window.location to prevent the
     // getter from being overriden (for security reasons).
 
     nsCOMPtr<nsIDOMLocation> location;
     nsresult rv = win->GetLocation(getter_AddRefs(location));
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Make sure we wrap the location object in the window's scope.
-    JSObject *scope = nullptr;
-    wrapper->GetJSObject(&scope);
+    JS::Rooted<JSObject*> scope(cx);
+    wrapper->GetJSObject(scope.address());
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-    jsval v;
+    JS::Rooted<JS::Value> v(cx);
     rv = WrapNative(cx, scope, location, &NS_GET_IID(nsIDOMLocation), true,
-                    &v, getter_AddRefs(holder));
+                    v.address(), getter_AddRefs(holder));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    JSBool ok = JS_WrapValue(cx, &v) &&
+    JSBool ok = JS_WrapValue(cx, v.address()) &&
                 JS_DefinePropertyById(cx, obj, id, v, JS_PropertyStub,
                                       LocationSetterUnwrapper,
                                       JSPROP_PERMANENT | JSPROP_ENUMERATE);
 
     if (!ok) {
       return NS_ERROR_FAILURE;
     }
 
@@ -4982,20 +4985,20 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
     return NS_OK;
   }
 
   if (sTop_id == id) {
     nsCOMPtr<nsIDOMWindow> top;
     nsresult rv = win->GetScriptableTop(getter_AddRefs(top));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    jsval v;
+    JS::Rooted<JS::Value> v(cx);
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     rv = WrapNative(cx, obj, top, &NS_GET_IID(nsIDOMWindow), true,
-                    &v, getter_AddRefs(holder));
+                    v.address(), getter_AddRefs(holder));
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Hold on to the top window object as a global property so we
     // don't need to worry about losing expando properties etc.
     if (!JS_DefinePropertyById(cx, obj, id, v, JS_PropertyStub, JS_StrictPropertyStub,
                                JSPROP_READONLY | JSPROP_PERMANENT |
                                JSPROP_ENUMERATE)) {
       return NS_ERROR_FAILURE;
@@ -5029,20 +5032,20 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
     return NS_OK;
   }
 
   if (s_content_id == id) {
     // Map window._content to window.content for backwards
     // compatibility, this should spit out an message on the JS
     // console.
 
-    JSObject* windowObj = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
+    JS::Rooted<JSObject*> windowObj(cx, js::CheckedUnwrap(obj, /* stopAtOuter = */ false));
     NS_ENSURE_TRUE(windowObj, NS_ERROR_DOM_SECURITY_ERR);
 
-    JSObject *funObj;
+    JSObject* funObj = nullptr;
     {
       JSAutoCompartment ac(cx, windowObj);
       JSFunction *fun = ::JS_NewFunction(cx, ContentWindowGetter, 0, 0,
                                          windowObj, "_content");
       if (!fun) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
       funObj = ::JS_GetFunctionObject(fun);
@@ -5079,20 +5082,20 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
       return NS_OK;
     }
   } else {
     if (sNavigator_id == id) {
       nsCOMPtr<nsIDOMNavigator> navigator;
       rv = win->GetNavigator(getter_AddRefs(navigator));
       NS_ENSURE_SUCCESS(rv, rv);
 
-      jsval v;
+      JS::Rooted<JS::Value> v(cx);
       nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
       rv = WrapNative(cx, obj, navigator, &NS_GET_IID(nsIDOMNavigator), true,
-                      &v, getter_AddRefs(holder));
+                      v.address(), getter_AddRefs(holder));
       NS_ENSURE_SUCCESS(rv, rv);
 
       // Hold on to the navigator object as a global property so we
       // don't need to worry about losing expando properties etc.
       if (!::JS_DefinePropertyById(cx, obj, id, v,
                                    JS_PropertyStub, JS_StrictPropertyStub,
                                    JSPROP_READONLY | JSPROP_PERMANENT |
                                    JSPROP_ENUMERATE)) {
@@ -5100,33 +5103,33 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
       }
       *objp = obj;
 
       return NS_OK;
     }
 
     if (sDocument_id == id) {
       nsCOMPtr<nsIDocument> document = win->GetDoc();
-      JS::Value v;
+      JS::Rooted<JS::Value> v(cx);
       nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
       rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), document, document,
-                      &NS_GET_IID(nsIDOMDocument), &v, getter_AddRefs(holder),
+                      &NS_GET_IID(nsIDOMDocument), v.address(), getter_AddRefs(holder),
                       false);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // The PostCreate hook for the document will handle defining the
       // property
       *objp = obj;
 
       // NB: We need to do this for any Xray wrapper.
       if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
         // Unless our object is a native wrapper, in which case we have to
         // define it ourselves.
 
-        *_retval = JS_WrapValue(cx, &v) &&
+        *_retval = JS_WrapValue(cx, v.address()) &&
                    JS_DefineProperty(cx, obj, "document", v,
                                      JS_PropertyStub, JS_StrictPropertyStub,
                                      JSPROP_READONLY | JSPROP_ENUMERATE);
         if (!*_retval) {
           return NS_ERROR_UNEXPECTED;
         }
       }
 
@@ -5159,31 +5162,31 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
     return rv;
   }
 
   // Make a fast expando if we're assigning to (not declaring or
   // binding a name) a new undefined property that's not already
   // defined on our prototype chain. This way we can access this
   // expando w/o ever getting back into XPConnect.
   if (flags & JSRESOLVE_ASSIGNING) {
-    JSObject *realObj;
-    wrapper->GetJSObject(&realObj);
+    JS::Rooted<JSObject*> realObj(cx);
+    wrapper->GetJSObject(realObj.address());
 
     if (obj == realObj) {
-      JSObject *proto;
-      if (!js::GetObjectProto(cx, obj, &proto)) {
+      JS::Rooted<JSObject*> proto(cx);
+      if (!js::GetObjectProto(cx, obj, proto.address())) {
           *_retval = JS_FALSE;
           return NS_OK;
       }
       if (proto) {
-        JSObject *pobj = NULL;
+        JS::Rooted<JSObject*> pobj(cx);
         jsval val;
 
         if (!::JS_LookupPropertyWithFlagsById(cx, proto, id, flags,
-                                              &pobj, &val)) {
+                                              pobj.address(), &val)) {
           *_retval = JS_FALSE;
 
           return NS_OK;
         }
 
         if (pobj) {
           // A property was found on the prototype chain.
           *objp = pobj;
@@ -5241,24 +5244,24 @@ nsWindowSH::OuterObject(nsIXPConnectWrap
     // running on a window w/o an outer, which means this hook should
     // never be called when we have no outer. But just in case, return
     // null to prevent leaking an inner window to code in a different
     // window.
     *_retval = nullptr;
     return NS_ERROR_UNEXPECTED;
   }
 
-  JSObject *winObj = win->FastGetGlobalJSObject();
+  JS::Rooted<JSObject*> winObj(cx, win->FastGetGlobalJSObject());
   MOZ_ASSERT(winObj);
 
   // Note that while |wrapper| is same-compartment with cx, the outer window
   // might not be. If we're running script in an inactive scope and evalute
   // |this|, the outer window is actually a cross-compartment wrapper. So we
   // need to wrap here.
-  if (!JS_WrapObject(cx, &winObj)) {
+  if (!JS_WrapObject(cx, winObj.address())) {
     *_retval = nullptr;
     return NS_ERROR_UNEXPECTED;
   }
 
   *_retval = winObj;
   return NS_OK;
 }
 
@@ -5390,38 +5393,38 @@ nsNavigatorSH::NewResolve(nsIXPConnectWr
   NS_ASSERTION(name_struct->mType == nsGlobalNameStruct::eTypeNavigatorProperty,
                "unexpected type");
 
   nsresult rv = NS_OK;
 
   nsCOMPtr<nsISupports> native(do_CreateInstance(name_struct->mCID, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  jsval prop_val = JSVAL_VOID; // Property value.
+  JS::Rooted<JS::Value> prop_val(cx, JS::UndefinedValue()); // Property value.
 
   nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi(do_QueryInterface(native));
 
   if (gpi) {
     nsCOMPtr<nsIDOMNavigator> navigator = do_QueryWrappedNative(wrapper);
     nsIDOMWindow *window = static_cast<Navigator*>(navigator.get())->GetWindow();
     NS_ENSURE_TRUE(window, NS_ERROR_UNEXPECTED);
 
-    rv = gpi->Init(window, &prop_val);
+    rv = gpi->Init(window, prop_val.address());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (JSVAL_IS_PRIMITIVE(prop_val) && !JSVAL_IS_NULL(prop_val)) {
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-    rv = WrapNative(cx, obj, native, true, &prop_val,
+    rv = WrapNative(cx, obj, native, true, prop_val.address(),
                     getter_AddRefs(holder));
 
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  if (!JS_WrapValue(cx, &prop_val)) {
+  if (!JS_WrapValue(cx, prop_val.address())) {
     return NS_ERROR_UNEXPECTED;
   }
 
   JSBool ok = ::JS_DefinePropertyById(cx, obj, id, prop_val,
                                       JS_PropertyStub, JS_StrictPropertyStub,
                                       JSPROP_ENUMERATE);
 
   *_retval = true;
@@ -5456,19 +5459,20 @@ nsNavigatorSH::PreCreate(nsISupports *na
     return NS_ERROR_UNEXPECTED;
   }
   return SetParentToWindow(win, parentObj);
 }
 
 // DOM Node helper
 
 NS_IMETHODIMP
-nsNodeSH::PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *globalObj,
+nsNodeSH::PreCreate(nsISupports *nativeObj, JSContext *cx, JSObject *aGlobalObj,
                     JSObject **parentObj)
 {
+  JS::Rooted<JSObject*> globalObj(cx, aGlobalObj);
   nsINode *node = static_cast<nsINode*>(nativeObj);
 
 #ifdef DEBUG
   {
     nsCOMPtr<nsINode> node_qi(do_QueryInterface(nativeObj));
 
     // If this assertion fires the QI implementation for the object in
     // question doesn't use the nsINode pointer as the nsISupports
@@ -5567,19 +5571,20 @@ nsNodeSH::AddProperty(nsIXPConnectWrappe
                       JSObject *obj, jsid id, jsval *vp, bool *_retval)
 {
   nsNodeSH::PreserveWrapper(GetNative(wrapper, obj));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNodeSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                     JSObject *obj, jsid id, uint32_t flags,
+                     JSObject *aObj, jsid id, uint32_t flags,
                      JSObject **objp, bool *_retval)
 {
+  JS::Rooted<JSObject*> obj(cx, aObj);
   if (id == sOnload_id || id == sOnerror_id) {
     // Make sure that this node can't go away while waiting for a
     // network load that could fire an event handler.
     // XXXbz won't this fail if the listener is added using
     // addEventListener?  On the other hand, even if I comment this
     // code out I can't seem to reproduce the bug it was trying to
     // fix....
     nsNodeSH::PreserveWrapper(GetNative(wrapper, obj));
@@ -5865,18 +5870,18 @@ nsGenericArraySH::NewResolve(nsIXPConnec
 }
 
 nsresult
 nsGenericArraySH::GetLength(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                              JSObject *obj, uint32_t *length)
 {
   *length = 0;
 
-  jsval lenval;
-  if (!JS_GetProperty(cx, obj, "length", &lenval)) {
+  JS::Rooted<JS::Value> lenval(cx);
+  if (!JS_GetProperty(cx, obj, "length", lenval.address())) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!JSVAL_IS_INT(lenval)) {
     // This can apparently happen with some sparse array impls falling back
     // onto this code.
     return NS_OK;
   }
@@ -5903,19 +5908,19 @@ nsGenericArraySH::Enumerate(nsIXPConnect
 
   if (sCurrentlyEnumerating) {
     // Don't recurse to death.
     return NS_OK;
   }
 
   sCurrentlyEnumerating = true;
 
-  jsval len_val;
+  JS::Rooted<JS::Value> len_val(cx);
   JSAutoRequest ar(cx);
-  JSBool ok = ::JS_GetProperty(cx, obj, "length", &len_val);
+  JSBool ok = ::JS_GetProperty(cx, obj, "length", len_val.address());
 
   if (ok && JSVAL_IS_INT(len_val)) {
     int32_t length = JSVAL_TO_INT(len_val);
 
     for (int32_t i = 0; ok && i < length; ++i) {
       ok = ::JS_DefineElement(cx, obj, i, JSVAL_VOID, nullptr, nullptr,
                               JSPROP_ENUMERATE | JSPROP_SHARED);
     }
@@ -5990,20 +5995,20 @@ NS_IMETHODIMP
 nsNamedArraySH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                            JSObject *obj, jsid id, uint32_t flags,
                            JSObject **objp, bool *_retval)
 {
   if ((!(JSRESOLVE_ASSIGNING & flags)) && JSID_IS_STRING(id) &&
       !ObjectIsNativeWrapper(cx, obj)) {
 
     {
-      JSObject *realObj;
+      JS::Rooted<JSObject*> realObj(cx);
 
       if (wrapper) {
-        wrapper->GetJSObject(&realObj);
+        wrapper->GetJSObject(realObj.address());
       } else {
         realObj = obj;
       }
 
       JSAutoCompartment ac(cx, realObj);
       JSObject *proto;
       if (!::JS_GetPrototype(cx, realObj, &proto)) {
         return NS_ERROR_FAILURE;
@@ -6087,21 +6092,21 @@ nsDocumentSH::NewResolve(nsIXPConnectWra
 
     nsCOMPtr<nsIDOMDocument> doc = do_QueryWrappedNative(wrapper, obj);
     NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
 
     nsCOMPtr<nsIDOMLocation> location;
     rv = doc->GetLocation(getter_AddRefs(location));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    jsval v;
+    JS::Rooted<JS::Value> v(cx);
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx), location,
-                    &NS_GET_IID(nsIDOMLocation), true, &v,
+                    &NS_GET_IID(nsIDOMLocation), true, v.address(),
                     getter_AddRefs(holder));
     NS_ENSURE_SUCCESS(rv, rv);
 
     JSBool ok = ::JS_DefinePropertyById(cx, obj, id, v, JS_PropertyStub,
                                         LocationSetter<nsIDOMDocument>,
                                         JSPROP_PERMANENT | JSPROP_ENUMERATE);
 
     if (!ok) {
@@ -6142,21 +6147,21 @@ nsDocumentSH::PostCreate(nsIXPConnectWra
   if (!win) {
     // No window, nothing else to do here
     return NS_OK;
   }
 
   nsIDocument* currentDoc = win->GetExtantDoc();
 
   if (SameCOMIdentity(doc, currentDoc)) {
-    jsval winVal;
+    JS::Rooted<JS::Value> winVal(cx);
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     nsresult rv = WrapNative(cx, obj, win, &NS_GET_IID(nsIDOMWindow), false,
-                             &winVal, getter_AddRefs(holder));
+                             winVal.address(), getter_AddRefs(holder));
     NS_ENSURE_SUCCESS(rv, rv);
 
     NS_NAMED_LITERAL_STRING(doc_str, "document");
 
     if (!::JS_DefineUCProperty(cx, JSVAL_TO_OBJECT(winVal),
                                reinterpret_cast<const jschar *>
                                                (doc_str.get()),
                                doc_str.Length(), OBJECT_TO_JSVAL(obj),
@@ -6253,21 +6258,21 @@ nsHTMLDocumentSH::GetDocumentAllNodeList
   // The document.all object is a mix of the node list returned by
   // document.getElementsByTagName("*") and a map of elements in the
   // document exposed by their id and/or name. To make access to the
   // node list part (i.e. access to elements by index) not walk the
   // document each time, we create a nsContentList and hold on to it
   // in a reserved slot (0) on the document.all JSObject.
   nsresult rv = NS_OK;
 
-  jsval collection = JS_GetReservedSlot(obj, 0);
+  JS::Rooted<JS::Value> collection(cx, JS_GetReservedSlot(obj, 0));
 
   if (!JSVAL_IS_PRIMITIVE(collection)) {
     // We already have a node list in our reserved slot, use it.
-    JSObject *obj = JSVAL_TO_OBJECT(collection);
+    JS::Rooted<JSObject*> obj(cx, JSVAL_TO_OBJECT(collection));
     nsIHTMLCollection* htmlCollection;
     rv = mozilla::dom::UnwrapObject<nsIHTMLCollection>(cx, obj, htmlCollection);
     if (NS_SUCCEEDED(rv)) {
       NS_ADDREF(*nodeList = static_cast<nsContentList*>(htmlCollection));
     }
     else {
       nsISupports *native = sXPConnect->GetNativeOfWrapper(cx, obj);
       if (native) {
@@ -6285,17 +6290,17 @@ nsHTMLDocumentSH::GetDocumentAllNodeList
       domdoc->GetElementsByTagName(NS_LITERAL_STRING("*"));
     if (!list) {
       rv = NS_ERROR_OUT_OF_MEMORY;
     }
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     nsresult tmp = WrapNative(cx, JS_GetGlobalForScopeChain(cx),
                               static_cast<nsINodeList*>(list), list, false,
-                              &collection, getter_AddRefs(holder));
+                              collection.address(), getter_AddRefs(holder));
     if (NS_FAILED(tmp)) {
       rv = tmp;
     }
 
     list.forget(nodeList);
 
     // ... and store it in our reserved slot.
     JS_SetReservedSlot(obj, 0, collection);
@@ -6309,28 +6314,28 @@ nsHTMLDocumentSH::GetDocumentAllNodeList
 
   return *nodeList != nullptr;
 }
 
 JSBool
 nsHTMLDocumentSH::DocumentAllGetProperty(JSContext *cx, JSHandleObject obj_,
                                          JSHandleId id, JSMutableHandleValue vp)
 {
-  JSObject *obj = obj_;
+  JS::Rooted<JSObject*> obj(cx, obj_);
 
   // document.all.item and .namedItem get their value in the
   // newResolve hook, so nothing to do for those properties here. And
   // we need to return early to prevent <div id="item"> from shadowing
   // document.all.item(), etc.
   if (sItem_id == id || sNamedItem_id == id) {
     return JS_TRUE;
   }
 
   while (js::GetObjectJSClass(obj) != &sHTMLDocumentAllClass) {
-    if (!js::GetObjectProto(cx, obj, &obj)) {
+    if (!js::GetObjectProto(cx, obj, obj.address())) {
       return JS_FALSE;
     }
 
     if (!obj) {
       NS_ERROR("The JS engine lies!");
 
       return JS_TRUE;
     }
@@ -6487,17 +6492,17 @@ nsHTMLDocumentSH::CallToGetPropMapper(JS
     // and create a new NS_ERROR_XPC_TOO_MANY_ARGS for argc > 1? IE
     // accepts nothing other than one arg.
     xpc::Throw(cx, NS_ERROR_INVALID_ARG);
 
     return JS_FALSE;
   }
 
   // Convert all types to string.
-  JSString *str = ::JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
+  JS::Rooted<JSString*> str(cx, ::JS_ValueToString(cx, JS_ARGV(cx, vp)[0]));
   if (!str) {
     return JS_FALSE;
   }
 
   // If we are called via document.all(id) instead of document.all.item(i) or
   // another method, use the document.all callee object as self.
   JSObject *self;
   JS::Value callee = JS_CALLEE(cx, vp);
@@ -6570,18 +6575,18 @@ nsHTMLDocumentSH::DocumentAllHelperGetPr
 
 JSBool
 nsHTMLDocumentSH::DocumentAllHelperNewResolve(JSContext *cx, JSHandleObject obj,
                                               JSHandleId id, unsigned flags,
                                               JS::MutableHandle<JSObject*> objp)
 {
   if (nsDOMClassInfo::sAll_id == id) {
     // document.all is resolved for the first time. Define it.
-    JSObject *helper;
-    if (!GetDocumentAllHelper(cx, obj, &helper)) {
+    JS::Rooted<JSObject*> helper(cx);
+    if (!GetDocumentAllHelper(cx, obj, helper.address())) {
       return JS_FALSE;
     }
 
     if (helper) {
       if (!::JS_DefineProperty(cx, helper, "all", JSVAL_VOID, nullptr, nullptr,
                                JSPROP_ENUMERATE)) {
         return JS_FALSE;
       }
@@ -6597,18 +6602,18 @@ nsHTMLDocumentSH::DocumentAllHelperNewRe
 JSBool
 nsHTMLDocumentSH::DocumentAllTagsNewResolve(JSContext *cx, JSHandleObject obj,
                                             JSHandleId id, unsigned flags,
                                             JS::MutableHandle<JSObject*> objp)
 {
   if (JSID_IS_STRING(id)) {
     nsDocument *doc = GetDocument(obj);
 
-    JSObject *proto;
-    if (!::JS_GetPrototype(cx, obj, &proto)) {
+    JS::Rooted<JSObject*> proto(cx);
+    if (!::JS_GetPrototype(cx, obj, proto.address())) {
       return JS_FALSE;
     }
     if (MOZ_UNLIKELY(!proto)) {
       return JS_TRUE;
     }
 
     JSBool found;
     if (!::JS_HasPropertyById(cx, proto, id, &found)) {
@@ -6618,21 +6623,21 @@ nsHTMLDocumentSH::DocumentAllTagsNewReso
     if (found) {
       return JS_TRUE;
     }
 
     nsRefPtr<nsContentList> tags =
       doc->GetElementsByTagName(nsDependentJSString(id));
 
     if (tags) {
-      jsval v;
+      JS::Rooted<JS::Value> v(cx);
       nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
       nsresult rv = WrapNative(cx, JS_GetGlobalForScopeChain(cx),
                                static_cast<nsINodeList*>(tags), tags, true,
-                               &v, getter_AddRefs(holder));
+                               v.address(), getter_AddRefs(holder));
       if (NS_FAILED(rv)) {
         xpc::Throw(cx, rv);
 
         return JS_FALSE;
       }
 
       if (!::JS_DefinePropertyById(cx, obj, id, v, nullptr, nullptr, 0)) {
         return JS_FALSE;
@@ -6671,21 +6676,21 @@ ResolveAll(JSContext* cx, nsIDocument* d
   if (proto && !JS_HasProperty(cx, proto, "all", &hasAll)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (hasAll && helper) {
     // Our helper's prototype now has an "all" property, remove
     // the helper out of the prototype chain to prevent
     // shadowing of the now defined "all" property.
-    JSObject *tmp = obj, *tmpProto = tmp;
+    JS::Rooted<JSObject*> tmp(cx, obj), tmpProto(cx, tmp);
 
     do {
       tmp = tmpProto;
-      if (!::JS_GetPrototype(cx, tmp, &tmpProto)) {
+      if (!::JS_GetPrototype(cx, tmp, tmpProto.address())) {
         return NS_ERROR_UNEXPECTED;
       }
     } while (tmpProto != helper);
 
     ::JS_SetPrototype(cx, tmp, proto);
   }
 
   // If we don't already have a helper and "all" isn't already defined on
@@ -6770,19 +6775,20 @@ nsHTMLDocumentSH::NewResolve(nsIXPConnec
     }
   }
 
   return nsDocumentSH::NewResolve(wrapper, cx, obj, id, flags, objp, _retval);
 }
 
 NS_IMETHODIMP
 nsHTMLDocumentSH::GetProperty(nsIXPConnectWrappedNative *wrapper,
-                              JSContext *cx, JSObject *obj, jsid id,
+                              JSContext *cx, JSObject *aObj, jsid id,
                               jsval *vp, bool *_retval)
 {
+  JS::Rooted<JSObject*> obj(cx, aObj);
   nsCOMPtr<nsISupports> result;
 
   JSAutoRequest ar(cx);
 
   nsWrapperCache *cache;
   nsresult rv = ResolveImpl(cx, wrapper, id, getter_AddRefs(result), &cache);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -6796,20 +6802,21 @@ nsHTMLDocumentSH::GetProperty(nsIXPConne
 
   return NS_OK;
 }
 
 // HTMLFormElement helper
 
 NS_IMETHODIMP
 nsHTMLFormElementSH::NewResolve(nsIXPConnectWrappedNative *wrapper,
-                                JSContext *cx, JSObject *obj, jsid id,
+                                JSContext *cx, JSObject *aObj, jsid id,
                                 uint32_t flags, JSObject **objp,
                                 bool *_retval)
 {
+  JS::Rooted<JSObject*> obj(cx, aObj);
   // For native wrappers, do not resolve random names on form
   if ((!(JSRESOLVE_ASSIGNING & flags)) && JSID_IS_STRING(id) &&
       (!ObjectIsNativeWrapper(cx, obj) ||
        xpc::WrapperFactory::XrayWrapperNotShadowing(obj, id))) {
     nsCOMPtr<nsIForm> form(do_QueryWrappedNative(wrapper, obj));
 
     nsDependentJSString name(id);
     nsWrapperCache* cache;
@@ -7164,31 +7171,31 @@ NS_IMETHODIMP
 nsStorage2SH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, jsid id, uint32_t flags,
                          JSObject **objp, bool *_retval)
 {
   if (ObjectIsNativeWrapper(cx, obj)) {
     return NS_OK;
   }
 
-  JSObject *realObj;
-  wrapper->GetJSObject(&realObj);
+  JS::Rooted<JSObject*> realObj(cx);
+  wrapper->GetJSObject(realObj.address());
 
   JSAutoCompartment ac(cx, realObj);
 
   // First check to see if the property is defined on our prototype,
   // after converting id to a string if it's an integer.
 
-  JSString *jsstr = IdToString(cx, id);
+  JS::Rooted<JSString*> jsstr(cx, IdToString(cx, id));
   if (!jsstr) {
     return NS_OK;
   }
 
-  JSObject *proto;
-  if (!::JS_GetPrototype(cx, realObj, &proto)) {
+  JS::Rooted<JSObject*> proto(cx);
+  if (!::JS_GetPrototype(cx, realObj, proto.address())) {
     return NS_ERROR_FAILURE;
   }
   JSBool hasProp;
 
   if (proto &&
       (::JS_HasPropertyById(cx, proto, id, &hasProp) &&
        hasProp)) {
     // We found the property we're resolving on the prototype,
@@ -7412,17 +7419,17 @@ nsDOMConstructorSH::NewResolve(nsIXPConn
   // For regular DOM constructors, we have our interface constants defined on
   // us by nsWindowSH::GlobalResolve. However, XrayWrappers can't see these
   // interface constants (as they look like expando properties) so we have to
   // specially resolve those constants here, but only for Xray wrappers.
   if (!ObjectIsNativeWrapper(cx, obj)) {
     return NS_OK;
   }
 
-  JSObject *nativePropsObj = xpc::XrayUtils::GetNativePropertiesObject(cx, obj);
+  JS::Rooted<JSObject*> nativePropsObj(cx, xpc::XrayUtils::GetNativePropertiesObject(cx, obj));
   nsDOMConstructor *wrapped =
     static_cast<nsDOMConstructor *>(wrapper->Native());
   nsresult rv = wrapped->ResolveInterfaceConstants(cx, nativePropsObj);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Now re-lookup the ID to see if we should report back that we resolved the
   // looked-for constant. Note that we don't have to worry about infinitely
   // recurring back here because the Xray wrapper's holder object doesn't call
@@ -7436,19 +7443,20 @@ nsDOMConstructorSH::NewResolve(nsIXPConn
   if (found) {
     *objp = obj;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMConstructorSH::Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                         JSObject *obj, uint32_t argc, jsval *argv, jsval *vp,
+                         JSObject *aObj, uint32_t argc, jsval *argv, jsval *vp,
                          bool *_retval)
 {
+  JS::Rooted<JSObject*> obj(cx, aObj);
   MOZ_ASSERT(obj);
 
   nsDOMConstructor *wrapped =
     static_cast<nsDOMConstructor *>(wrapper->Native());
 
 #ifdef DEBUG
   {
     nsCOMPtr<nsIDOMDOMConstructor> is_constructor =
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -370,18 +370,19 @@ public:
   NS_IMETHOD OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
                          JSObject * obj, JSObject * *_retval);
 
   static JSBool GlobalScopePolluterNewResolve(JSContext *cx, JSHandleObject obj,
                                               JSHandleId id, unsigned flags,
                                               JS::MutableHandle<JSObject*> objp);
   static JSBool GlobalScopePolluterGetProperty(JSContext *cx, JSHandleObject obj,
                                                JSHandleId id, JSMutableHandleValue vp);
-  static JSBool InvalidateGlobalScopePolluter(JSContext *cx, JSObject *obj);
-  static nsresult InstallGlobalScopePolluter(JSContext *cx, JSObject *obj);
+  static JSBool InvalidateGlobalScopePolluter(JSContext *cx, JSObject* obj);
+  static nsresult InstallGlobalScopePolluter(JSContext *cx,
+                                             JS::Handle<JSObject*> obj);
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsWindowSH(aData);
   }
 };
 
 // Location scriptable helper
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2462,17 +2462,18 @@ nsGlobalWindow::SetNewDocument(nsIDocume
   // fixup above) we would end up holding the GSP alive (through
   // XPConnect's internal marking of wrapper prototypes) as long as
   // the inner window was around, and if the GSP had properties on
   // it that held an element alive we'd hold the document alive,
   // which could hold event handlers alive, which hold the context
   // alive etc.
 
   if ((!reUseInnerWindow || aDocument != oldDoc) && !aState) {
-    nsWindowSH::InstallGlobalScopePolluter(cx, newInnerWindow->mJSObject);
+    JS::Rooted<JSObject*> obj(cx, newInnerWindow->mJSObject);
+    nsWindowSH::InstallGlobalScopePolluter(cx, obj);
   }
 
   aDocument->SetScriptGlobalObject(newInnerWindow);
 
   if (!aState) {
     if (reUseInnerWindow) {
       if (newInnerWindow->mDoc != aDocument) {
         newInnerWindow->mDoc = aDocument;
--- a/dom/interfaces/contacts/moz.build
+++ b/dom/interfaces/contacts/moz.build
@@ -1,17 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPIDL_SOURCES += [
+    'nsIContactProperties.idl',
     'nsIDOMContactManager.idl',
-    'nsIContactProperties.idl',
     'nsIDOMMozContactChangeEvent.idl',
 ]
 
 XPIDL_MODULE = 'dom_contacts'
 
 XPIDL_FLAGS += [
     '-I$(topsrcdir)/dom/interfaces/base',
     '-I$(topsrcdir)/dom/interfaces/events',
--- a/dom/interfaces/events/moz.build
+++ b/dom/interfaces/events/moz.build
@@ -20,16 +20,19 @@ XPIDL_SOURCES += [
     'nsIDOMDeviceMotionEvent.idl',
     'nsIDOMDeviceOrientationEvent.idl',
     'nsIDOMDeviceProximityEvent.idl',
     'nsIDOMDragEvent.idl',
     'nsIDOMElementReplaceEvent.idl',
     'nsIDOMEvent.idl',
     'nsIDOMEventListener.idl',
     'nsIDOMEventTarget.idl',
+    'nsIDOMGamepadAxisMoveEvent.idl',
+    'nsIDOMGamepadButtonEvent.idl',
+    'nsIDOMGamepadEvent.idl',
     'nsIDOMHashChangeEvent.idl',
     'nsIDOMKeyEvent.idl',
     'nsIDOMMessageEvent.idl',
     'nsIDOMMouseEvent.idl',
     'nsIDOMMouseScrollEvent.idl',
     'nsIDOMMutationEvent.idl',
     'nsIDOMNSEvent.idl',
     'nsIDOMNotifyAudioAvailableEvent.idl',
@@ -45,19 +48,16 @@ XPIDL_SOURCES += [
     'nsIDOMSmartCardEvent.idl',
     'nsIDOMStyleSheetAddedEvent.idl',
     'nsIDOMStyleSheetRemovedEvent.idl',
     'nsIDOMTouchEvent.idl',
     'nsIDOMTransitionEvent.idl',
     'nsIDOMUIEvent.idl',
     'nsIDOMUserProximityEvent.idl',
     'nsIDOMWheelEvent.idl',
-    'nsIDOMGamepadButtonEvent.idl',
-    'nsIDOMGamepadAxisMoveEvent.idl',
-    'nsIDOMGamepadEvent.idl',
 ]
 
 XPIDL_MODULE = 'dom_events'
 
 XPIDL_FLAGS += [
     '-I$(topsrcdir)/dom/interfaces/base',
 ]
 
--- a/dom/mobilemessage/src/moz.build
+++ b/dom/mobilemessage/src/moz.build
@@ -1,31 +1,31 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.dom.mobilemessage += [
+    'Constants.h',
+    'MobileMessageCallback.h',
+    'MobileMessageService.h',
     'SmsChild.h',
     'SmsParent.h',
-    'MobileMessageService.h',
     'SmsServicesFactory.h',
-    'Constants.h',
     'Types.h',
-    'MobileMessageCallback.h',
 ]
 
 if not CONFIG['MOZ_B2G_RIL']:
     EXPORTS.mozilla.dom.mobilemessage += [
+        'MmsService.h',
         'MobileMessageDatabaseService.h',
-        'MmsService.h',
     ]
 
 EXPORTS.mozilla.dom += [
+    'MmsMessage.h',
+    'MobileMessageManager.h',
+    'SmsFilter.h',
     'SmsManager.h',
-    'MobileMessageManager.h',
     'SmsMessage.h',
-    'MmsMessage.h',
     'SmsSegmentInfo.h',
-    'SmsFilter.h',
 ]
 
--- a/dom/src/notification/moz.build
+++ b/dom/src/notification/moz.build
@@ -2,12 +2,12 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'dom'
 
 EXPORTS.mozilla.dom += [
+    'DesktopNotification.h',
     'Notification.h',
-    'DesktopNotification.h',
 ]
 
--- a/dom/webidl/AudioNode.webidl
+++ b/dom/webidl/AudioNode.webidl
@@ -21,20 +21,18 @@ enum ChannelInterpretation {
     "discrete"
 };
 
 [PrefControlled]
 interface AudioNode : EventTarget {
 
     [Throws]
     void connect(AudioNode destination, optional unsigned long output = 0, optional unsigned long input = 0);
-
-    // [Throws]
-    // void connect(AudioParam destination, optional unsigned long output = 0);
-
+    [Throws]
+    void connect(AudioParam destination, optional unsigned long output = 0);
     [Throws]
     void disconnect(optional unsigned long output = 0);
 
     readonly attribute AudioContext context;
     readonly attribute unsigned long numberOfInputs;
     readonly attribute unsigned long numberOfOutputs;
 
     // Channel up-mixing and down-mixing rules for all inputs.
--- a/gfx/cairo/cairo/src/moz.build
+++ b/gfx/cairo/cairo/src/moz.build
@@ -55,16 +55,16 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'qt
 
 if CONFIG['BUILD_CAIRO_SVG']:
     EXPORTS.cairo += [
         'cairo-svg.h'
     ]
 
 if CONFIG['MOZ_X11']:
     EXPORTS.cairo += [
+        'cairo-xlib-xrender.h',
         'cairo-xlib.h',
-        'cairo-xlib-xrender.h',
     ]
 
 if CONFIG['MOZ_ENABLE_CAIRO_FT']:
     EXPORTS.cairo += [
         'cairo-ft.h',
     ]
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -1,19 +1,56 @@
 /* -*- Mode: C++; tab-width: 20; 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 "mozilla/layers/Compositor.h"
+#include "mozilla/layers/Effects.h"
 
 namespace mozilla {
 namespace layers {
 
 /* static */ LayersBackend Compositor::sBackend = LAYERS_NONE;
 /* static */ LayersBackend
 Compositor::GetBackend()
 {
   return sBackend;
 }
 
+void
+Compositor::DrawDiagnostics(const gfx::Color& aColor,
+                            const gfx::Rect& rect,
+                            const gfx::Rect& aClipRect,
+                            const gfx::Matrix4x4& aTransform,
+                            const gfx::Point& aOffset)
+{
+  if (!mDrawColoredBorders) {
+    return;
+  }
+  EffectChain effects;
+  effects.mPrimaryEffect = new EffectSolidColor(aColor);
+  int lWidth = 1;
+  float opacity = 0.8;
+  // left
+  this->DrawQuad(gfx::Rect(rect.x, rect.y,
+                           lWidth, rect.height),
+                 aClipRect, effects, opacity,
+                 aTransform, aOffset);
+  // top
+  this->DrawQuad(gfx::Rect(rect.x + lWidth, rect.y,
+                           rect.width - 2 * lWidth, lWidth),
+                 aClipRect, effects, opacity,
+                 aTransform, aOffset);
+  // right
+  this->DrawQuad(gfx::Rect(rect.x + rect.width - lWidth, rect.y,
+                           lWidth, rect.height),
+                 aClipRect, effects, opacity,
+                 aTransform, aOffset);
+  // bottom
+  this->DrawQuad(gfx::Rect(rect.x + lWidth, rect.y + rect.height-lWidth,
+                           rect.width - 2 * lWidth, lWidth),
+                 aClipRect, effects, opacity,
+                 aTransform, aOffset);
+}
+
 } // namespace
 } // namespace
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -165,16 +165,17 @@ enum SurfaceInitMode
  * The target and viewport methods can be called before any DrawQuad call and
  * affect any subsequent DrawQuad calls.
  */
 class Compositor : public RefCounted<Compositor>
 {
 public:
   Compositor()
     : mCompositorID(0)
+    , mDrawColoredBorders(false)
   {
     MOZ_COUNT_CTOR(Compositor);
   }
   virtual ~Compositor()
   {
     MOZ_COUNT_DTOR(Compositor);
   }
 
@@ -317,16 +318,32 @@ public:
   virtual void PrepareViewport(const gfx::IntSize& aSize,
                                const gfxMatrix& aWorldTransform) = 0;
 
   /**
    * Whether textures created by this compositor can receive partial updates.
    */
   virtual bool SupportsPartialTextureUpdate() = 0;
 
+  void EnableColoredBorders()
+  {
+    mDrawColoredBorders = true;
+  }
+  void DisableColoredBorders()
+  {
+    mDrawColoredBorders = false;
+  }
+
+  void DrawDiagnostics(const gfx::Color& color,
+                       const gfx::Rect& visibleRect,
+                       const gfx::Rect& aClipRect,
+                       const gfx::Matrix4x4& transform,
+                       const gfx::Point& aOffset);
+
+
 #ifdef MOZ_DUMP_PAINTING
   virtual const char* Name() const = 0;
 #endif // MOZ_DUMP_PAINTING
 
 
   /**
    * Each Compositor has a unique ID.
    * This ID is used to keep references to each Compositor in a map accessed
@@ -375,14 +392,15 @@ public:
    * static method. We need this for creating texture clients/hosts etc. when we
    * don't have a reference to a Compositor.
    */
   static LayersBackend GetBackend();
 
 protected:
   uint32_t mCompositorID;
   static LayersBackend sBackend;
+  bool mDrawColoredBorders;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_COMPOSITOR_H */
--- a/gfx/layers/composite/ColorLayerComposite.cpp
+++ b/gfx/layers/composite/ColorLayerComposite.cpp
@@ -31,12 +31,16 @@ ColorLayerComposite::RenderLayer(const n
 
   float opacity = GetEffectiveOpacity();
 
   gfx::Matrix4x4 transform;
   ToMatrix4x4(GetEffectiveTransform(), transform);
 
   mCompositor->DrawQuad(rect, clipRect, effects, opacity,
                         transform, gfx::Point(aOffset.x, aOffset.y));
+  mCompositor->DrawDiagnostics(gfx::Color(0.0, 1.0, 1.0, 1.0),
+                               rect, clipRect,
+                               transform, gfx::Point(aOffset.x, aOffset.y));
+
 }
 
 } /* layers */
 } /* mozilla */
--- a/gfx/layers/composite/ContentHost.cpp
+++ b/gfx/layers/composite/ContentHost.cpp
@@ -169,16 +169,19 @@ ContentHostBase::Composite(EffectChain& 
             gfx::Rect rect(tileScreenRect.x, tileScreenRect.y,
                            tileScreenRect.width, tileScreenRect.height);
 
             effect->mTextureCoords = Rect(Float(tileRegionRect.x) / texRect.width,
                                           Float(tileRegionRect.y) / texRect.height,
                                           Float(tileRegionRect.width) / texRect.width,
                                           Float(tileRegionRect.height) / texRect.height);
             GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain, aOpacity, aTransform, aOffset);
+            GetCompositor()->DrawDiagnostics(gfx::Color(0.0,1.0,0.0,1.0),
+                                             rect, aClipRect, aTransform, aOffset);
+
         }
       }
     }
 
     if (iterOnWhite) {
       iterOnWhite->NextTile();
     }
   } while (usingTiles && tileIter->NextTile());
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -98,16 +98,18 @@ ImageHostSingle::Composite(EffectChain& 
   TileIterator* it = mTextureHost->AsTileIterator();
   if (it) {
     it->BeginTileIteration();
     do {
       nsIntRect tileRect = it->GetTileRect();
       gfx::Rect rect(tileRect.x, tileRect.y, tileRect.width, tileRect.height);
       GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain,
                                 aOpacity, aTransform, aOffset);
+      GetCompositor()->DrawDiagnostics(gfx::Color(0.5,0.0,0.0,1.0),
+                                       rect, aClipRect, aTransform, aOffset);
     } while (it->NextTile());
     it->EndTileIteration();
   } else {
     IntSize textureSize = mTextureHost->GetSize();
     gfx::Rect rect(0, 0,
                    mPictureRect.width,
                    mPictureRect.height);
     if (mHasPictureRect) {
@@ -122,16 +124,18 @@ ImageHostSingle::Composite(EffectChain& 
 
     if (mTextureHost->GetFlags() & NeedsYFlip) {
       effect->mTextureCoords.y = effect->mTextureCoords.YMost();
       effect->mTextureCoords.height = -effect->mTextureCoords.height;
     }
 
     GetCompositor()->DrawQuad(rect, aClipRect, aEffectChain,
                               aOpacity, aTransform, aOffset);
+    GetCompositor()->DrawDiagnostics(gfx::Color(1.0,0.1,0.1,1.0),
+                                     rect, aClipRect, aTransform, aOffset);
   }
 
   mTextureHost->Unlock();
 }
 
 #ifdef MOZ_LAYERS_HAVE_LOG
 void
 ImageHostSingle::PrintInfo(nsACString& aTo, const char* aPrefix)
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -200,16 +200,18 @@ TiledContentHost::RenderTile(const Tiled
     Rect textureRect(rect->x - aTextureOffset.x, rect->y - aTextureOffset.y,
                      rect->width, rect->height);
 
     effect->mTextureCoords = Rect(textureRect.x / aTextureBounds.width,
                                   textureRect.y / aTextureBounds.height,
                                   textureRect.width / aTextureBounds.width,
                                   textureRect.height / aTextureBounds.height);
     mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, aOpacity, aTransform, aOffset);
+    mCompositor->DrawDiagnostics(gfx::Color(0.0,0.5,0.0,1.0),
+                                 graphicsRect, aClipRect, aTransform, aOffset);
   }
 
   aTile.mTextureHost->Unlock();
 }
 
 void
 TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
                                     const nsIntRegion& aValidRegion,
--- a/gfx/layers/ipc/LayerTransaction.ipdlh
+++ b/gfx/layers/ipc/LayerTransaction.ipdlh
@@ -227,16 +227,17 @@ struct OpSetLayerAttributes {
 // Monkey with the tree structure
 struct OpSetRoot          { PLayer root; };
 struct OpInsertAfter      { PLayer container; PLayer childLayer; PLayer after; };
 struct OpAppendChild      { PLayer container; PLayer childLayer; };
 struct OpRemoveChild      { PLayer container; PLayer childLayer; };
 struct OpRepositionChild  { PLayer container; PLayer childLayer; PLayer after; };
 struct OpRaiseToTopChild  { PLayer container; PLayer childLayer; };
 
+struct OpSetColoredBorders { bool enabled; };
 
 // Paint (buffer update)
 struct OpPaintTiledLayerBuffer {
   PCompositable compositable;
   // Bug 747811
   // FIXME: We need to support sharing tile across process.
   uintptr_t tiledLayerBuffer;
 };
@@ -286,16 +287,17 @@ union Edit {
   OpCreateThebesLayer;
   OpCreateContainerLayer;
   OpCreateImageLayer;
   OpCreateColorLayer;
   OpCreateCanvasLayer;
   OpCreateRefLayer;
 
   OpSetLayerAttributes;
+  OpSetColoredBorders;
 
   OpSetRoot;
   OpInsertAfter;
   OpAppendChild;
   OpRemoveChild;
   OpRepositionChild;
   OpRaiseToTopChild;
 
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -233,17 +233,17 @@ LayerTransactionParent::RecvUpdate(const
       MOZ_LAYERS_LOG(("[ParentSide] CreateRefLayer"));
 
       nsRefPtr<RefLayerComposite> layer =
         layer_manager()->CreateRefLayerComposite();
       AsLayerComposite(edit.get_OpCreateRefLayer())->Bind(layer);
       break;
     }
 
-      // Attributes
+    // Attributes
     case Edit::TOpSetLayerAttributes: {
       MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes"));
 
       const OpSetLayerAttributes& osla = edit.get_OpSetLayerAttributes();
       Layer* layer = AsLayerComposite(osla)->AsLayer();
       const LayerAttributes& attrs = osla.attrs();
 
       const CommonLayerAttributes& common = attrs.common();
@@ -324,16 +324,24 @@ LayerTransactionParent::RecvUpdate(const
         imageLayer->SetFilter(attrs.filter());
         break;
       }
       default:
         NS_RUNTIMEABORT("not reached");
       }
       break;
     }
+    case Edit::TOpSetColoredBorders: {
+      if (edit.get_OpSetColoredBorders().enabled()) {
+        mLayerManager->GetCompositor()->EnableColoredBorders();
+      } else {
+        mLayerManager->GetCompositor()->DisableColoredBorders();
+      }
+      break;
+    }
     // Tree ops
     case Edit::TOpSetRoot: {
       MOZ_LAYERS_LOG(("[ParentSide] SetRoot"));
 
       mRoot = AsLayerComposite(edit.get_OpSetRoot())->AsContainer();
       break;
     }
     case Edit::TOpInsertAfter: {
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -160,16 +160,17 @@ CompositableForwarder::IdentifyTextureHo
 {
   mMaxTextureSize = aIdentifier.mMaxTextureSize;
   mCompositorBackend = aIdentifier.mParentBackend;
 }
 
 ShadowLayerForwarder::ShadowLayerForwarder()
  : mShadowManager(NULL)
  , mIsFirstPaint(false)
+ , mDrawColoredBorders(false)
 {
   mTxn = new Transaction();
 }
 
 ShadowLayerForwarder::~ShadowLayerForwarder()
 {
   NS_ABORT_IF_FALSE(mTxn->Finished(), "unfinished transaction?");
   delete mTxn;
@@ -355,16 +356,21 @@ ShadowLayerForwarder::UpdatePictureRect(
 bool
 ShadowLayerForwarder::EndTransaction(InfallibleTArray<EditReply>* aReplies)
 {
   PROFILER_LABEL("ShadowLayerForwarder", "EndTranscation");
   RenderTraceScope rendertrace("Foward Transaction", "000091");
   NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to");
   NS_ABORT_IF_FALSE(!mTxn->Finished(), "forgot BeginTransaction?");
 
+  if (mDrawColoredBorders != gfxPlatform::DrawLayerBorders()) {
+    mDrawColoredBorders = gfxPlatform::DrawLayerBorders();
+    mTxn->AddEdit(OpSetColoredBorders(mDrawColoredBorders));
+  }
+
   AutoTxnEnd _(mTxn);
 
   if (mTxn->Empty() && !mTxn->RotationChanged()) {
     MOZ_LAYERS_LOG(("[LayersForwarder] 0-length cset (?) and no rotation event, skipping Update()"));
     return true;
   }
 
   MOZ_LAYERS_LOG(("[LayersForwarder] destroying buffers..."));
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -427,16 +427,17 @@ private:
   static bool
   PlatformCloseDescriptor(const SurfaceDescriptor& aDescriptor);
 
   bool PlatformDestroySharedSurface(SurfaceDescriptor* aSurface);
 
   Transaction* mTxn;
 
   bool mIsFirstPaint;
+  bool mDrawColoredBorders;
 };
 
 class CompositableClient;
 
 /**
  * A ShadowableLayer is a Layer can be shared with a parent context
  * through a ShadowLayerForwarder.  A ShadowableLayer maps to a
  * Shadow*Layer in a parent context.
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -7,45 +7,45 @@
 MODULE = 'thebes'
 
 EXPORTS += [
     'BasicCanvasLayer.h',
     'BasicImplData.h',
     'BasicLayers.h',
     'BasicLayersImpl.h',
     'BasicThebesLayer.h',
-    'CompositableHost.h',
-    'CopyableCanvasLayer.h',
+    'ClientCanvasLayer.h',
+    'ClientContainerLayer.h',
     'ClientLayerManager.h',
-    'ClientContainerLayer.h',
     'ClientThebesLayer.h',
     'ClientTiledThebesLayer.h',
-    'ClientCanvasLayer.h',
     'Composer2D.h',
-    'GonkIOSurfaceImage.h',
-    'FrameMetrics.h',
+    'CompositableHost.h',
     'CompositorChild.h',
     'CompositorParent.h',
+    'CopyableCanvasLayer.h',
+    'FrameMetrics.h',
+    'GonkIOSurfaceImage.h',
     'ImageContainer.h',
-    'ImageLayers.h',
+    'ImageHost.h',
     'ImageLayerOGL.h',
-    'ImageHost.h',
+    'ImageLayers.h',
     'ImageTypes.h',
+    'LayerManagerOGL.h',
+    'LayerManagerOGLProgram.h',
+    'LayerManagerOGLShaders.h',
+    'LayerSorter.h',
+    'LayerTreeInvalidation.h',
     'Layers.h',
     'LayersLogging.h',
-    'LayerManagerOGLShaders.h',
-    'LayerManagerOGL.h',
-    'LayerManagerOGLProgram.h',
-    'LayerSorter.h',
-    'LayerTreeInvalidation.h',
     'ReadbackLayer.h',
     'ShadowLayersManager.h',
     'SharedTextureImage.h',
+    'ShmemYCbCrImage.h',
     'TexturePoolOGL.h',
-    'ShmemYCbCrImage.h',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     if CONFIG['MOZ_ENABLE_D3D9_LAYER']:
         EXPORTS += [
             'LayerManagerD3D9.h',
             'DeviceManagerD3D9.h',
         ]
@@ -66,51 +66,51 @@ EXPORTS.mozilla.layers += [
     'AsyncCompositionManager.h',
     'AsyncPanZoomController.h',
     'Axis.h',
     'BasicCompositor.h',
     'CanvasClient.h',
     'CanvasLayerComposite.h',
     'ColorLayerComposite.h',
     'CompositableClient.h',
+    'CompositableForwarder.h',
     'CompositableTransactionParent.h',
+    'CompositingRenderTargetOGL.h',
     'Compositor.h',
+    'CompositorChild.h',
+    'CompositorCocoaWidgetHelper.h',
     'CompositorOGL.h',
+    'CompositorParent.h',
     'CompositorTypes.h',
     'ContainerLayerComposite.h',
+    'ContentClient.h',
     'ContentHost.h',
-    'CompositingRenderTargetOGL.h',
-    'CompositorCocoaWidgetHelper.h',
-    'CompositorChild.h',
-    'CompositorParent.h',
-    'CompositableForwarder.h',
-    'ContentClient.h',
     'Effects.h',
     'GeckoContentController.h',
     'GestureEventListener.h',
+    'ISurfaceAllocator.h',
     'ImageBridgeChild.h',
     'ImageBridgeParent.h',
     'ImageClient.h',
+    'ImageHost.h',
     'ImageLayerComposite.h',
-    'ImageHost.h',
-    'ISurfaceAllocator.h',
     'LayerManagerComposite.h',
-    'LayersTypes.h',
     'LayerTransactionChild.h',
     'LayerTransactionParent.h',
+    'LayersTypes.h',
+    'RenderTrace.h',
     'ShadowLayers.h',
     'ShadowLayersManager.h',
-    'RenderTrace.h',
+    'SharedPlanarYCbCrImage.h',
     'SharedRGBImage.h',
-    'SharedPlanarYCbCrImage.h',
     'ShmemYCbCrImage.h',
     'TaskThrottler.h',
     'TextureClient.h',
+    'TextureClientOGL.h',
     'TextureHost.h',
-    'TextureClientOGL.h',
     'TextureHostOGL.h',
     'ThebesLayerComposite.h',
     'TiledContentClient.h',
 ]
 
 if CONFIG['MOZ_X11']:
     EXPORTS.mozilla.layers += [
         'ShadowLayerUtilsX11.h'
--- a/gfx/thebes/d3dkmtQueryStatistics.h
+++ b/gfx/thebes/d3dkmtQueryStatistics.h
@@ -135,23 +135,23 @@ typedef union _D3DKMTQS_RESULT
 
 typedef struct _D3DKMTQS_QUERY_SEGMENT
 {
     ULONG SegmentId;
 } D3DKMTQS_QUERY_SEGMENT;
 
 typedef struct _D3DKMTQS
 {
-    __in D3DKMTQS_TYPE Type;
-    __in LUID AdapterLuid;
-    __in_opt HANDLE hProcess;
-    __out D3DKMTQS_RESULT QueryResult;
+    D3DKMTQS_TYPE Type;
+    LUID AdapterLuid;
+    HANDLE hProcess;
+    D3DKMTQS_RESULT QueryResult;
 
     union
     {
-        __in D3DKMTQS_QUERY_SEGMENT QuerySegment;
-        __in D3DKMTQS_QUERY_SEGMENT QueryProcessSegment;
+        D3DKMTQS_QUERY_SEGMENT QuerySegment;
+        D3DKMTQS_QUERY_SEGMENT QueryProcessSegment;
     };
 } D3DKMTQS;
 
 extern "C" {
-typedef __checkReturn NTSTATUS (APIENTRY *PFND3DKMTQS)(__in const D3DKMTQS *);
+typedef __checkReturn NTSTATUS (APIENTRY *PFND3DKMTQS)(const D3DKMTQS *);
 }
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -91,16 +91,18 @@ static qcms_transform *gCMSRGBATransform
 
 static bool gCMSInitialized = false;
 static eCMSMode gCMSMode = eCMSMode_Off;
 static int gCMSIntent = -2;
 
 static void ShutdownCMS();
 static void MigratePrefs();
 
+static bool sDrawLayerBorders = false;
+
 #include "mozilla/gfx/2D.h"
 using namespace mozilla::gfx;
 
 // logs shared across gfx
 #ifdef PR_LOGGING
 static PRLogModuleInfo *sFontlistLog = nullptr;
 static PRLogModuleInfo *sFontInitLog = nullptr;
 static PRLogModuleInfo *sTextrunLog = nullptr;
@@ -395,16 +397,20 @@ gfxPlatform::Init()
     // ::Shutdown to be called.
     nsCOMPtr<nsISupports> forceReg
         = do_CreateInstance("@mozilla.org/gfx/init;1");
 
     Preferences::RegisterCallbackAndCall(RecordingPrefChanged, "gfx.2d.recording", nullptr);
 
     gPlatform->mOrientationSyncMillis = Preferences::GetUint("layers.orientation.sync.timeout", (uint32_t)0);
 
+    mozilla::Preferences::AddBoolVarCache(&sDrawLayerBorders,
+                                          "layers.draw-borders",
+                                          false);
+
     CreateCMSOutputProfile();
 }
 
 void
 gfxPlatform::Shutdown()
 {
     // These may be called before the corresponding subsystems have actually
     // started up. That's OK, they can handle it.
@@ -1098,16 +1104,23 @@ gfxPlatform::IsLangCJK(eFontPrefLang aLa
         case eFontPrefLang_Korean:
         case eFontPrefLang_CJKSet:
             return true;
         default:
             return false;
     }
 }
 
+bool
+gfxPlatform::DrawLayerBorders()
+{
+    return sDrawLayerBorders;
+}
+
+
 void
 gfxPlatform::GetLangPrefs(eFontPrefLang aPrefLangs[], uint32_t &aLen, eFontPrefLang aCharLang, eFontPrefLang aPageLang)
 {
     if (IsLangCJK(aCharLang)) {
         AppendCJKPrefLangs(aPrefLangs, aLen, aCharLang, aPageLang);
     } else {
         AppendPrefLang(aPrefLangs, aLen, aCharLang);
     }
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -525,16 +525,18 @@ public:
     bool WorkAroundDriverBugs() const { return mWorkAroundDriverBugs; }
 
     virtual int GetScreenDepth() const;
 
     bool WidgetUpdateFlashing() const { return mWidgetUpdateFlashing; }
 
     uint32_t GetOrientationSyncMillis() const;
 
+    static bool DrawLayerBorders();
+
 protected:
     gfxPlatform();
     virtual ~gfxPlatform();
 
     void AppendCJKPrefLangs(eFontPrefLang aPrefLangs[], uint32_t &aLen, 
                             eFontPrefLang aCharLang, eFontPrefLang aPageLang);
 
     /**
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -163,17 +163,17 @@ typedef HRESULT(WINAPI*CreateDXGIFactory
   REFIID riid,
   void **ppFactory
 );
 #endif
 
 class GPUAdapterMultiReporter : public nsIMemoryMultiReporter {
 
     // Callers must Release the DXGIAdapter after use or risk mem-leak
-    static bool GetDXGIAdapter(__out IDXGIAdapter **DXGIAdapter)
+    static bool GetDXGIAdapter(IDXGIAdapter **DXGIAdapter)
     {
         ID3D10Device1 *D2D10Device;
         IDXGIDevice *DXGIDevice;
         bool result = false;
         
         if (D2D10Device = mozilla::gfx::Factory::GetDirect3D10Device()) {
             if (D2D10Device->QueryInterface(__uuidof(IDXGIDevice), (void **)&DXGIDevice) == S_OK) {
                 result = (DXGIDevice->GetAdapter(DXGIAdapter) == S_OK);
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -75,18 +75,18 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gt
         'gfxGdkNativeRenderer.h',
         'gfxPDFSurface.h',
         'gfxPSSurface.h',
         'gfxPlatformGtk.h',
     ]
 
     if CONFIG['MOZ_X11']:
         EXPORTS += [
+            'gfxXlibNativeRenderer.h',
             'gfxXlibSurface.h',
-            'gfxXlibNativeRenderer.h',
         ]
 
     if CONFIG['MOZ_PANGO']:
         EXPORTS += ['gfxPangoFonts.h']
     else:
         EXPORTS += ['gfxFT2Fonts.h']
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'os2':
     EXPORTS += [
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -208,16 +208,17 @@ CPPSRCS += 	MethodJIT.cpp \
 		$(NULL)
 
 # Ion
 ifdef ENABLE_ION
 VPATH +=	$(srcdir)/ion
 VPATH +=	$(srcdir)/ion/shared
 
 CPPSRCS +=	MIR.cpp \
+		BytecodeAnalysis.cpp \
 		BaselineCompiler.cpp \
 		BaselineIC.cpp \
 		BaselineFrame.cpp \
 		BaselineFrameInfo.cpp \
 		BaselineJIT.cpp \
 		BaselineInspector.cpp \
 		BaselineBailouts.cpp \
 		BacktrackingAllocator.cpp \
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -23,16 +23,23 @@ function indirectCallCannotGC(caller, na
 
     if (name == "params" && caller == "PR_ExplodeTime")
         return true;
 
     // hook called during script finalization which cannot GC.
     if (/CallDestroyScriptHook/.test(caller))
         return true;
 
+    // template method called during marking and hence cannot GC
+    if (name == "op" &&
+        /^bool js::WeakMap<Key, Value, HashPolicy>::keyNeedsMark\(JSObject\*\)/.test(caller))
+    {
+        return true;
+    }
+
     return false;
 }
 
 // Ignore calls through functions pointers with these types
 var ignoreClasses = {
     "JSTracer" : true,
     "JSStringFinalizer" : true,
     "SprintfStateStr" : true,
--- a/js/src/devtools/rootAnalysis/computeGCTypes.js
+++ b/js/src/devtools/rootAnalysis/computeGCTypes.js
@@ -14,17 +14,17 @@ function processCSU(csu, body)
         if (type.Kind == "Pointer") {
             var target = type.Type;
             if (target.Kind == "CSU")
                 addNestedPointer(csu, target.Name);
         }
         if (type.Kind == "CSU") {
             // Ignore nesting in classes which are AutoGCRooters. We only consider
             // types with fields that may not be properly rooted.
-            if (type.Name == "JS::AutoGCRooter")
+            if (type.Name == "JS::AutoGCRooter" || type.Name == "JS::CustomAutoRooter")
                 return;
             addNestedStructure(csu, type.Name);
         }
     }
 }
 
 function addNestedStructure(csu, inner)
 {
--- a/js/src/ion/BaselineCompiler.cpp
+++ b/js/src/ion/BaselineCompiler.cpp
@@ -24,16 +24,19 @@ BaselineCompiler::BaselineCompiler(JSCon
   : BaselineCompilerSpecific(cx, script),
     return_(new HeapLabel())
 {
 }
 
 bool
 BaselineCompiler::init()
 {
+    if (!analysis_.init())
+        return false;
+
     if (!labels_.init(script->length))
         return false;
 
     for (size_t i = 0; i < script->length; i++)
         new (&labels_[i]) Label();
 
     if (!frame.init())
         return false;
@@ -59,18 +62,22 @@ BaselineCompiler::addPCMappingEntry(bool
 }
 
 MethodStatus
 BaselineCompiler::compile()
 {
     IonSpew(IonSpew_BaselineScripts, "Baseline compiling script %s:%d (%p)",
             script->filename(), script->lineno, script.get());
 
-    if (!script->ensureRanAnalysis(cx))
-        return Method_Error;
+    // Only need to analyze scripts which are marked |argumensHasVarBinding|, to
+    // compute |needsArgsObj| flag.
+    if (script->argumentsHasVarBinding()) {
+        if (!script->ensureRanAnalysis(cx))
+            return Method_Error;
+    }
 
     // Pin analysis info during compilation.
     types::AutoEnterAnalysis autoEnterAnalysis(cx);
 
     if (!emitPrologue())
         return Method_Error;
 
     MethodStatus status = emitBody();
@@ -514,17 +521,17 @@ BaselineCompiler::emitSPSPush()
 
 void
 BaselineCompiler::emitSPSPop()
 {
     // If profiler entry was pushed on this frame, pop it.
     Label noPop;
     masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
                       Imm32(BaselineFrame::HAS_PUSHED_SPS_FRAME), &noPop);
-    masm.spsPopFrame(&cx->runtime->spsProfiler, R1.scratchReg());
+    masm.spsPopFrameSafe(&cx->runtime->spsProfiler, R1.scratchReg());
     masm.bind(&noPop);
 }
 
 MethodStatus
 BaselineCompiler::emitBody()
 {
     JS_ASSERT(pc == script->code);
 
@@ -532,31 +539,31 @@ BaselineCompiler::emitBody()
     uint32_t emittedOps = 0;
 
     while (true) {
         SPEW_OPCODE();
         JSOp op = JSOp(*pc);
         IonSpew(IonSpew_BaselineOp, "Compiling op @ %d: %s",
                 int(pc - script->code), js_CodeName[op]);
 
-        analyze::Bytecode *code = script->analysis()->maybeCode(pc);
+        BytecodeInfo *info = analysis_.maybeInfo(pc);
 
         // Skip unreachable ops.
-        if (!code) {
+        if (!info) {
             if (op == JSOP_STOP)
                 break;
             pc += GetBytecodeLength(pc);
             lastOpUnreachable = true;
             continue;
         }
 
         // Fully sync the stack if there are incoming jumps.
-        if (code->jumpTarget) {
+        if (info->jumpTarget) {
             frame.syncStack(0);
-            frame.setStackDepth(code->stackDepth);
+            frame.setStackDepth(info->stackDepth);
         }
 
         // Always sync in debug mode.
         if (debugMode_)
             frame.syncStack(0);
 
         // At the beginning of any op, at most the top 2 stack-values are unsynced.
         if (frame.stackDepth() > 2)
@@ -2414,17 +2421,17 @@ static const VMFunction NewArgumentsObje
     FunctionInfo<NewArgumentsObjectFn>(ion::NewArgumentsObject);
 
 bool
 BaselineCompiler::emit_JSOP_ARGUMENTS()
 {
     frame.syncStack(0);
 
     Label done;
-    if (!script->needsArgsObj()) {
+    if (!script->argumentsHasVarBinding() || !script->needsArgsObj()) {
         // We assume the script does not need an arguments object. However, this
         // assumption can be invalidated later, see argumentsOptimizationFailed
         // in JSScript. Because we can't invalidate baseline JIT code, we set a
         // flag on BaselineScript when that happens and guard on it here.
         masm.moveValue(MagicValue(JS_OPTIMIZED_ARGUMENTS), R0);
 
         // Load script->baseline.
         Register scratch = R1.scratchReg();
--- a/js/src/ion/BaselineCompiler.h
+++ b/js/src/ion/BaselineCompiler.h
@@ -8,19 +8,21 @@
 #define jsion_baseline_compiler_h__
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "IonCode.h"
 #include "jsinfer.h"
 #include "jsinterp.h"
 
+#include "IonAllocPolicy.h"
 #include "BaselineJIT.h"
 #include "BaselineIC.h"
 #include "FixedList.h"
+#include "BytecodeAnalysis.h"
 
 #if defined(JS_CPU_X86)
 # include "x86/BaselineCompiler-x86.h"
 #elif defined(JS_CPU_X64)
 # include "x64/BaselineCompiler-x64.h"
 #else
 # include "arm/BaselineCompiler-arm.h"
 #endif
--- a/js/src/ion/BaselineFrameInfo.cpp
+++ b/js/src/ion/BaselineFrameInfo.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "BaselineFrameInfo.h"
 #include "IonSpewer.h"
+#include "shared/BaselineCompiler-shared.h"
 
 #include "jsanalyze.h"
 #include "jsinferinlines.h"
 
 using namespace js;
 using namespace js::ion;
 
 bool
@@ -142,18 +143,18 @@ FrameInfo::popRegsAndSync(uint32_t uses)
     }
 }
 
 #ifdef DEBUG
 void
 FrameInfo::assertValidState(jsbytecode *pc)
 {
     // Check stack depth.
-    analyze::Bytecode *code = script->analysis()->maybeCode(pc);
-    JS_ASSERT_IF(code, stackDepth() == code->stackDepth);
+    BytecodeInfo *info = compiler.analysis().maybeInfo(pc);
+    JS_ASSERT_IF(info, stackDepth() == info->stackDepth);
 
     // Start at the bottom, find the first value that's not synced.
     uint32_t i = 0;
     for (; i < stackDepth(); i++) {
         if (stack[i].kind() != StackValue::Stack)
             break;
     }
 
--- a/js/src/ion/BaselineFrameInfo.h
+++ b/js/src/ion/BaselineFrameInfo.h
@@ -8,16 +8,17 @@
 #define jsion_baseline_frameinfo_h__
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "BaselineJIT.h"
 #include "BaselineFrame.h"
 #include "BaselineRegisters.h"
+#include "BytecodeAnalysis.h"
 #include "IonMacroAssembler.h"
 #include "FixedList.h"
 
 namespace js {
 namespace ion {
 
 // FrameInfo overview.
 //
@@ -153,27 +154,32 @@ class StackValue
     void setStack() {
         kind_ = Stack;
         knownType_ = JSVAL_TYPE_UNKNOWN;
     }
 };
 
 enum StackAdjustment { AdjustStack, DontAdjustStack };
 
+class BaselineCompilerShared;
+
 class FrameInfo
 {
+    BaselineCompilerShared &compiler;
     RootedScript script;
     MacroAssembler &masm;
 
     FixedList<StackValue> stack;
     size_t spIndex;
 
   public:
-    FrameInfo(JSContext *cx, HandleScript script, MacroAssembler &masm)
-      : script(cx, script),
+    FrameInfo(JSContext *cx, BaselineCompilerShared &compiler, HandleScript script,
+              MacroAssembler &masm)
+      : compiler(compiler),
+        script(cx, script),
         masm(masm),
         stack(),
         spIndex(0)
     { }
 
     bool init();
 
     uint32_t nlocals() const {
new file mode 100644
--- /dev/null
+++ b/js/src/ion/BytecodeAnalysis.cpp
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "ion/BytecodeAnalysis.h"
+#include "jsopcode.h"
+#include "jsopcodeinlines.h"
+
+using namespace js;
+using namespace js::ion;
+
+BytecodeAnalysis::BytecodeAnalysis(JSScript *script)
+  : script_(script),
+    infos_()
+{
+}
+
+bool
+BytecodeAnalysis::init()
+{
+    if (!infos_.growByUninitialized(script_->length))
+        return false;
+
+    jsbytecode *end = script_->code + script_->length;
+
+    // Clear all BytecodeInfo.
+    mozilla::PodZero(infos_.begin(), infos_.length());
+    infos_[0].init(/*stackDepth=*/0);
+
+    for (jsbytecode *pc = script_->code; pc < end; pc += GetBytecodeLength(pc)) {
+        JSOp op = JSOp(*pc);
+        unsigned offset = pc - script_->code;
+
+        IonSpew(IonSpew_BaselineOp, "Analyzing op @ %d (end=%d): %s",
+                int(pc - script_->code), int(end - script_->code), js_CodeName[op]);
+
+        // If this bytecode info has not yet been initialized, it's not reachable.
+        if (!infos_[offset].initialized)
+            continue;
+
+
+        unsigned stackDepth = infos_[offset].stackDepth;
+#ifdef DEBUG
+        for (jsbytecode *chkpc = pc + 1; chkpc < (pc + GetBytecodeLength(pc)); chkpc++)
+            JS_ASSERT(!infos_[chkpc - script_->code].initialized);
+#endif
+
+        // Treat decompose ops as no-ops which do not adjust the stack. We will
+        // pick up the stack depths as we go through the decomposed version.
+        if (!(js_CodeSpec[op].format & JOF_DECOMPOSE)) {
+            unsigned nuses = GetUseCount(script_, offset);
+            unsigned ndefs = GetDefCount(script_, offset);
+
+            JS_ASSERT(stackDepth >= nuses);
+            stackDepth -= nuses;
+            stackDepth += ndefs;
+
+            // If stack depth exceeds max allowed by analysis, fail fast.
+            JS_ASSERT(stackDepth <= BytecodeInfo::MAX_STACK_DEPTH);
+        }
+
+        if (op == JSOP_TABLESWITCH) {
+            unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
+            jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
+            int32_t low = GET_JUMP_OFFSET(pc2);
+            pc2 += JUMP_OFFSET_LEN;
+            int32_t high = GET_JUMP_OFFSET(pc2);
+            pc2 += JUMP_OFFSET_LEN;
+
+            infos_[defaultOffset].init(stackDepth);
+            infos_[defaultOffset].jumpTarget = true;
+
+            for (int32_t i = low; i <= high; i++) {
+                unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
+                if (targetOffset != offset) {
+                    infos_[targetOffset].init(stackDepth);
+                    infos_[targetOffset].jumpTarget = true;
+                }
+                pc2 += JUMP_OFFSET_LEN;
+            }
+        } else if (op == JSOP_TRY) {
+            JSTryNote *tn = script_->trynotes()->vector;
+            JSTryNote *tnlimit = tn + script_->trynotes()->length;
+            for (; tn < tnlimit; tn++) {
+                unsigned startOffset = script_->mainOffset + tn->start;
+                if (startOffset == offset + 1) {
+                    unsigned catchOffset = startOffset + tn->length;
+
+                    if (tn->kind != JSTRY_ITER) {
+                        infos_[catchOffset].init(stackDepth);
+                        infos_[catchOffset].jumpTarget = true;
+                    }
+                }
+            }
+        }
+
+        bool jump = IsJumpOpcode(op);
+        if (jump) {
+            // Case instructions do not push the lvalue back when branching.
+            unsigned newStackDepth = stackDepth;
+            if (op == JSOP_CASE)
+                newStackDepth--;
+
+            unsigned targetOffset = offset + GET_JUMP_OFFSET(pc);
+
+            // If this is a a backedge to an un-analyzed segment, analyze from there.
+            bool jumpBack = (targetOffset < offset) && !infos_[targetOffset].initialized;
+
+            infos_[targetOffset].init(newStackDepth);
+            infos_[targetOffset].jumpTarget = true;
+
+            if (jumpBack)
+                pc = script_->code + targetOffset;
+        }
+
+        // Handle any fallthrough from this opcode.
+        if (BytecodeFallsThrough(op)) {
+            jsbytecode *nextpc = pc + GetBytecodeLength(pc);
+            JS_ASSERT(nextpc < end);
+            unsigned nextOffset = nextpc - script_->code;
+
+            infos_[nextOffset].init(stackDepth);
+
+            if (jump)
+                infos_[nextOffset].jumpFallthrough = true;
+
+            // Treat the fallthrough of a branch instruction as a jump target.
+            if (jump)
+                infos_[nextOffset].jumpTarget = true;
+            else
+                infos_[nextOffset].fallthrough = true;
+        }
+    }
+
+    return true;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/ion/BytecodeAnalysis.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 jsion_bytecodeanalysis_h__
+#define jsion_bytecodeanalysis_h__
+
+#include "jscntxt.h"
+
+#include "IonAllocPolicy.h"
+#include "js/Vector.h"
+
+namespace js {
+namespace ion {
+
+
+// Basic information about bytecodes in the script.  Used to help baseline compilation.
+struct BytecodeInfo
+{
+    static const uint16_t MAX_STACK_DEPTH = 0xffffU;
+    uint16_t stackDepth;
+    bool initialized : 1;
+    bool jumpTarget : 1;
+    bool jumpFallthrough : 1;
+    bool fallthrough : 1;
+
+    void init(unsigned depth) {
+        JS_ASSERT(depth <= MAX_STACK_DEPTH);
+        JS_ASSERT_IF(initialized, stackDepth == depth);
+        initialized = true;
+        stackDepth = depth;
+    }
+};
+
+class BytecodeAnalysis
+{
+    JSScript *script_;
+    Vector<BytecodeInfo, 0, IonAllocPolicy> infos_;
+
+  public:
+    BytecodeAnalysis(JSScript *script);
+
+    bool init();
+
+    BytecodeInfo &info(jsbytecode *pc) {
+        JS_ASSERT(infos_[pc - script_->code].initialized);
+        return infos_[pc - script_->code];
+    }
+
+    BytecodeInfo *maybeInfo(jsbytecode *pc) {
+        if (infos_[pc - script_->code].initialized)
+            return &infos_[pc - script_->code];
+        return NULL;
+    }
+};
+
+
+} // namespace ion
+} // namespace js
+
+#endif // jsion_bytecodeanalysis_h__
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -490,18 +490,22 @@ HandleException(ResumeFromException *rfe
             bool calledDebugEpilogue = false;
 
             HandleException(cx, iter, rfe, &calledDebugEpilogue);
             if (rfe->kind != ResumeFromException::RESUME_ENTRY_FRAME)
                 return;
 
             // Unwind profiler pseudo-stack
             JSScript *script = iter.script();
-            Probes::exitScript(cx, script, script->function(), NULL);
-
+            Probes::exitScript(cx, script, script->function(), iter.baselineFrame());
+            // After this point, any pushed SPS frame would have been popped if it needed
+            // to be.  Unset the flag here so that if we call DebugEpilogue below,
+            // it doesn't try to pop the SPS frame again.
+            iter.baselineFrame()->unsetPushedSPSFrame();
+ 
             if (cx->compartment->debugMode() && !calledDebugEpilogue) {
                 // If DebugEpilogue returns |true|, we have to perform a forced
                 // return, e.g. return frame->returnValue() to the caller.
                 BaselineFrame *frame = iter.baselineFrame();
                 if (ion::DebugEpilogue(cx, frame, false)) {
                     JS_ASSERT(frame->hasReturnValue());
                     rfe->kind = ResumeFromException::RESUME_FORCED_RETURN;
                     rfe->framePointer = iter.fp() - BaselineFrame::FramePointerOffset;
--- a/js/src/ion/IonMacroAssembler.h
+++ b/js/src/ion/IonMacroAssembler.h
@@ -738,32 +738,64 @@ class MacroAssembler : public MacroAssem
         branch32(Assembler::GreaterThanOrEqual, temp, Imm32(p->maxSize()), full);
 
         // 4 * sizeof(void*) * idx = idx << (2 + log(sizeof(void*)))
         JS_STATIC_ASSERT(sizeof(ProfileEntry) == 4 * sizeof(void*));
         lshiftPtr(Imm32(2 + (sizeof(void*) == 4 ? 2 : 3)), temp);
         addPtr(ImmWord(p->stack()), temp);
     }
 
+    // The safe version of the above method refrains from assuming that the fields
+    // of the SPSProfiler class are going to stay the same across different runs of
+    // the jitcode.  Ion can use the more efficient unsafe version because ion jitcode
+    // will not survive changes to to the profiler settings.  Baseline jitcode, however,
+    // can span these changes, so any hardcoded field values will be incorrect afterwards.
+    // All the sps-related methods used by baseline call |spsProfileEntryAddressSafe|.
+    void spsProfileEntryAddressSafe(SPSProfiler *p, int offset, Register temp,
+                                    Label *full)
+    {
+        movePtr(ImmWord(p->addressOfSizePointer()), temp);
+
+        // Load size pointer
+        loadPtr(Address(temp, 0), temp);
+
+        // Load size
+        load32(Address(temp, 0), temp);
+        if (offset != 0)
+            add32(Imm32(offset), temp);
+
+        // Test against max size.
+        branch32(Assembler::LessThanOrEqual, AbsoluteAddress(p->addressOfMaxSize()), temp, full);
+
+        // 4 * sizeof(void*) * idx = idx << (2 + log(sizeof(void*)))
+        JS_STATIC_ASSERT(sizeof(ProfileEntry) == 4 * sizeof(void*));
+        lshiftPtr(Imm32(2 + (sizeof(void*) == 4 ? 2 : 3)), temp);
+        push(temp);
+        movePtr(ImmWord(p->addressOfStack()), temp);
+        loadPtr(Address(temp, 0), temp);
+        addPtr(Address(StackPointer, 0), temp);
+        addPtr(Imm32(sizeof(size_t)), StackPointer);
+    }
+
   public:
 
     // These functions are needed by the IonInstrumentation interface defined in
     // vm/SPSProfiler.h.  They will modify the pseudostack provided to SPS to
     // perform the actual instrumentation.
 
     void spsUpdatePCIdx(SPSProfiler *p, int32_t idx, Register temp) {
         Label stackFull;
         spsProfileEntryAddress(p, -1, temp, &stackFull);
         store32(Imm32(idx), Address(temp, ProfileEntry::offsetOfPCIdx()));
         bind(&stackFull);
     }
 
     void spsUpdatePCIdx(SPSProfiler *p, Register idx, Register temp) {
         Label stackFull;
-        spsProfileEntryAddress(p, -1, temp, &stackFull);
+        spsProfileEntryAddressSafe(p, -1, temp, &stackFull);
         store32(idx, Address(temp, ProfileEntry::offsetOfPCIdx()));
         bind(&stackFull);
     }
 
     void spsPushFrame(SPSProfiler *p, const char *str, JSScript *s, Register temp) {
         Label stackFull;
         spsProfileEntryAddress(p, 0, temp, &stackFull);
 
@@ -777,42 +809,50 @@ class MacroAssembler : public MacroAssem
         movePtr(ImmWord(p->sizePointer()), temp);
         add32(Imm32(1), Address(temp, 0));
     }
 
     void spsPushFrame(SPSProfiler *p, const Address &str, const Address &script,
                       Register temp, Register temp2)
     {
         Label stackFull;
-        spsProfileEntryAddress(p, 0, temp, &stackFull);
+        spsProfileEntryAddressSafe(p, 0, temp, &stackFull);
 
         loadPtr(str, temp2);
         storePtr(temp2, Address(temp, ProfileEntry::offsetOfString()));
 
         loadPtr(script, temp2);
         storePtr(temp2, Address(temp, ProfileEntry::offsetOfScript()));
 
         storePtr(ImmWord((void*) 0), Address(temp, ProfileEntry::offsetOfStackAddress()));
 
         // Store 0 for PCIdx because that's what interpreter does.
         // (See Probes::enterScript, which calls spsProfiler.enter, which pushes an entry
         //  with 0 pcIdx).
         store32(Imm32(0), Address(temp, ProfileEntry::offsetOfPCIdx()));
 
         /* Always increment the stack size, whether or not we actually pushed. */
         bind(&stackFull);
-        movePtr(ImmWord(p->sizePointer()), temp);
+        movePtr(ImmWord(p->addressOfSizePointer()), temp);
+        loadPtr(Address(temp, 0), temp);
         add32(Imm32(1), Address(temp, 0));
     }
 
     void spsPopFrame(SPSProfiler *p, Register temp) {
         movePtr(ImmWord(p->sizePointer()), temp);
         add32(Imm32(-1), Address(temp, 0));
     }
 
+    // spsPropFrameSafe does not assume |profiler->sizePointer()| will stay constant.
+    void spsPopFrameSafe(SPSProfiler *p, Register temp) {
+        movePtr(ImmWord(p->addressOfSizePointer()), temp);
+        loadPtr(Address(temp, 0), temp);
+        add32(Imm32(-1), Address(temp, 0));
+    }
+
     void loadBaselineOrIonRaw(Register script, Register dest, ExecutionMode mode, Label *failure);
 
     void loadBaselineFramePtr(Register framePtr, Register dest);
 
     void pushBaselineFramePtr(Register framePtr, Register scratch) {
         loadBaselineFramePtr(framePtr, scratch);
         push(scratch);
     }
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -637,19 +637,29 @@ DebugEpilogue(JSContext *cx, BaselineFra
     if (frame->isNonEvalFunctionFrame()) {
         JS_ASSERT_IF(ok, frame->hasReturnValue());
         DebugScopes::onPopCall(frame, cx);
     } else if (frame->isStrictEvalFrame()) {
         JS_ASSERT_IF(frame->hasCallObj(), frame->scopeChain()->asCall().isForEval());
         DebugScopes::onPopStrictEvalScope(frame);
     }
 
+    // If the frame has a pushed SPS frame, make sure to pop it.
+    if (frame->hasPushedSPSFrame()) {
+        cx->runtime->spsProfiler.exit(cx, frame->script(), frame->maybeFun());
+        // Unset the pushedSPSFrame flag because DebugEpilogue may get called before
+        // Probes::exitScript in baseline during exception handling, and we don't
+        // want to double-pop SPS frames.
+        frame->unsetPushedSPSFrame();
+    }
+
     if (!ok) {
         // Pop this frame by updating ionTop, so that the exception handling
         // code will start at the previous frame.
+
         IonJSFrameLayout *prefix = frame->framePrefix();
         EnsureExitFrame(prefix);
         cx->mainThread().ionTop = (uint8_t *)prefix;
     }
 
     return ok;
 }
 
--- a/js/src/ion/arm/MacroAssembler-arm.h
+++ b/js/src/ion/arm/MacroAssembler-arm.h
@@ -891,16 +891,21 @@ class MacroAssemblerARMCompat : public M
         ma_cmp(secondScratchReg_, ptr);
         ma_b(label, cond);
     }
     void branch32(Condition cond, const AbsoluteAddress &lhs, Imm32 rhs, Label *label) {
         loadPtr(lhs, secondScratchReg_); // ma_cmp will use the scratch register.
         ma_cmp(secondScratchReg_, rhs);
         ma_b(label, cond);
     }
+    void branch32(Condition cond, const AbsoluteAddress &lhs, const Register &rhs, Label *label) {
+        loadPtr(lhs, secondScratchReg_); // ma_cmp will use the scratch register.
+        ma_cmp(secondScratchReg_, rhs);
+        ma_b(label, cond);
+    }
 
     void loadUnboxedValue(Address address, MIRType type, AnyRegister dest) {
         if (dest.isFloat())
             loadInt32OrDouble(Operand(address), dest.fpu());
         else
             ma_ldr(address, dest.gpr());
     }
 
--- a/js/src/ion/shared/BaselineCompiler-shared.cpp
+++ b/js/src/ion/shared/BaselineCompiler-shared.cpp
@@ -13,17 +13,18 @@ using namespace js::ion;
 
 BaselineCompilerShared::BaselineCompilerShared(JSContext *cx, HandleScript script)
   : cx(cx),
     script(cx, script),
     pc(script->code),
     ionCompileable_(ion::IsEnabled(cx) && CanIonCompileScript(cx, script, false)),
     ionOSRCompileable_(ion::IsEnabled(cx) && CanIonCompileScript(cx, script, true)),
     debugMode_(cx->compartment->debugMode()),
-    frame(cx, script, masm),
+    analysis_(script),
+    frame(cx, *this, script, masm),
     stubSpace_(),
     icEntries_(),
     pcMappingEntries_(),
     icLoadLabels_(),
     pushedBeforeCall_(0),
     inCall_(false),
     spsPushToggleOffset_()
 { }
--- a/js/src/ion/shared/BaselineCompiler-shared.h
+++ b/js/src/ion/shared/BaselineCompiler-shared.h
@@ -23,16 +23,17 @@ class BaselineCompilerShared
     JSContext *cx;
     RootedScript script;
     jsbytecode *pc;
     MacroAssembler masm;
     bool ionCompileable_;
     bool ionOSRCompileable_;
     bool debugMode_;
 
+    BytecodeAnalysis analysis_;
     FrameInfo frame;
 
     FallbackICStubSpace stubSpace_;
     js::Vector<ICEntry, 16, SystemAllocPolicy> icEntries_;
 
     // Stores the native code offset for a bytecode pc.
     struct PCMappingEntry
     {
@@ -124,14 +125,19 @@ class BaselineCompilerShared
         // Ensure everything is synced.
         frame.syncStack(0);
 
         // Save the frame pointer.
         masm.Push(BaselineFrameReg);
     }
 
     bool callVM(const VMFunction &fun);
+
+  public:
+    BytecodeAnalysis &analysis() {
+        return analysis_;
+    }
 };
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_baselinecompiler_shared_h__
--- a/js/src/ion/x64/MacroAssembler-x64.h
+++ b/js/src/ion/x64/MacroAssembler-x64.h
@@ -427,16 +427,20 @@ class MacroAssemblerX64 : public MacroAs
     void subPtr(const Address &addr, const Register &dest) {
         subq(Operand(addr), dest);
     }
 
     void branch32(Condition cond, const AbsoluteAddress &lhs, Imm32 rhs, Label *label) {
         mov(ImmWord(lhs.addr), ScratchReg);
         branch32(cond, Address(ScratchReg, 0), rhs, label);
     }
+    void branch32(Condition cond, const AbsoluteAddress &lhs, Register rhs, Label *label) {
+        mov(ImmWord(lhs.addr), ScratchReg);
+        branch32(cond, Address(ScratchReg, 0), rhs, label);
+    }
 
     // Specialization for AbsoluteAddress.
     void branchPtr(Condition cond, const AbsoluteAddress &addr, const Register &ptr, Label *label) {
         JS_ASSERT(ptr != ScratchReg);
         mov(ImmWord(addr.addr), ScratchReg);
         branchPtr(cond, Operand(ScratchReg, 0x0), ptr, label);
     }
 
--- a/js/src/ion/x86/MacroAssembler-x86.h
+++ b/js/src/ion/x86/MacroAssembler-x86.h
@@ -451,16 +451,20 @@ class MacroAssemblerX86 : public MacroAs
     void subPtr(const Address &addr, const Register &dest) {
         subl(Operand(addr), dest);
     }
 
     void branch32(Condition cond, const AbsoluteAddress &lhs, Imm32 rhs, Label *label) {
         cmpl(Operand(lhs), rhs);
         j(cond, label);
     }
+    void branch32(Condition cond, const AbsoluteAddress &lhs, Register rhs, Label *label) {
+        cmpl(Operand(lhs), rhs);
+        j(cond, label);
+    }
 
     template <typename T, typename S>
     void branchPtr(Condition cond, T lhs, S ptr, Label *label) {
         cmpl(Operand(lhs), ptr);
         j(cond, label);
     }
 
     template <typename T>
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -34,28 +34,16 @@ analyze::PrintBytecode(JSContext *cx, Ha
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return;
     js_Disassemble1(cx, script, pc, pc - script->code, true, &sprinter);
     fprintf(stdout, "%s", sprinter.string());
 }
 #endif
 
-static inline bool
-IsJumpOpcode(JSOp op)
-{
-    uint32_t type = JOF_TYPE(js_CodeSpec[op].format);
-
-    /*
-     * LABEL opcodes have type JOF_JUMP but are no-ops, don't treat them as
-     * jumps to avoid degrading precision.
-     */
-    return type == JOF_JUMP && op != JSOP_LABEL;
-}
-
 /////////////////////////////////////////////////////////////////////
 // Bytecode Analysis
 /////////////////////////////////////////////////////////////////////
 
 inline bool
 ScriptAnalysis::addJump(JSContext *cx, unsigned offset,
                         unsigned *currentOffset, unsigned *forwardJump, unsigned *forwardLoop,
                         unsigned stackDepth)
@@ -585,17 +573,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
             if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, &forwardLoop, newStackDepth))
                 return;
 
             if (op == JSOP_CASE || op == JSOP_DEFAULT)
                 getCode(targetOffset).safePoint = true;
         }
 
         /* Handle any fallthrough from this opcode. */
-        if (!BytecodeNoFallThrough(op)) {
+        if (BytecodeFallsThrough(op)) {
             JS_ASSERT(successorOffset < script_->length);
 
             Bytecode *&nextcode = codeArray[successorOffset];
 
             if (!nextcode) {
                 nextcode = alloc.new_<Bytecode>();
                 if (!nextcode) {
                     setOOM(cx);
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -12,16 +12,17 @@
 #include "mozilla/PodOperations.h"
 #include "mozilla/TypeTraits.h"
 
 #include "jsautooplen.h"
 #include "jscompartment.h"
 #include "jscntxt.h"
 #include "jsinfer.h"
 #include "jsscript.h"
+#include "jsopcodeinlines.h"
 
 #include "ds/LifoAlloc.h"
 #include "js/TemplateLib.h"
 #include "vm/ScopeObject.h"
 
 class JSScript;
 
 /* Forward declaration of downstream register allocations computed for join points. */
@@ -166,56 +167,16 @@ class Bytecode
 
     /* Types for all values pushed by this bytecode. */
     types::StackTypeSet *pushedTypes;
 
     /* Any type barriers in place at this bytecode. */
     types::TypeBarrier *typeBarriers;
 };
 
-static inline unsigned
-GetDefCount(JSScript *script, unsigned offset)
-{
-    JS_ASSERT(offset < script->length);
-    jsbytecode *pc = script->code + offset;
-
-    /*
-     * Add an extra pushed value for OR/AND opcodes, so that they are included
-     * in the pushed array of stack values for type inference.
-     */
-    switch (JSOp(*pc)) {
-      case JSOP_OR:
-      case JSOP_AND:
-        return 1;
-      case JSOP_PICK:
-        /*
-         * Pick pops and pushes how deep it looks in the stack + 1
-         * items. i.e. if the stack were |a b[2] c[1] d[0]|, pick 2
-         * would pop b, c, and d to rearrange the stack to |a c[0]
-         * d[1] b[2]|.
-         */
-        return (pc[1] + 1);
-      default:
-        return StackDefs(script, pc);
-    }
-}
-
-static inline unsigned
-GetUseCount(JSScript *script, unsigned offset)
-{
-    JS_ASSERT(offset < script->length);
-    jsbytecode *pc = script->code + offset;
-
-    if (JSOp(*pc) == JSOP_PICK)
-        return (pc[1] + 1);
-    if (js_CodeSpec[*pc].nuses == -1)
-        return StackUses(script, pc);
-    return js_CodeSpec[*pc].nuses;
-}
-
 /*
  * For opcodes which assign to a local variable or argument, track an extra def
  * during SSA analysis for the value's use chain and assigned type.
  */
 static inline bool
 ExtendedDef(jsbytecode *pc)
 {
     switch ((JSOp)*pc) {
@@ -230,37 +191,16 @@ ExtendedDef(jsbytecode *pc)
       case JSOP_LOCALINC:
       case JSOP_LOCALDEC:
         return true;
       default:
         return false;
     }
 }
 
-/* Return whether op bytecodes do not fallthrough (they may do a jump). */
-static inline bool
-BytecodeNoFallThrough(JSOp op)
-{
-    switch (op) {
-      case JSOP_GOTO:
-      case JSOP_DEFAULT:
-      case JSOP_RETURN:
-      case JSOP_STOP:
-      case JSOP_RETRVAL:
-      case JSOP_THROW:
-      case JSOP_TABLESWITCH:
-        return true;
-      case JSOP_GOSUB:
-        /* These fall through indirectly, after executing a 'finally'. */
-        return false;
-      default:
-        return false;
-    }
-}
-
 /*
  * For opcodes which access local variables or arguments, we track an extra
  * use during SSA analysis for the value of the variable before/after the op.
  */
 static inline bool
 ExtendedUse(jsbytecode *pc)
 {
     if (ExtendedDef(pc))
--- a/js/src/jsopcodeinlines.h
+++ b/js/src/jsopcodeinlines.h
@@ -8,16 +8,88 @@
 #define jsopcodeinlines_h__
 
 #include "jsautooplen.h"
 
 #include "frontend/BytecodeEmitter.h"
 
 namespace js {
 
+static inline unsigned
+GetDefCount(JSScript *script, unsigned offset)
+{
+    JS_ASSERT(offset < script->length);
+    jsbytecode *pc = script->code + offset;
+
+    /*
+     * Add an extra pushed value for OR/AND opcodes, so that they are included
+     * in the pushed array of stack values for type inference.
+     */
+    switch (JSOp(*pc)) {
+      case JSOP_OR:
+      case JSOP_AND:
+        return 1;
+      case JSOP_PICK:
+        /*
+         * Pick pops and pushes how deep it looks in the stack + 1
+         * items. i.e. if the stack were |a b[2] c[1] d[0]|, pick 2
+         * would pop b, c, and d to rearrange the stack to |a c[0]
+         * d[1] b[2]|.
+         */
+        return (pc[1] + 1);
+      default:
+        return StackDefs(script, pc);
+    }
+}
+
+static inline unsigned
+GetUseCount(JSScript *script, unsigned offset)
+{
+    JS_ASSERT(offset < script->length);
+    jsbytecode *pc = script->code + offset;
+
+    if (JSOp(*pc) == JSOP_PICK)
+        return (pc[1] + 1);
+    if (js_CodeSpec[*pc].nuses == -1)
+        return StackUses(script, pc);
+    return js_CodeSpec[*pc].nuses;
+}
+
+static inline bool
+IsJumpOpcode(JSOp op)
+{
+    uint32_t type = JOF_TYPE(js_CodeSpec[op].format);
+
+    /*
+     * LABEL opcodes have type JOF_JUMP but are no-ops, don't treat them as
+     * jumps to avoid degrading precision.
+     */
+    return type == JOF_JUMP && op != JSOP_LABEL;
+}
+
+static inline bool
+BytecodeFallsThrough(JSOp op)
+{
+    switch (op) {
+      case JSOP_GOTO:
+      case JSOP_DEFAULT:
+      case JSOP_RETURN:
+      case JSOP_STOP:
+      case JSOP_RETRVAL:
+      case JSOP_THROW:
+      case JSOP_TABLESWITCH:
+        return false;
+      case JSOP_GOSUB:
+        /* These fall through indirectly, after executing a 'finally'. */
+        return true;
+      default:
+        return true;
+    }
+}
+
 static inline PropertyName *
 GetNameFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, JSOp op)
 {
     if (op == JSOP_LENGTH)
         return cx->names().length;
 
     // The method JIT's implementation of instanceof contains an internal lookup
     // of the prototype property.
--- a/js/src/jsprobes.h
+++ b/js/src/jsprobes.h
@@ -73,16 +73,17 @@ bool callTrackingActive(JSContext *);
  * This information will not be collected otherwise.
  */
 bool wantNativeAddressInfo(JSContext *);
 
 /* Entering a JS function */
 bool enterScript(JSContext *, JSScript *, JSFunction *, StackFrame *);
 
 /* About to leave a JS function */
+bool exitScript(JSContext *, JSScript *, JSFunction *, AbstractFramePtr);
 bool exitScript(JSContext *, JSScript *, JSFunction *, StackFrame *);
 
 /* Executing a script */
 bool startExecution(JSScript *script);
 
 /* Script has completed execution */
 bool stopExecution(JSScript *script);
 
@@ -200,17 +201,17 @@ Probes::enterScript(JSContext *cx, JSScr
         fp->setPushedSPSFrame();
     }
 
     return ok;
 }
 
 inline bool
 Probes::exitScript(JSContext *cx, JSScript *script, JSFunction *maybeFun,
-                   StackFrame *fp)
+                   AbstractFramePtr fp)
 {
     bool ok = true;
 
 #ifdef INCLUDE_MOZILLA_DTRACE
     if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
         DTraceExitJSFun(cx, maybeFun, script);
 #endif
 #ifdef MOZ_TRACE_JSCALLS
@@ -218,24 +219,28 @@ Probes::exitScript(JSContext *cx, JSScri
 #endif
 
     JSRuntime *rt = cx->runtime;
     /*
      * Coming from IonMonkey, the fp might not be known (fp == NULL), but
      * IonMonkey will only call exitScript() when absolutely necessary, so it is
      * guaranteed that fp->hasPushedSPSFrame() would have been true
      */
-    if ((fp == NULL && rt->spsProfiler.enabled()) ||
-        (fp != NULL && fp->hasPushedSPSFrame()))
-    {
+    if ((!fp && rt->spsProfiler.enabled()) || (fp && fp.hasPushedSPSFrame()))
         rt->spsProfiler.exit(cx, script, maybeFun);
-    }
     return ok;
 }
 
+inline bool
+Probes::exitScript(JSContext *cx, JSScript *script, JSFunction *maybeFun,
+                   StackFrame *fp)
+{
+    return Probes::exitScript(cx, script, maybeFun, fp ? AbstractFramePtr(fp) : AbstractFramePtr());
+}
+
 #ifdef INCLUDE_MOZILLA_DTRACE
 static const char *ObjectClassname(JSObject *obj) {
     if (!obj)
         return "(null object)";
     Class *clasp = obj->getClass();
     if (!clasp)
         return "(null)";
     const char *class_name = clasp->name;
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -832,17 +832,17 @@ MakeJITScript(JSContext *cx, JSScript *s
             if (finishChunk && !preserveChunk) {
                 ChunkDescriptor desc;
                 desc.begin = chunkStart;
                 desc.end = nextOffset;
                 if (!chunks.append(desc))
                     return NULL;
 
                 /* Add an edge for fallthrough from this chunk to the next one. */
-                if (!BytecodeNoFallThrough(op)) {
+                if (BytecodeFallsThrough(op)) {
                     CrossChunkEdge edge;
                     edge.source = offset;
                     edge.target = nextOffset;
                     analysis->getCode(edge.target).safePoint = true;
                     if (!edges.append(edge))
                         return NULL;
                 }
 
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -29,37 +29,37 @@ if CONFIG['JS_STANDALONE']:
 
 CONFIGURE_SUBST_FILES += ['devtools/rootAnalysis/Makefile']
 
 # Changes to internal header files, used externally, massively slow down
 # browser builds.  Don't add new files here unless you know what you're
 # doing!
 EXPORTS += [
     'js-config.h',
-    'jscpucfg.h',
     'js.msg',
     'jsalloc.h',
     'jsapi.h',
     'jsclass.h',
     'jsclist.h',
+    'jscpucfg.h',
     'jsdbgapi.h',
     'jsdhash.h',
     'jsfriendapi.h',
     'jslock.h',
     'json.h',
-    'jsproxy.h',
+    'jsperf.h',
     'jsprf.h',
     'jsprototypes.h',
+    'jsproxy.h',
     'jsprvtd.h',
     'jspubtd.h',
     'jstypes.h',
     'jsutil.h',
     'jsversion.h',
     'jswrapper.h',
-    'jsperf.h',
 ]
 
 if CONFIG['HAVE_DTRACE']:
     EXPORTS += [
         '$(CURDIR)/javascript-trace.h',
     ]
 
 # If you add a header here, add it to js/src/jsapi-tests/testIntTypesABI.cpp so
--- a/js/src/vm/SPSProfiler.h
+++ b/js/src/vm/SPSProfiler.h
@@ -139,16 +139,28 @@ class SPSProfiler
                                    JSFunction *function);
     void push(const char *string, void *sp, JSScript *script, jsbytecode *pc);
     void pop();
 
   public:
     SPSProfiler(JSRuntime *rt);
     ~SPSProfiler();
 
+    uint32_t **addressOfSizePointer() {
+        return &size_;
+    }
+
+    uint32_t *addressOfMaxSize() {
+        return &max_;
+    }
+
+    ProfileEntry **addressOfStack() {
+        return &stack_;
+    }
+
     uint32_t *sizePointer() { return size_; }
     uint32_t maxSize() { return max_; }
     ProfileEntry *stack() { return stack_; }
 
     /* management of whether instrumentation is on or off */
     bool enabled() { JS_ASSERT_IF(enabled_, installed()); return enabled_; }
     bool installed() { return stack_ != NULL && size_ != NULL; }
     void enable(bool enabled);
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -622,16 +622,29 @@ AbstractFramePtr::setReturnValue(const V
     }
 #ifdef JS_ION
     asBaselineFrame()->setReturnValue(rval);
 #else
     JS_NOT_REACHED("Invalid frame");
 #endif
 }
 
+inline bool
+AbstractFramePtr::hasPushedSPSFrame() const
+{
+    if (isStackFrame())
+        return asStackFrame()->hasPushedSPSFrame();
+#ifdef JS_ION
+    return asBaselineFrame()->hasPushedSPSFrame();
+#else
+    JS_NOT_REACHED("Invalid frame");
+    return false;
+#endif
+}
+
 inline JSObject *
 AbstractFramePtr::scopeChain() const
 {
     if (isStackFrame())
         return asStackFrame()->scopeChain();
 #ifdef JS_ION
     return asBaselineFrame()->scopeChain();
 #else
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -326,16 +326,18 @@ class AbstractFramePtr
 
     JSObject *evalPrevScopeChain(JSRuntime *rt) const;
 
     inline void *maybeHookData() const;
     inline void setHookData(void *data) const;
     inline Value returnValue() const;
     inline void setReturnValue(const Value &rval) const;
 
+    inline bool hasPushedSPSFrame() const;
+
     inline void popBlock(JSContext *cx) const;
     inline void popWith(JSContext *cx) const;
 };
 
 class NullFramePtr : public AbstractFramePtr
 {
   public:
     NullFramePtr()
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -29,17 +29,16 @@
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/Preferences.h"
 #include "nsPrincipal.h"
 #include "mozilla/Attributes.h"
 #include "nsIScriptContext.h"
 #include "nsJSEnvironment.h"
 #include "nsXMLHttpRequest.h"
 #include "mozilla/Telemetry.h"
-#include "mozilla/XPTInterfaceInfoManager.h"
 #include "nsDOMClassInfoID.h"
 
 using namespace mozilla;
 using namespace js;
 using namespace xpc;
 
 using mozilla::dom::DestroyProtoAndIfaceCache;
 
@@ -116,17 +115,17 @@ public:
     NS_DECL_NSICLASSINFO
     NS_DECL_NSISECURITYCHECKEDCOMPONENT
 
 public:
     nsXPCComponents_Interfaces();
     virtual ~nsXPCComponents_Interfaces();
 
 private:
-    nsCOMArray<nsIInterfaceInfo> mInterfaces;
+    nsCOMPtr<nsIInterfaceInfoManager> mManager;
 };
 
 /* void getInterfaces (out uint32_t count, [array, size_is (count), retval]
                        out nsIIDPtr array); */
 NS_IMETHODIMP
 nsXPCComponents_Interfaces::GetInterfaces(uint32_t *aCount, nsIID * **aArray)
 {
     const uint32_t count = 3;
@@ -211,19 +210,19 @@ nsXPCComponents_Interfaces::GetFlags(uin
 
 /* [notxpcom] readonly attribute nsCID classIDNoAlloc; */
 NS_IMETHODIMP
 nsXPCComponents_Interfaces::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
 {
     return NS_ERROR_NOT_AVAILABLE;
 }
 
-nsXPCComponents_Interfaces::nsXPCComponents_Interfaces()
-{
-    XPTInterfaceInfoManager::GetSingleton()->GetScriptableInterfaces(mInterfaces);
+nsXPCComponents_Interfaces::nsXPCComponents_Interfaces() :
+    mManager(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID))
+{
 }
 
 nsXPCComponents_Interfaces::~nsXPCComponents_Interfaces()
 {
     // empty
 }
 
 
@@ -250,72 +249,99 @@ NS_IMPL_THREADSAFE_RELEASE(nsXPCComponen
 
 /* bool newEnumerate (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in uint32_t enum_op, in JSValPtr statep, out JSID idp); */
 NS_IMETHODIMP
 nsXPCComponents_Interfaces::NewEnumerate(nsIXPConnectWrappedNative *wrapper,
                                          JSContext * cx, JSObject * obj,
                                          uint32_t enum_op, jsval * statep,
                                          jsid * idp, bool *_retval)
 {
+    nsIEnumerator* e;
+
     switch (enum_op) {
         case JSENUMERATE_INIT:
         case JSENUMERATE_INIT_ALL:
         {
-            *statep = JSVAL_ZERO;
+            if (!mManager ||
+                NS_FAILED(mManager->EnumerateInterfaces(&e)) || !e ||
+                NS_FAILED(e->First()))
+
+            {
+                *statep = JSVAL_NULL;
+                return NS_ERROR_UNEXPECTED;
+            }
+
+            *statep = PRIVATE_TO_JSVAL(e);
             if (idp)
-                *idp = INT_TO_JSID(mInterfaces.Length());
+                *idp = INT_TO_JSID(0); // indicate that we don't know the count
             return NS_OK;
         }
         case JSENUMERATE_NEXT:
         {
-            uint32_t idx = JSVAL_TO_INT(*statep);
-            nsIInterfaceInfo* interface = mInterfaces.SafeElementAt(idx);
-            *statep = UINT_TO_JSVAL(idx + 1);
-
-            if (interface) {
-                JSString* idstr;
-                const char* name;
-
-                if (NS_SUCCEEDED(interface->GetNameShared(&name)) && name &&
-                        nullptr != (idstr = JS_NewStringCopyZ(cx, name)) &&
-                        JS_ValueToId(cx, STRING_TO_JSVAL(idstr), idp)) {
-                    return NS_OK;
+            nsCOMPtr<nsISupports> isup;
+
+            e = (nsIEnumerator*) JSVAL_TO_PRIVATE(*statep);
+
+            while (1) {
+                if (static_cast<nsresult>(NS_ENUMERATOR_FALSE) == e->IsDone() &&
+                    NS_SUCCEEDED(e->CurrentItem(getter_AddRefs(isup))) && isup) {
+                    e->Next();
+                    nsCOMPtr<nsIInterfaceInfo> iface(do_QueryInterface(isup));
+                    if (iface) {
+                        JSString* idstr;
+                        const char* name;
+                        bool scriptable;
+
+                        if (NS_SUCCEEDED(iface->IsScriptable(&scriptable)) &&
+                            !scriptable) {
+                            continue;
+                        }
+
+                        if (NS_SUCCEEDED(iface->GetNameShared(&name)) && name &&
+                            nullptr != (idstr = JS_NewStringCopyZ(cx, name)) &&
+                            JS_ValueToId(cx, STRING_TO_JSVAL(idstr), idp)) {
+                            return NS_OK;
+                        }
+                    }
                 }
+                // else...
+                break;
             }
-            // fall through
+            // FALL THROUGH
         }
 
         case JSENUMERATE_DESTROY:
         default:
+            e = (nsIEnumerator*) JSVAL_TO_PRIVATE(*statep);
+            NS_IF_RELEASE(e);
             *statep = JSVAL_NULL;
             return NS_OK;
     }
 }
 
 /* bool newResolve (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval id, in uint32_t flags, out JSObjectPtr objp); */
 NS_IMETHODIMP
 nsXPCComponents_Interfaces::NewResolve(nsIXPConnectWrappedNative *wrapper,
                                        JSContext *cx, JSObject *objArg,
                                        jsid idArg, uint32_t flags,
                                        JSObject **objp, bool *_retval)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
 
-    if (!JSID_IS_STRING(id))
+    if (!mManager || !JSID_IS_STRING(id))
         return NS_OK;
 
     JSAutoByteString name;
     RootedString str(cx, JSID_TO_STRING(id));
 
     // we only allow interfaces by name here
     if (name.encodeLatin1(cx, str) && name.ptr()[0] != '{') {
         nsCOMPtr<nsIInterfaceInfo> info;
-        XPTInterfaceInfoManager::GetSingleton()->
-            GetInfoForName(name.ptr(), getter_AddRefs(info));
+        mManager->GetInfoForName(name.ptr(), getter_AddRefs(info));
         if (!info)
             return NS_OK;
 
         nsCOMPtr<nsIJSIID> nsid =
             dont_AddRef(static_cast<nsIJSIID*>(nsJSIID::NewID(info)));
 
         if (nsid) {
             nsCOMPtr<nsIXPConnect> xpc;
@@ -397,17 +423,17 @@ public:
     NS_DECL_NSICLASSINFO
     NS_DECL_NSISECURITYCHECKEDCOMPONENT
 
 public:
     nsXPCComponents_InterfacesByID();
     virtual ~nsXPCComponents_InterfacesByID();
 
 private:
-    nsCOMArray<nsIInterfaceInfo> mInterfaces;
+    nsCOMPtr<nsIInterfaceInfoManager> mManager;
 };
 
 /***************************************************************************/
 /* void getInterfaces (out uint32_t count, [array, size_is (count), retval]
                        out nsIIDPtr array); */
 NS_IMETHODIMP
 nsXPCComponents_InterfacesByID::GetInterfaces(uint32_t *aCount, nsIID * **aArray)
 {
@@ -493,19 +519,19 @@ nsXPCComponents_InterfacesByID::GetFlags
 
 /* [notxpcom] readonly attribute nsCID classIDNoAlloc; */
 NS_IMETHODIMP
 nsXPCComponents_InterfacesByID::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
 {
     return NS_ERROR_NOT_AVAILABLE;
 }
 
-nsXPCComponents_InterfacesByID::nsXPCComponents_InterfacesByID()
-{
-    XPTInterfaceInfoManager::GetSingleton()->GetScriptableInterfaces(mInterfaces);
+nsXPCComponents_InterfacesByID::nsXPCComponents_InterfacesByID() :
+    mManager(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID))
+{
 }
 
 nsXPCComponents_InterfacesByID::~nsXPCComponents_InterfacesByID()
 {
     // empty
 }
 
 NS_INTERFACE_MAP_BEGIN(nsXPCComponents_InterfacesByID)
@@ -530,74 +556,107 @@ NS_IMPL_THREADSAFE_RELEASE(nsXPCComponen
 
 /* bool newEnumerate (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in uint32_t enum_op, in JSValPtr statep, out JSID idp); */
 NS_IMETHODIMP
 nsXPCComponents_InterfacesByID::NewEnumerate(nsIXPConnectWrappedNative *wrapper,
                                              JSContext * cx, JSObject * obj,
                                              uint32_t enum_op, jsval * statep,
                                              jsid * idp, bool *_retval)
 {
+    nsIEnumerator* e;
+
     switch (enum_op) {
         case JSENUMERATE_INIT:
         case JSENUMERATE_INIT_ALL:
         {
-            *statep = JSVAL_ZERO;
+            if (!mManager ||
+                NS_FAILED(mManager->EnumerateInterfaces(&e)) || !e ||
+                NS_FAILED(e->First()))
+
+            {
+                *statep = JSVAL_NULL;
+                return NS_ERROR_UNEXPECTED;
+            }
+
+            *statep = PRIVATE_TO_JSVAL(e);
             if (idp)
-                *idp = INT_TO_JSID(mInterfaces.Length());
+                *idp = INT_TO_JSID(0); // indicate that we don't know the count
             return NS_OK;
         }
         case JSENUMERATE_NEXT:
         {
-            uint32_t idx = JSVAL_TO_INT(*statep);
-            nsIInterfaceInfo* interface = mInterfaces.SafeElementAt(idx);
-            *statep = UINT_TO_JSVAL(idx + 1);
-            if (interface) {
-                nsIID const *iid;
-                char idstr[NSID_LENGTH];
-                JSString* jsstr;
-
-                if (NS_SUCCEEDED(interface->GetIIDShared(&iid))) {
-                    iid->ToProvidedString(idstr);
-                    jsstr = JS_NewStringCopyZ(cx, idstr);
-                    if (jsstr && JS_ValueToId(cx, STRING_TO_JSVAL(jsstr), idp)) {
-                        return NS_OK;
+            nsCOMPtr<nsISupports> isup;
+
+            e = (nsIEnumerator*) JSVAL_TO_PRIVATE(*statep);
+
+            while (1) {
+                if (static_cast<nsresult>(NS_ENUMERATOR_FALSE) == e->IsDone() &&
+                    NS_SUCCEEDED(e->CurrentItem(getter_AddRefs(isup))) && isup) {
+                    e->Next();
+                    nsCOMPtr<nsIInterfaceInfo> iface(do_QueryInterface(isup));
+                    if (iface) {
+                        nsIID const *iid;
+                        char idstr[NSID_LENGTH];
+                        JSString* jsstr;
+                        bool scriptable;
+
+                        if (NS_SUCCEEDED(iface->IsScriptable(&scriptable)) &&
+                            !scriptable) {
+                            continue;
+                        }
+
+                        if (NS_SUCCEEDED(iface->GetIIDShared(&iid))) {
+                            iid->ToProvidedString(idstr);
+                            jsstr = JS_NewStringCopyZ(cx, idstr);
+                            if (jsstr &&
+                                JS_ValueToId(cx, STRING_TO_JSVAL(jsstr), idp)) {
+                                return NS_OK;
+                            }
+                        }
                     }
                 }
+                // else...
+                break;
             }
             // FALL THROUGH
         }
 
         case JSENUMERATE_DESTROY:
         default:
+            e = (nsIEnumerator*) JSVAL_TO_PRIVATE(*statep);
+            NS_IF_RELEASE(e);
             *statep = JSVAL_NULL;
             return NS_OK;
     }
 }
 
 /* bool newResolve (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval id, in uint32_t flags, out JSObjectPtr objp); */
 NS_IMETHODIMP
 nsXPCComponents_InterfacesByID::NewResolve(nsIXPConnectWrappedNative *wrapper,
                                            JSContext *cx, JSObject *objArg,
                                            jsid idArg, uint32_t flags,
                                            JSObject **objp, bool *_retval)
 {
     RootedObject obj(cx, objArg);
     RootedId id(cx, idArg);
+
+    if (!mManager || !JSID_IS_STRING(id))
+        return NS_OK;
+
     RootedString str(cx, JSID_TO_STRING(id));
     if (38 != JS_GetStringLength(str))
         return NS_OK;
 
     if (const jschar *name = JS_GetInternedStringChars(str)) {
         nsID iid;
         if (!iid.Parse(NS_ConvertUTF16toUTF8(name).get()))
             return NS_OK;
 
         nsCOMPtr<nsIInterfaceInfo> info;
-        XPTInterfaceInfoManager::GetSingleton()->
-            GetInfoForIID(&iid, getter_AddRefs(info));
+        mManager->GetInfoForIID(&iid, getter_AddRefs(info));
         if (!info)
             return NS_OK;
 
         nsCOMPtr<nsIJSIID> nsid =
             dont_AddRef(static_cast<nsIJSIID*>(nsJSIID::NewID(info)));
 
         if (!nsid)
             return NS_ERROR_OUT_OF_MEMORY;
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -693,21 +693,20 @@ nsDocumentViewer::InitPresentationStuff(
                "Someone should have destroyed the presshell!");
 
   // Create the style set...
   nsStyleSet *styleSet;
   nsresult rv = CreateStyleSet(mDocument, &styleSet);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Now make the shell for the document
-  rv = mDocument->CreateShell(mPresContext, mViewManager, styleSet,
-                              getter_AddRefs(mPresShell));
-  if (NS_FAILED(rv)) {
+  mPresShell = mDocument->CreateShell(mPresContext, mViewManager, styleSet);
+  if (!mPresShell) {
     delete styleSet;
-    return rv;
+    return NS_ERROR_FAILURE;
   }
 
   // We're done creating the style set
   styleSet->EndUpdate();
 
   if (aDoInitialReflow) {
     // Since Initialize() will create frames for *all* items
     // that are currently in the document tree, we need to flush
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -172,22 +172,16 @@ protected:
   typedef mozilla::layers::LayerManager LayerManager;
 
   enum eRenderFlag {
     STATE_IGNORING_VIEWPORT_SCROLLING = 0x1
   };
   typedef uint8_t RenderFlags; // for storing the above flags
 
 public:
-  virtual NS_HIDDEN_(nsresult) Init(nsIDocument* aDocument,
-                                   nsPresContext* aPresContext,
-                                   nsViewManager* aViewManager,
-                                   nsStyleSet* aStyleSet,
-                                   nsCompatibility aCompatMode) = 0;
-
   /**
    * All callers are responsible for calling |Destroy| after calling
    * |EndObservingDocument|.  It needs to be separate only because form
    * controls incorrectly store their data in the frames rather than the
    * content model and printing calls |EndObservingDocument| multiple
    * times to make form controls behave nicely when printed.
    */
   virtual NS_HIDDEN_(void) Destroy() = 0;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -764,35 +764,33 @@ PresShell::~PresShell()
   NS_IF_RELEASE(mPresContext);
   NS_IF_RELEASE(mDocument);
   NS_IF_RELEASE(mSelection);
 }
 
 /**
  * Initialize the presentation shell. Create view manager and style
  * manager.
+ * Note this can't be merged into our constructor because caret initialization
+ * calls AddRef() on us.
  */
-nsresult
+void
 PresShell::Init(nsIDocument* aDocument,
                 nsPresContext* aPresContext,
                 nsViewManager* aViewManager,
                 nsStyleSet* aStyleSet,
                 nsCompatibility aCompatMode)
 {
-  NS_PRECONDITION(nullptr != aDocument, "null ptr");
-  NS_PRECONDITION(nullptr != aPresContext, "null ptr");
-  NS_PRECONDITION(nullptr != aViewManager, "null ptr");
-
-  if ((nullptr == aDocument) || (nullptr == aPresContext) ||
-      (nullptr == aViewManager)) {
-    return NS_ERROR_NULL_POINTER;
-  }
-  if (mDocument) {
-    NS_WARNING("PresShell double init'ed");
-    return NS_ERROR_ALREADY_INITIALIZED;
+  NS_PRECONDITION(aDocument, "null ptr");
+  NS_PRECONDITION(aPresContext, "null ptr");
+  NS_PRECONDITION(aViewManager, "null ptr");
+  NS_PRECONDITION(!mDocument, "already initialized");
+
+  if (!aDocument || !aPresContext || !aViewManager || mDocument) {
+    return;
   }
 
   mFramesToDirty.Init();
 
   mDocument = aDocument;
   NS_ADDREF(mDocument);
   mViewManager = aViewManager;
 
@@ -806,21 +804,16 @@ PresShell::Init(nsIDocument* aDocument,
 
   // Bind the context to the presentation shell.
   mPresContext = aPresContext;
   NS_ADDREF(mPresContext);
   aPresContext->SetShell(this);
 
   // Now we can initialize the style set.
   aStyleSet->Init(aPresContext);
-
-  // From this point on, any time we return an error we need to make
-  // sure to null out mStyleSet first, since an error return from this
-  // method will cause the caller to delete the style set, so we don't
-  // want to delete it in our destructor.
   mStyleSet = aStyleSet;
 
   // Notify our prescontext that it now has a compatibility mode.  Note that
   // this MUST happen after we set up our style set but before we create any
   // frames.
   mPresContext->CompatibilityModeChanged();
 
   // setup the preference style rules (no forced reflow), and do it
@@ -889,18 +882,16 @@ PresShell::Init(nsIDocument* aDocument,
     animCtrl->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
   }
 
   // Get our activeness from the docShell.
   QueryIsActive();
 
   // Setup our font inflation preferences.
   SetupFontInflation();
-
-  return NS_OK;
 }
 
 void
 PresShell::Destroy()
 {
   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
     "destroy called on presshell while scripts not blocked");
 
@@ -8564,19 +8555,18 @@ PresShell::VerifyIncrementalReflow()
   // Make the new presentation context the same size as our
   // presentation context.
   nsRect r = mPresContext->GetVisibleArea();
   cx->SetVisibleArea(r);
 
   // Create a new presentation shell to view the document. Use the
   // exact same style information that this document has.
   nsAutoPtr<nsStyleSet> newSet(CloneStyleSet(mStyleSet));
-  nsCOMPtr<nsIPresShell> sh;
-  rv = mDocument->CreateShell(cx, vm, newSet, getter_AddRefs(sh));
-  NS_ENSURE_SUCCESS(rv, false);
+  nsCOMPtr<nsIPresShell> sh = mDocument->CreateShell(cx, vm, newSet);
+  NS_ENSURE_TRUE(sh, false);
   newSet.forget();
   // Note that after we create the shell, we must make sure to destroy it
   sh->SetVerifyReflowEnable(false); // turn off verify reflow while we're reflowing the test frame tree
   vm->SetPresShell(sh);
   {
     nsAutoCauseReflowNotifier crNotifier(this);
     sh->Initialize(r.width, r.height);
   }
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -62,22 +62,19 @@ class PresShell : public nsIPresShell,
 public:
   PresShell();
 
   NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
 
   // nsISupports
   NS_DECL_ISUPPORTS
 
-  // nsIPresShell
-  virtual NS_HIDDEN_(nsresult) Init(nsIDocument* aDocument,
-                                   nsPresContext* aPresContext,
-                                   nsViewManager* aViewManager,
-                                   nsStyleSet* aStyleSet,
-                                   nsCompatibility aCompatMode);
+  void Init(nsIDocument* aDocument, nsPresContext* aPresContext,
+            nsViewManager* aViewManager, nsStyleSet* aStyleSet,
+            nsCompatibility aCompatMode);
   virtual NS_HIDDEN_(void) Destroy();
   virtual NS_HIDDEN_(void) MakeZombie();
 
   virtual NS_HIDDEN_(nsresult) SetPreferenceStyleRules(bool aForceReflow);
 
   NS_IMETHOD GetSelection(SelectionType aType, nsISelection** aSelection);
   virtual mozilla::Selection* GetCurrentSelection(SelectionType aType);
 
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -2218,21 +2218,21 @@ nsPrintEngine::ReflowPrintObject(nsPrint
 
   rv = aPO->mViewManager->Init(mPrt->mPrintDC);
   NS_ENSURE_SUCCESS(rv,rv);
 
   nsStyleSet* styleSet;
   rv = mDocViewerPrint->CreateStyleSet(aPO->mDocument, &styleSet);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = aPO->mDocument->CreateShell(aPO->mPresContext, aPO->mViewManager,
-                                   styleSet, getter_AddRefs(aPO->mPresShell));
-  if (NS_FAILED(rv)) {
+  aPO->mPresShell = aPO->mDocument->CreateShell(aPO->mPresContext,
+                                                aPO->mViewManager, styleSet);
+  if (!aPO->mPresShell) {
     delete styleSet;
-    return rv;
+    return NS_ERROR_FAILURE;
   }
 
   styleSet->EndUpdate();
   
   // The pres shell now owns the style set object.
 
 
   bool doReturn = false;;
--- a/layout/tools/reftest/Makefile.in
+++ b/layout/tools/reftest/Makefile.in
@@ -56,16 +56,17 @@ endif
   $(srcdir)/runreftest.py \
   $(srcdir)/remotereftest.py \
   $(srcdir)/runreftestb2g.py \
   $(srcdir)/b2g_start_script.js \
   automation.py \
   $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/devicemanager.py \
   $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py \
   $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py \
+  $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/droid.py \
   $(topsrcdir)/testing/mozbase/mozdevice/mozdevice/Zeroconf.py \
   $(topsrcdir)/build/mobile/b2gautomation.py \
   $(topsrcdir)/build/automationutils.py \
   $(topsrcdir)/build/mobile/remoteautomation.py \
   $(topsrcdir)/testing/mochitest/server.js \
   $(topsrcdir)/build/pgo/server-locations.txt \
   $(NULL)
 
--- a/layout/tools/reftest/remotereftest.py
+++ b/layout/tools/reftest/remotereftest.py
@@ -9,17 +9,18 @@ import tempfile
 import traceback
 
 # We need to know our current directory so that we can serve our test files from it.
 SCRIPT_DIRECTORY = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0])))
 
 from runreftest import RefTest
 from runreftest import ReftestOptions
 from automation import Automation
-import devicemanager, devicemanagerADB, devicemanagerSUT
+import devicemanager
+import droid
 from remoteautomation import RemoteAutomation, fennecLogcatFilters
 
 class RemoteOptions(ReftestOptions):
     def __init__(self, automation):
         ReftestOptions.__init__(self, automation)
 
         defaults = {}
         defaults["logFile"] = "reftest.log"
@@ -387,21 +388,21 @@ def main(args):
 
     if (options.deviceIP == None):
         print "Error: you must provide a device IP to connect to via the --device option"
         return 1
 
     try:
         if (options.dm_trans == "adb"):
             if (options.deviceIP):
-                dm = devicemanagerADB.DeviceManagerADB(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot)
+                dm = droid.DroidADB(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot)
             else:
-                dm = devicemanagerADB.DeviceManagerADB(None, None, deviceRoot=options.remoteTestRoot)
+                dm = droid.DroidADB(None, None, deviceRoot=options.remoteTestRoot)
         else:
-            dm = devicemanagerSUT.DeviceManagerSUT(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot)
+            dm = droid.DroidSUT(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot)
     except devicemanager.DMError:
         print "Error: exception while initializing devicemanager.  Most likely the device is not in a testable state."
         return 1
 
     automation.setDeviceManager(dm)
 
     if (options.remoteProductName != None):
         automation.setProduct(options.remoteProductName)
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -551,21 +551,16 @@ abstract public class BrowserApp extends
             mLayerView.getLayerMarginsAnimator().showMargins(true);
             mLayerView.getLayerClient().setOnMetricsChangedListener(this);
         }
 
         // Intercept key events for gamepad shortcuts
         mLayerView.setOnKeyListener(this);
     }
 
-    private void setSidebarMargin(int margin) {
-        ((RelativeLayout.LayoutParams) mGeckoLayout.getLayoutParams()).leftMargin = margin;
-        mGeckoLayout.requestLayout();
-    }
-
     private void setToolbarMargin(int margin) {
         ((RelativeLayout.LayoutParams) mGeckoLayout.getLayoutParams()).topMargin = margin;
         mGeckoLayout.requestLayout();
     }
 
     @Override
     public void onMetricsChanged(ImmutableViewportMetrics aMetrics) {
         if (mAboutHome.getUserVisibleHint() || mBrowserToolbar == null) {
@@ -697,53 +692,27 @@ abstract public class BrowserApp extends
         return (mTabsPanel != null && mTabsPanel.isSideBar());
     }
 
     private void updateSideBarState() {
         if (mMainLayoutAnimator != null)
             mMainLayoutAnimator.stop();
 
         boolean isSideBar = (HardwareUtils.isTablet() && mOrientation == Configuration.ORIENTATION_LANDSCAPE);
+        final int sidebarWidth = getResources().getDimensionPixelSize(R.dimen.tabs_sidebar_width);
 
-        ViewGroup.LayoutParams lp = mTabsPanel.getLayoutParams();
-        if (isSideBar) {
-            lp.width = getResources().getDimensionPixelSize(R.dimen.tabs_sidebar_width);
-        } else {
-            lp.width = ViewGroup.LayoutParams.FILL_PARENT;
-        }
+        ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mTabsPanel.getLayoutParams();
+        lp.width = (isSideBar ? sidebarWidth : ViewGroup.LayoutParams.FILL_PARENT);
         mTabsPanel.requestLayout();
 
-        final boolean changed = (mTabsPanel.isSideBar() != isSideBar);
-        final boolean needsRelayout = (changed && mTabsPanel.isShown());
-
-        if (needsRelayout) {
-            final int width;
-            final int scrollY;
-
-            if (isSideBar) {
-                width = lp.width;
-                mMainLayout.scrollTo(0, 0);
-            } else {
-                width = 0;
-            }
+        final boolean sidebarIsShown = (isSideBar && mTabsPanel.isShown());
+        final int mainLayoutScrollX = (sidebarIsShown ? -sidebarWidth : 0);
+        mMainLayout.scrollTo(mainLayoutScrollX, 0);
 
-            mBrowserToolbar.adjustForTabsLayout(width);
-            setSidebarMargin(width);
-        }
-
-        if (changed) {
-            // Cancel state of previous sidebar state
-            mBrowserToolbar.updateTabs(false);
-
-            mTabsPanel.setIsSideBar(isSideBar);
-            mBrowserToolbar.setIsSideBar(isSideBar);
-
-            // Update with new sidebar state
-            mBrowserToolbar.updateTabs(mTabsPanel.isShown());
-        }
+        mTabsPanel.setIsSideBar(isSideBar);
     }
 
     @Override
     public void handleMessage(String event, JSONObject message) {
         try {
             if (event.equals("Menu:Add")) {
                 MenuItemInfo info = new MenuItemInfo();
                 info.label = message.getString("name");
@@ -914,17 +883,17 @@ abstract public class BrowserApp extends
 
     @Override
     public void hideTabs() {
         mTabsPanel.hide();
     }
 
     @Override
     public boolean autoHideTabs() {
-        if (!hasTabsSideBar() && areTabsShown()) {
+        if (areTabsShown()) {
             hideTabs();
             return true;
         }
         return false;
     }
 
     @Override
     public boolean areTabsShown() {
@@ -935,86 +904,61 @@ abstract public class BrowserApp extends
     public void onTabsLayoutChange(int width, int height) {
         int animationLength = TABS_ANIMATION_DURATION;
 
         if (mMainLayoutAnimator != null) {
             animationLength = Math.max(1, animationLength - (int)mMainLayoutAnimator.getRemainingTime());
             mMainLayoutAnimator.stop(false);
         }
 
-        if (mTabsPanel.isShown())
+        if (areTabsShown()) {
             mTabsPanel.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+        }
 
         mMainLayoutAnimator = new PropertyAnimator(animationLength, sTabsInterpolator);
         mMainLayoutAnimator.setPropertyAnimationListener(this);
 
-        boolean usingTextureView = mLayerView.shouldUseTextureView();
-        mMainLayoutAnimator.setUseHardwareLayer(usingTextureView);
-
         if (hasTabsSideBar()) {
-            mBrowserToolbar.prepareTabsAnimation(mMainLayoutAnimator, width);
-
-            // Set the gecko layout for sliding.
-            if (!mTabsPanel.isShown()) {
-                mGeckoLayout.scrollTo(mTabsPanel.getWidth() * -1, 0);
-                setSidebarMargin(0);
-            }
-
-            mMainLayoutAnimator.attach(mGeckoLayout,
+            mMainLayoutAnimator.attach(mMainLayout,
                                        PropertyAnimator.Property.SCROLL_X,
                                        -width);
         } else {
-            mTabsPanel.prepareTabsAnimation(mMainLayoutAnimator);
-
             mMainLayoutAnimator.attach(mMainLayout,
                                        PropertyAnimator.Property.SCROLL_Y,
                                        -height);
         }
 
+        mTabsPanel.prepareTabsAnimation(mMainLayoutAnimator);
+
         // If the tabs layout is animating onto the screen, pin the dynamic
         // toolbar.
         if (mLayerView != null && isDynamicToolbarEnabled()) {
             if (width > 0 && height > 0) {
                 mLayerView.getLayerMarginsAnimator().showMargins(false);
                 mLayerView.getLayerMarginsAnimator().setMarginsPinned(true);
             } else {
                 mLayerView.getLayerMarginsAnimator().setMarginsPinned(false);
             }
         }
 
         mMainLayoutAnimator.start();
     }
 
     @Override
     public void onPropertyAnimationStart() {
-        mBrowserToolbar.updateTabs(true);
     }
 
     @Override
     public void onPropertyAnimationEnd() {
-        if (mTabsPanel.isShown()) {
-            if (hasTabsSideBar()) {
-                setSidebarMargin(mTabsPanel.getWidth());
-                mGeckoLayout.scrollTo(0, 0);
-            }
-
-            mGeckoLayout.requestLayout();
-        } else {
+        if (!areTabsShown()) {
             mTabsPanel.setVisibility(View.INVISIBLE);
-            mBrowserToolbar.updateTabs(false);
-            mBrowserToolbar.finishTabsAnimation();
             mTabsPanel.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
         }
 
-        mBrowserToolbar.refreshBackground();
         mTabsPanel.finishTabsAnimation();
-
-        if (hasTabsSideBar())
-            mBrowserToolbar.adjustTabsAnimation(true);
-
         mMainLayoutAnimator = null;
     }
 
     /* Favicon methods */
     private void loadFavicon(final Tab tab) {
         maybeCancelFaviconLoad(tab);
 
         long id = Favicons.getInstance().loadFavicon(tab.getURL(), tab.getFaviconURL(), !tab.isPrivate(),
--- a/mobile/android/base/BrowserToolbar.java
+++ b/mobile/android/base/BrowserToolbar.java
@@ -69,17 +69,16 @@ public class BrowserToolbar implements T
     private View mAwesomeBarEntry;
     private ImageView mAwesomeBarRightEdge;
     private BrowserToolbarBackground mAddressBarBg;
     private GeckoTextView mTitle;
     private int mTitlePadding;
     private boolean mSiteSecurityVisible;
     private boolean mSwitchingTabs;
     private ShapedButton mTabs;
-    private int mTabsPaneWidth;
     private ImageButton mBack;
     private ImageButton mForward;
     public ImageButton mFavicon;
     public ImageButton mStop;
     public ImageButton mSiteSecurity;
     public ImageButton mReader;
     private AnimationDrawable mProgressSpinner;
     private TabCounter mTabsCounter;
@@ -215,21 +214,19 @@ public class BrowserToolbar implements T
         mAddressBarBg = (BrowserToolbarBackground) mLayout.findViewById(R.id.address_bar_bg);
         mAddressBarViewOffset = mActivity.getResources().getDimensionPixelSize(R.dimen.addressbar_offset_left);
         mDefaultForwardMargin = mActivity.getResources().getDimensionPixelSize(R.dimen.forward_default_offset);
         mAwesomeBarContent = mLayout.findViewById(R.id.awesome_bar_content);
         mAwesomeBarEntry = mLayout.findViewById(R.id.awesome_bar_entry);
 
         // This will clip the right edge's image at half of its width
         mAwesomeBarRightEdge = (ImageView) mLayout.findViewById(R.id.awesome_bar_right_edge);
-        mAwesomeBarRightEdge.getDrawable().setLevel(5000);
-
-        // This will hold the translation width inside the toolbar when the tabs
-        // pane is visible. It will affect the padding applied to the title TextView.
-        mTabsPaneWidth = 0;
+        if (mAwesomeBarRightEdge != null) {
+            mAwesomeBarRightEdge.getDrawable().setLevel(5000);
+        }
 
         mTitle = (GeckoTextView) mLayout.findViewById(R.id.awesome_bar_title);
         mTitlePadding = mTitle.getPaddingRight();
         if (Build.VERSION.SDK_INT >= 16)
             mTitle.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
 
         mTabs = (ShapedButton) mLayout.findViewById(R.id.tabs);
         mTabs.setOnClickListener(new Button.OnClickListener() {
@@ -419,23 +416,16 @@ public class BrowserToolbar implements T
 
         mFocusOrder = Arrays.asList(mBack, mForward, mLayout, mReader, mSiteSecurity, mStop, mTabs);
     }
 
     public View getLayout() {
         return mLayout;
     }
 
-    public void refreshBackground() {
-        mAddressBarBg.requestLayout();
-
-        if (mAwesomeBarRightEdge != null)
-            mAwesomeBarRightEdge.requestLayout();
-    }
-
     @Override
     public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) {
         switch(msg) {
             case TITLE:
                 if (Tabs.getInstance().isSelectedTab(tab)) {
                     setTitle(tab.getDisplayTitle());
                 }
                 break;
@@ -570,18 +560,21 @@ public class BrowserToolbar implements T
         // shrink back.
         if (!mLayout.isSelected()) {
             // Keep the entry highlighted during the animation
             mLayout.setSelected(true);
 
             final int entryTranslation = getAwesomeBarEntryTranslation();
             final int curveTranslation = getAwesomeBarCurveTranslation();
 
-            proxy = AnimatorProxy.create(mAwesomeBarRightEdge);
-            proxy.setTranslationX(entryTranslation);
+            if (mAwesomeBarRightEdge != null) {
+                proxy = AnimatorProxy.create(mAwesomeBarRightEdge);
+                proxy.setTranslationX(entryTranslation);
+            }
+
             proxy = AnimatorProxy.create(mTabs);
             proxy.setTranslationX(curveTranslation);
             proxy = AnimatorProxy.create(mTabsCounter);
             proxy.setTranslationX(curveTranslation);
             proxy = AnimatorProxy.create(mActionItemBar);
             proxy.setTranslationX(curveTranslation);
 
             if (mHasSoftMenuButton) {
@@ -594,19 +587,23 @@ public class BrowserToolbar implements T
             proxy = AnimatorProxy.create(mStop);
             proxy.setAlpha(0);
         }
 
         final PropertyAnimator contentAnimator = new PropertyAnimator(250);
         contentAnimator.setUseHardwareLayer(false);
 
         // Shrink the awesome entry back to its original size
-        contentAnimator.attach(mAwesomeBarRightEdge,
-                               PropertyAnimator.Property.TRANSLATION_X,
-                               0);
+
+        if (mAwesomeBarRightEdge != null) {
+            contentAnimator.attach(mAwesomeBarRightEdge,
+                                   PropertyAnimator.Property.TRANSLATION_X,
+                                   0);
+        }
+
         contentAnimator.attach(mTabs,
                                PropertyAnimator.Property.TRANSLATION_X,
                                0);
         contentAnimator.attach(mTabsCounter,
                                PropertyAnimator.Property.TRANSLATION_X,
                                0);
         contentAnimator.attach(mActionItemBar,
                                PropertyAnimator.Property.TRANSLATION_X,
@@ -679,19 +676,23 @@ public class BrowserToolbar implements T
 
         // Hide stop/reader buttons immediately
         AnimatorProxy proxy = AnimatorProxy.create(mReader);
         proxy.setAlpha(0);
         proxy = AnimatorProxy.create(mStop);
         proxy.setAlpha(0);
 
         // Slide the right side elements of the toolbar
-        contentAnimator.attach(mAwesomeBarRightEdge,
-                               PropertyAnimator.Property.TRANSLATION_X,
-                               entryTranslation);
+
+        if (mAwesomeBarRightEdge != null) {
+            contentAnimator.attach(mAwesomeBarRightEdge,
+                                   PropertyAnimator.Property.TRANSLATION_X,
+                                   entryTranslation);
+        }
+
         contentAnimator.attach(mTabs,
                                PropertyAnimator.Property.TRANSLATION_X,
                                curveTranslation);
         contentAnimator.attach(mTabsCounter,
                                PropertyAnimator.Property.TRANSLATION_X,
                                curveTranslation);
         contentAnimator.attach(mActionItemBar,
                                PropertyAnimator.Property.TRANSLATION_X,
@@ -771,98 +772,16 @@ public class BrowserToolbar implements T
         if (mLayout.isSelected()) {
             return;
         }
 
         mTabsCounter.setCurrentText(String.valueOf(count));
         mTabs.setContentDescription((count > 1) ?
                                     mActivity.getString(R.string.num_tabs, count) :
                                     mActivity.getString(R.string.one_tab));
-        updateTabs(mActivity.areTabsShown());
-    }
-
-    public void prepareTabsAnimation(PropertyAnimator animator, int width) {
-        animator.attach(mAwesomeBarEntry,
-                        PropertyAnimator.Property.TRANSLATION_X,
-                        width);
-        animator.attach(mAddressBarBg,
-                        PropertyAnimator.Property.TRANSLATION_X,
-                        width);
-        animator.attach(mTabs,
-                        PropertyAnimator.Property.TRANSLATION_X,
-                        width);
-        animator.attach(mTabsCounter,
-                        PropertyAnimator.Property.TRANSLATION_X,
-                        width);
-        animator.attach(mBack,
-                        PropertyAnimator.Property.TRANSLATION_X,
-                        width);
-        animator.attach(mForward,
-                        PropertyAnimator.Property.TRANSLATION_X,
-                        width);
-        animator.attach(mTitle,
-                        PropertyAnimator.Property.TRANSLATION_X,
-                        width);
-        animator.attach(mFavicon,
-                        PropertyAnimator.Property.TRANSLATION_X,
-                        width);
-        animator.attach(mSiteSecurity,
-                        PropertyAnimator.Property.TRANSLATION_X,
-                        width);
-
-        // Uses the old mTabsPaneWidth.
-        adjustTabsAnimation(false);
-
-        mTabsPaneWidth = width;
-
-        // Only update title padding immediatelly when shrinking the browser
-        // toolbar. Leave the padding update to the end of the animation when
-        // expanding (see finishTabsAnimation()).
-        if (mTabsPaneWidth > 0)
-            setPageActionVisibility(mStop.getVisibility() == View.VISIBLE);
-    }
-
-    public void adjustTabsAnimation(boolean reset) {
-        int width = reset ? 0 : mTabsPaneWidth;
-        mAwesomeBarEntry.setTranslationX(width);
-        mAddressBarBg.setTranslationX(width);
-        mTabs.setTranslationX(width);
-        mTabsCounter.setTranslationX(width);
-        mBack.setTranslationX(width);
-        mForward.setTranslationX(width);
-        mTitle.setTranslationX(width);
-        mFavicon.setTranslationX(width);
-        mSiteSecurity.setTranslationX(width);
-
-        ((ViewGroup.MarginLayoutParams) mLayout.getLayoutParams()).leftMargin = reset ? mTabsPaneWidth : 0;
-    }
-
-    public void finishTabsAnimation() {
-        setPageActionVisibility(mStop.getVisibility() == View.VISIBLE);
-    }
-
-    public void adjustForTabsLayout(int width) {
-        mTabsPaneWidth = width;
-        adjustTabsAnimation(true);
-    }
-
-    public void updateTabs(boolean areTabsShown) {
-        if (areTabsShown)
-            mTabs.setImageLevel(TABS_EXPANDED);
-        else
-            mTabs.setImageLevel(TABS_CONTRACTED);
-
-        // A level change will not trigger onMeasure() for the tabs, where the path is created.
-        // Manually requesting a layout to re-calculate the path.
-        mTabs.requestLayout();
-    }
-
-    public void setIsSideBar(boolean isSideBar) {
-        Resources resources = mActivity.getResources();
-        mTabs.setBackgroundDrawable(resources.getDrawable(R.drawable.shaped_button));
     }
 
     public void setProgressVisibility(boolean visible) {
         // The "Throbber start" and "Throbber stop" log messages in this method
         // are needed by S1/S2 tests (http://mrcote.info/phonedash/#).
         // See discussion in Bug 804457. Bug 805124 tracks paring these down.
         if (visible) {
             mFavicon.setImageDrawable(mProgressSpinner);
--- a/mobile/android/base/TabsPanel.java
+++ b/mobile/android/base/TabsPanel.java
@@ -371,35 +371,68 @@ public class TabsPanel extends LinearLay
         // Not worth doing this on pre-Honeycomb without proper
         // hardware accelerated animations.
         if (Build.VERSION.SDK_INT < 11) {
             return;
         }
 
         final Resources resources = getContext().getResources();
         final int toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height);
+        final int tabsPanelWidth = getWidth();
 
         if (mVisible) {
-            AnimatorProxy proxy = AnimatorProxy.create(mHeader);
-            proxy.setTranslationY(-toolbarHeight);
+            AnimatorProxy proxy;
+
+            proxy = AnimatorProxy.create(mHeader);
+            if (mIsSideBar) {
+                proxy.setTranslationX(-tabsPanelWidth);
+            } else {
+                proxy.setTranslationY(-toolbarHeight);
+            }
 
             proxy = AnimatorProxy.create(mTabsContainer);
-            proxy.setTranslationY((float) (-toolbarHeight));
-            proxy.setAlpha(0);
+            if (mIsSideBar) {
+                proxy.setTranslationX(-tabsPanelWidth);
+            } else {
+                proxy.setTranslationY((float) (-toolbarHeight));
+                proxy.setAlpha(0);
+            }
+
+            // The footer view is only present on the sidebar
+            if (mIsSideBar) {
+                proxy = AnimatorProxy.create(mFooter);
+                proxy.setTranslationX(-tabsPanelWidth);
+            }
         }
 
-        animator.attach(mTabsContainer,
-                        PropertyAnimator.Property.ALPHA,
-                        mVisible ? 1.0f : 0.0f);
-        animator.attach(mHeader,
-                        PropertyAnimator.Property.TRANSLATION_Y,
-                        mVisible ? 0 : -toolbarHeight);
-        animator.attach(mTabsContainer,
-                        PropertyAnimator.Property.TRANSLATION_Y,
-                        mVisible ? 0 : -toolbarHeight);
+        if (mIsSideBar) {
+            final int translationX = (mVisible ? 0 : -tabsPanelWidth);
+
+            animator.attach(mTabsContainer,
+                            PropertyAnimator.Property.TRANSLATION_X,
+                            translationX);
+            animator.attach(mHeader,
+                            PropertyAnimator.Property.TRANSLATION_X,
+                            translationX);
+            animator.attach(mFooter,
+                            PropertyAnimator.Property.TRANSLATION_X,
+                            translationX);
+        } else {
+            final int translationY = (mVisible ? 0 : -toolbarHeight);
+
+            animator.attach(mTabsContainer,
+                            PropertyAnimator.Property.ALPHA,
+                            mVisible ? 1.0f : 0.0f);
+            animator.attach(mTabsContainer,
+                            PropertyAnimator.Property.TRANSLATION_Y,
+                            translationY);
+            animator.attach(mHeader,
+                            PropertyAnimator.Property.TRANSLATION_Y,
+                            translationY);
+        }
 
         mHeader.setLayerType(View.LAYER_TYPE_HARDWARE, null);
         mTabsContainer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
     }
 
     public void finishTabsAnimation() {
         if (Build.VERSION.SDK_INT < 11) {
             return;
--- a/mobile/android/base/resources/layout-large-v11/browser_toolbar.xml
+++ b/mobile/android/base/resources/layout-large-v11/browser_toolbar.xml
@@ -35,48 +35,28 @@
                         style="@style/AddressBar.ImageButton.TabCount"
                         android:layout_width="24dip"
                         android:layout_height="24dip"
                         android:layout_marginLeft="12dip"
                         android:layout_marginRight="48dip"
                         android:layout_marginTop="16dp"
                         android:layout_alignLeft="@id/tabs"/>
 
-    <FrameLayout android:layout_width="fill_parent"
-                 android:layout_height="fill_parent"
-                 android:layout_toRightOf="@id/tabs"
-                 android:layout_toLeftOf="@id/menu_items"
-                 android:layout_marginLeft="-18dp"
-                 android:paddingRight="18dp"
-                 android:layout_marginTop="7dp"
-                 android:layout_marginBottom="7dp"
-                 android:duplicateParentState="true">
-
-        <ImageView android:id="@+id/awesome_bar_entry"
-                   style="@style/AddressBar.Button"
-                   android:layout_toRightOf="@id/tabs"
-                   android:layout_toLeftOf="@id/menu_items"
-                   android:duplicateParentState="true"
-                   android:clickable="false"
-                   android:focusable="false"
-                   android:src="@drawable/address_bar_url"
-                   android:scaleType="fitXY"/>
-
-    </FrameLayout>
-
-    <ImageView android:id="@+id/awesome_bar_right_edge"
-               style="@style/AddressBar.Button"
+    <ImageView android:id="@+id/awesome_bar_entry"
+               android:layout_width="fill_parent"
+               android:layout_height="fill_parent"
+               android:layout_toRightOf="@id/tabs"
                android:layout_toLeftOf="@id/menu_items"
+               android:layout_marginLeft="-18dp"
                android:layout_marginTop="7dp"
                android:layout_marginBottom="7dp"
                android:duplicateParentState="true"
                android:clickable="false"
                android:focusable="false"
-               android:src="@drawable/address_bar_right_edge"
-               android:scaleType="fitXY"/>
+               android:background="@drawable/address_bar_url"/>
 
     <org.mozilla.gecko.ForwardButton style="@style/AddressBar.ImageButton.Forward"
                                      android:id="@+id/forward"
                                      android:layout_toRightOf="@+id/tabs"/>
 
     <org.mozilla.gecko.BackButton android:id="@+id/back"
                                   style="@style/AddressBar.ImageButton"
                                   android:layout_width="50dip"
--- a/modules/libjar/moz.build
+++ b/modules/libjar/moz.build
@@ -11,12 +11,12 @@ TEST_TOOL_DIRS += ['test']
 
 XPIDL_SOURCES += [
     '$(MODULES_LIBJAR_LXPIDLSRCS)',
 ]
 
 MODULE = 'jar'
 
 EXPORTS += [
-    'zipstruct.h',
     'nsJARURI.h',
     'nsZipArchive.h',
+    'zipstruct.h',
 ]
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -3957,16 +3957,18 @@ pref("layers.acceleration.disabled", fal
 // means a startup crash for everyone.
 pref("layers.acceleration.force-enabled", true);
 #else
 pref("layers.acceleration.force-enabled", false);
 #endif
 
 pref("layers.acceleration.draw-fps", false);
 
+pref("layers.draw-borders", false);
+
 pref("layers.offmainthreadcomposition.enabled", false);
 // same effect as layers.offmainthreadcomposition.enabled, but specifically for
 // use with tests.
 pref("layers.offmainthreadcomposition.testing.enabled", false);
 // Whether to animate simple opacity and transforms on the compositor
 pref("layers.offmainthreadcomposition.animate-opacity", false);
 pref("layers.offmainthreadcomposition.animate-transform", false);
 pref("layers.offmainthreadcomposition.log-animations", false);
--- a/netwerk/base/public/moz.build
+++ b/netwerk/base/public/moz.build
@@ -5,18 +5,18 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPIDL_SOURCES += [
     'mozIThirdPartyUtil.idl',
     'nsIApplicationCache.idl',
     'nsIApplicationCacheChannel.idl',
     'nsIApplicationCacheContainer.idl',
     'nsIApplicationCacheService.idl',
+    'nsIArrayBufferInputStream.idl',
     'nsIAsyncStreamCopier.idl',
-    'nsIArrayBufferInputStream.idl',
     'nsIAsyncVerifyRedirectCallback.idl',
     'nsIAuthInformation.idl',
     'nsIAuthModule.idl',
     'nsIAuthPrompt.idl',
     'nsIAuthPrompt2.idl',
     'nsIAuthPromptAdapterFactory.idl',
     'nsIAuthPromptCallback.idl',
     'nsIAuthPromptProvider.idl',
@@ -103,18 +103,18 @@ XPIDL_SOURCES += [
     'nsIURIChecker.idl',
     'nsIURIClassifier.idl',
     'nsIURIWithPrincipal.idl',
     'nsIURL.idl',
     'nsIURLParser.idl',
     'nsIUnicharStreamLoader.idl',
     'nsIUploadChannel.idl',
     'nsIUploadChannel2.idl',
+    'nsPILoadGroupInternal.idl',
     'nsPISocketTransportService.idl',
-    'nsPILoadGroupInternal.idl',
 ]
 
 if CONFIG['MOZ_TOOLKIT_SEARCH']:
     XPIDL_SOURCES += [
         'nsIBrowserSearchService.idl',
     ]
 
 MODULE = 'necko'
--- a/testing/mochitest/Makefile.in
+++ b/testing/mochitest/Makefile.in
@@ -35,16 +35,17 @@ include $(topsrcdir)/build/automation-bu
 		automation.py \
 		runtestsb2g.py \
 		runtestsremote.py \
 		runtestsvmware.py \
 		manifest.webapp \
 		$(topsrcdir)/testing/mozbase/mozdevice/mozdevice/devicemanager.py \
 		$(topsrcdir)/testing/mozbase/mozdevice/mozdevice/devicemanagerADB.py \
 		$(topsrcdir)/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py \
+		$(topsrcdir)/testing/mozbase/mozdevice/mozdevice/droid.py \
 		$(topsrcdir)/testing/mozbase/mozdevice/mozdevice/Zeroconf.py \
 		$(topsrcdir)/build/automationutils.py \
 		$(topsrcdir)/build/manifestparser.py \
 		$(topsrcdir)/build/mobile/remoteautomation.py \
 		$(topsrcdir)/build/mobile/b2gautomation.py \
 		gen_template.pl \
 		server.js \
 		harness-overlay.xul \
--- a/testing/mochitest/runtestsremote.py
+++ b/testing/mochitest/runtestsremote.py
@@ -15,17 +15,18 @@ import base64
 sys.path.insert(0, os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))))
 
 from automation import Automation
 from remoteautomation import RemoteAutomation, fennecLogcatFilters
 from runtests import Mochitest
 from runtests import MochitestOptions
 from runtests import MochitestServer
 
-import devicemanager, devicemanagerADB, devicemanagerSUT
+import devicemanager
+import droid
 import manifestparser
 
 class RemoteOptions(MochitestOptions):
 
     def __init__(self, automation, scriptdir, **kwargs):
         defaults = {}
         MochitestOptions.__init__(self, automation, scriptdir)
 
@@ -486,21 +487,21 @@ class MochiRemote(Mochitest):
         
 def main():
     scriptdir = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
     auto = RemoteAutomation(None, "fennec")
     parser = RemoteOptions(auto, scriptdir)
     options, args = parser.parse_args()
     if (options.dm_trans == "adb"):
         if (options.deviceIP):
-            dm = devicemanagerADB.DeviceManagerADB(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot)
+            dm = droid.DroidADB(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot)
         else:
-            dm = devicemanagerADB.DeviceManagerADB(deviceRoot=options.remoteTestRoot)
+            dm = droid.DroidADB(deviceRoot=options.remoteTestRoot)
     else:
-         dm = devicemanagerSUT.DeviceManagerSUT(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot)
+         dm = droid.DroidSUT(options.deviceIP, options.devicePort, deviceRoot=options.remoteTestRoot)
     auto.setDeviceManager(dm)
     options = parser.verifyRemoteOptions(options, auto)
     if (options == None):
         print "ERROR: Invalid options specified, use --help for a list of valid options"
         sys.exit(1)
 
     productPieces = options.remoteProductName.split('.')
     if (productPieces != None):
--- a/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py
+++ b/testing/mozbase/mozdevice/mozdevice/devicemanagerSUT.py
@@ -503,17 +503,25 @@ class DeviceManagerSUT(DeviceManager):
                 return None
             outputFile += "/process.txt"
             cmdline += " > " + outputFile
 
         # Prepend our env to the command
         cmdline = '%s %s' % (self._formatEnvString(env), cmdline)
 
         # fireProcess may trigger an exception, but we won't handle it
-        self.fireProcess(cmdline, failIfRunning)
+        if cmd[0] == "am":
+            # Robocop tests spawn "am instrument". sutAgent's exec ensures that
+            # am has started before returning, so there is no point in having
+            # fireProcess wait for it to start. Also, since "am" does not show
+            # up in the process list while the test is running, waiting for it
+            # in fireProcess is difficult.
+            self.fireProcess(cmdline, failIfRunning, 0)
+        else:
+            self.fireProcess(cmdline, failIfRunning)
         return outputFile
 
     def killProcess(self, appname, forceKill=False):
         if forceKill:
             self._logger.warn("killProcess(): forceKill parameter unsupported on SUT")
         retries = 0
         while retries < self.retryLimit:
             try:
--- a/testing/mozbase/mozdevice/mozdevice/droid.py
+++ b/testing/mozbase/mozdevice/mozdevice/droid.py
@@ -86,17 +86,32 @@ class DroidMixin(object):
         # Additional command line arguments that fennec will read and use (e.g.
         # with a custom profile)
         if extraArgs:
             extras['args'] = " ".join(extraArgs)
 
         self.launchApplication(appName, ".App", intent, url=url, extras=extras)
 
 class DroidADB(DeviceManagerADB, DroidMixin):
-    pass
+
+    def getTopActivity(self):
+        package = None
+        data = self.shellCheckOutput(["dumpsys", "window", "input"])
+        # "dumpsys window input" produces many lines of input. The top/foreground
+        # activity is indicated by something like:
+        #   mFocusedApp=AppWindowToken{483e6db0 token=HistoryRecord{484dcad8 com.mozilla.SUTAgentAndroid/.SUTAgentAndroid}}
+        # Extract this line, ending in the forward slash:
+        m = re.search('mFocusedApp(.+)/', data)
+        if m:
+             line = m.group(0)
+             # Extract package name: string of non-whitespace ending in forward slash
+             m = re.search('(\S+)/$', line)
+             if m:
+                 package = m.group(1)
+        return package
 
 class DroidSUT(DeviceManagerSUT, DroidMixin):
 
     def _getExtraAmStartArgs(self):
         # in versions of android in jellybean and beyond, the agent may run as
         # a different process than the one that started the app. In this case,
         # we need to get back the original user serial number and then pass
         # that to the 'am start' command line
@@ -114,16 +129,19 @@ class DroidSUT(DeviceManagerSUT, DroidMi
             else:
                 self._userSerial = None
 
         if self._userSerial is not None:
             return [ "--user", self._userSerial ]
 
         return []
 
+    def getTopActivity(self):
+        return self._runCmds([{ 'cmd': "activity" }]).strip()
+
 def DroidConnectByHWID(hwid, timeout=30, **kwargs):
     """Try to connect to the given device by waiting for it to show up using mDNS with the given timeout."""
     nt = NetworkTools()
     local_ip = nt.getLanIp()
 
     zc = Zeroconf(local_ip)
 
     evt = threading.Event()
--- a/testing/xpcshell/head.js
+++ b/testing/xpcshell/head.js
@@ -327,19 +327,19 @@ function _execute_test() {
   });
 
   // _HEAD_FILES is dynamically defined by <runxpcshelltests.py>.
   _load_files(_HEAD_FILES);
   // _TEST_FILE is dynamically defined by <runxpcshelltests.py>.
   _load_files(_TEST_FILE);
 
   try {
-    do_test_pending();
+    do_test_pending("MAIN run_test");
     run_test();
-    do_test_finished();
+    do_test_finished("MAIN run_test");
     _do_main();
   } catch (e) {
     _passed = false;
     // do_check failures are already logged and set _quit to true and throw
     // NS_ERROR_ABORT. If both of those are true it is likely this exception
     // has already been logged so there is no need to log it again. It's
     // possible that this will mask an NS_ERROR_ABORT that happens after a
     // do_check failure though.
@@ -423,18 +423,19 @@ function do_print(msg) {
  *   the number of milliseconds to delay
  * @param callback : function() : void
  *   the function to call
  */
 function do_timeout(delay, func) {
   new _Timer(func, Number(delay));
 }
 
-function do_execute_soon(callback) {
-  do_test_pending();
+function do_execute_soon(callback, aName) {
+  let funcName = (aName ? aName : callback.name);
+  do_test_pending(funcName);
   var tm = Components.classes["@mozilla.org/thread-manager;1"]
                      .getService(Components.interfaces.nsIThreadManager);
 
   tm.mainThread.dispatch({
     run: function() {
       try {
         callback();
       } catch (e) {
@@ -451,17 +452,17 @@ function do_execute_soon(callback) {
           }
           else {
             dump("\n");
           }
           _do_quit();
         }
       }
       finally {
-        do_test_finished();
+        do_test_finished(funcName);
       }
     }
   }, Components.interfaces.nsIThread.DISPATCH_NORMAL);
 }
 
 function do_throw(text, stack) {
   if (!stack)
     stack = Components.stack.caller;
@@ -780,26 +781,26 @@ function format_pattern_match_failure(di
     a = diagnosis;
   } else if (diagnosis.property) {
     a = "Property " + uneval(diagnosis.property) + " of object didn't match:\n";
     a += format_pattern_match_failure(diagnosis.diagnosis, indent + "  ");
   }
   return indent + a;
 }
 
-function do_test_pending() {
+function do_test_pending(aName) {
   ++_tests_pending;
 
-  _dump("TEST-INFO | (xpcshell/head.js) | test " + _tests_pending +
-         " pending\n");
+  _dump("TEST-INFO | (xpcshell/head.js) | test" + (aName ? " " + aName : "") +
+         " pending (" + _tests_pending + ")\n");
 }
 
-function do_test_finished() {
-  _dump("TEST-INFO | (xpcshell/head.js) | test " + _tests_pending +
-         " finished\n");
+function do_test_finished(aName) {
+  _dump("TEST-INFO | (xpcshell/head.js) | test" + (aName ? " " + aName : "") +
+         " finished (" + _tests_pending + ")\n");
 
   if (--_tests_pending == 0)
     _do_quit();
 }
 
 function do_get_file(path, allowNonexistent) {
   try {
     let lf = Components.classes["@mozilla.org/file/directory_service;1"]
@@ -1022,17 +1023,17 @@ function do_load_child_test_harness()
 function run_test_in_child(testFile, optionalCallback) 
 {
   var callback = (typeof optionalCallback == 'undefined') ? 
                     do_test_finished : optionalCallback;
 
   do_load_child_test_harness();
 
   var testPath = do_get_file(testFile).path.replace(/\\/g, "/");
-  do_test_pending();
+  do_test_pending("run in child");
   sendCommand("_dump('CHILD-TEST-STARTED'); "
               + "const _TEST_FILE=['" + testPath + "']; _execute_test(); "
               + "_dump('CHILD-TEST-COMPLETED');", 
               callback);
 }
 
 
 /**
@@ -1104,20 +1105,20 @@ function add_task(func) {
  */
 let _gRunningTest = null;
 let _gTestIndex = 0; // The index of the currently running test.
 function run_next_test()
 {
   function _run_next_test()
   {
     if (_gTestIndex < _gTests.length) {
-      do_test_pending();
       let _isTask;
       [_isTask, _gRunningTest] = _gTests[_gTestIndex++];
       print("TEST-INFO | " + _TEST_FILE + " | Starting " + _gRunningTest.name);
+      do_test_pending(_gRunningTest.name);
 
       if (_isTask) {
         _Task.spawn(_gRunningTest)
              .then(run_next_test, do_report_unexpected_exception);
       } else {
         // Exceptions do not kill asynchronous tests, so they'll time out.
         try {
           _gRunningTest();
@@ -1127,15 +1128,15 @@ function run_next_test()
       }
     }
   }
 
   // For sane stacks during failures, we execute this code soon, but not now.
   // We do this now, before we call do_test_finished(), to ensure the pending
   // counter (_tests_pending) never reaches 0 while we still have tests to run
   // (do_execute_soon bumps that counter).
-  do_execute_soon(_run_next_test);
+  do_execute_soon(_run_next_test, "run_next_test " + _gTestIndex);
 
   if (_gRunningTest !== null) {
     // Close the previous test do_test_pending call.
-    do_test_finished();
+    do_test_finished(_gRunningTest.name);
   }
 }
--- a/toolkit/components/places/moz.build
+++ b/toolkit/components/places/moz.build
@@ -8,21 +8,21 @@ if CONFIG['MOZ_PLACES']:
     TEST_DIRS += ['tests']
 
 XPIDL_SOURCES += [
     'nsINavHistoryService.idl',
 ]
 
 if CONFIG['MOZ_PLACES']:
     XPIDL_SOURCES += [
+        'mozIAsyncFavicons.idl',
         'mozIAsyncHistory.idl',
-        'mozIAsyncFavicons.idl',
         'mozIAsyncLivemarks.idl',
+        'mozIColorAnalyzer.idl',
         'mozIPlacesAutoComplete.idl',
-        'mozIColorAnalyzer.idl',
         'nsIAnnotationService.idl',
         'nsIBrowserHistory.idl',
         'nsIFaviconService.idl',
         'nsINavBookmarksService.idl',
         'nsITaggingService.idl ',
         'nsPIPlacesDatabase.idl',
         'nsPIPlacesHistoryListenersNotifier.idl',
     ]
--- a/toolkit/components/social/SocialService.jsm
+++ b/toolkit/components/social/SocialService.jsm
@@ -621,67 +621,72 @@ SocialProvider.prototype = {
   // no FrameWorker)
   // This distinction might be used to cache certain data between runs - eg,
   // browser-social.js caches the notification icons so they can be displayed
   // quickly at startup without waiting for the provider to initialize -
   // 'undefined' means 'ok to use cached values' versus 'null' meaning 'cached
   // values aren't to be used as the user is logged out'.
   profile: undefined,
 
-  // Contains the information necessary to support our page mark feature.
+  // Contains the information necessary to support our "recommend" feature.
   // null means no info yet provided (which includes the case of the provider
   // not supporting the feature) or the provided data is invalid.  Updated via
-  // the 'pageMarkInfo' setter and returned via the getter.
-  _pageMarkInfo: null,
-  get pageMarkInfo() {
-    return this._pageMarkInfo;
+  // the 'recommendInfo' setter and returned via the getter.
+  _recommendInfo: null,
+  get recommendInfo() {
+    return this._recommendInfo;
   },
-  set pageMarkInfo(data) {
-    // Accept *and validate* the page-mark-config message from the provider.
+  set recommendInfo(data) {
+    // Accept *and validate* the user-recommend-prompt-response message from
+    // the provider.
     let promptImages = {};
     let promptMessages = {};
     function reportError(reason) {
-      Cu.reportError("Invalid page-mark data from provider: " + reason + ": marking is disabled for this provider");
-      // and we explicitly reset the page-mark data to null to avoid stale
+      Cu.reportError("Invalid recommend data from provider: " + reason + ": sharing is disabled for this provider");
+      // and we explicitly reset the recommend data to null to avoid stale
       // data being used and notify our observers.
-      this._pageMarkInfo = null;
-      Services.obs.notifyObservers(null, "social:page-mark-config", this.origin);
+      this._recommendInfo = null;
+      Services.obs.notifyObservers(null, "social:recommend-info-changed", this.origin);
     }
     if (!data ||
         !data.images || typeof data.images != "object" ||
         !data.messages || typeof data.messages != "object") {
       reportError("data is missing valid 'images' or 'messages' elements");
       return;
     }
-    for (let sub of ["marked", "unmarked"]) {
+    for (let sub of ["share", "unshare"]) {
       let url = data.images[sub];
       if (!url || typeof url != "string" || url.length == 0) {
-        reportError('images["' + sub + '"] is not a valid string');
+        reportError('images["' + sub + '"] is missing or not a non-empty string');
         return;
       }
       // resolve potentially relative URLs but there is no same-origin check
       // for images to help providers utilize content delivery networks...
       // Also note no scheme checks are necessary - even a javascript: URL
       // is safe as gecko evaluates them in a sandbox.
       let imgUri = this.resolveUri(url);
       if (!imgUri) {
         reportError('images["' + sub + '"] is an invalid URL');
         return;
       }
       promptImages[sub] = imgUri.spec;
     }
-    for (let sub of ["markedTooltip", "unmarkedTooltip", "markedLabel", "unmarkedLabel"]) {
+    for (let sub of ["shareTooltip", "unshareTooltip",
+                     "sharedLabel", "unsharedLabel", "unshareLabel",
+                     "portraitLabel",
+                     "unshareConfirmLabel", "unshareConfirmAccessKey",
+                     "unshareCancelLabel", "unshareCancelAccessKey"]) {
       if (typeof data.messages[sub] != "string" || data.messages[sub].length == 0) {
         reportError('messages["' + sub + '"] is not a valid string');
         return;
       }
       promptMessages[sub] = data.messages[sub];
     }
-    this._pageMarkInfo = {images: promptImages, messages: promptMessages};
-    Services.obs.notifyObservers(null, "social:page-mark-config", this.origin);
+    this._recommendInfo = {images: promptImages, messages: promptMessages};
+    Services.obs.notifyObservers(null, "social:recommend-info-changed", this.origin);
   },
 
   // Map of objects describing the provider's notification icons, whose
   // properties include:
   //   name, iconURL, counter, contentPanel
   // See https://developer.mozilla.org/en-US/docs/Social_API
   ambientNotificationIcons: null,
 
--- a/toolkit/components/social/WorkerAPI.jsm
+++ b/toolkit/components/social/WorkerAPI.jsm
@@ -53,22 +53,24 @@ WorkerAPI.prototype = {
       getFrameWorkerHandle(this._provider.workerURL, null)._worker.reload();
       // the frameworker is going to be reloaded, send the initialization
       // so it can have the same startup sequence as if it were loaded
       // the first time.  This will be queued until the frameworker is ready.
       this._port.postMessage({topic: "social.initialize"});
     },
     "social.user-profile": function (data) {
       this._provider.updateUserProfile(data);
+      // get the info we need for 'recommend' support.
+      this._port.postMessage({topic: "social.user-recommend-prompt"});
     },
     "social.ambient-notification": function (data) {
       this._provider.setAmbientNotification(data);
     },
-    "social.page-mark-config": function(data) {
-      this._provider.pageMarkInfo = data;
+    "social.user-recommend-prompt-response": function(data) {
+      this._provider.recommendInfo = data;
     },
     "social.cookies-get": function(data) {
       let document = this._port._window.document;
       let cookies = document.cookie.split(";");
       let results = [];
       cookies.forEach(function(aCookie) {
         let [name, value] = aCookie.split("=");
         results.push({name: unescape(name.trim()),
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -6,18 +6,18 @@
 
 if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
     MODULE = 'profiler'
     XPIDL_SOURCES += [
         'nsIProfileSaveEvent.idl',
         'nsIProfiler.idl',
     ]
     EXPORTS += [
+        'GeckoProfilerFunc.h',
         'GeckoProfilerImpl.h',
-        'GeckoProfilerFunc.h',
         'PseudoStack.h',
         'shared-libraries.h',
     ]
 
 EXPORTS += [
     'GeckoProfiler.h',
 ]
 
--- a/uriloader/exthandler/moz.build
+++ b/uriloader/exthandler/moz.build
@@ -28,17 +28,20 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'co
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     osdir = 'android'
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     osdir = 'gonk'
 else:
     osdir = 'unix'
 
 EXPORTS += [
-    osdir + '/nsOSHelperAppService.h',
+    osdir + '/nsOSHelperAppService.h'
+]
+
+EXPORTS += [
     'nsExternalHelperAppService.h',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     EXPORTS += [
         'nsExternalSharingAppService.h',
         'nsExternalURLHandlerService.h',
     ]
--- a/xpcom/reflect/xptinfo/public/nsIInterfaceInfoManager.idl
+++ b/xpcom/reflect/xptinfo/public/nsIInterfaceInfoManager.idl
@@ -7,25 +7,27 @@
 
 
 #include "nsISupports.idl"
 #include "nsIInterfaceInfo.idl"
 #include "nsIEnumerator.idl"
 #include "nsISimpleEnumerator.idl"
 
 /* this is NOT intended to be scriptable */
-[uuid(1d53d8d9-1d92-428f-b5cc-198b55e897d7)]
+[uuid(8B161900-BE2B-11d2-9831-006008962422)]
 interface nsIInterfaceInfoManager : nsISupports
 {
     nsIInterfaceInfo getInfoForIID(in nsIIDPtr iid);
     nsIInterfaceInfo getInfoForName(in string name);
 
     nsIIDPtr getIIDForName(in string name);
     string   getNameForIID(in nsIIDPtr iid);
 
+    nsIEnumerator enumerateInterfaces();
+
     void autoRegisterInterfaces();
 
     nsIEnumerator enumerateInterfacesWhoseNamesStartWith(in string prefix);
 };
 
 %{C++
 #define NS_INTERFACEINFOMANAGER_SERVICE_CID            \
  { /* 13bef784-f8e0-4f96-85c1-09f9ef4f9a19 */          \
--- a/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
+++ b/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
@@ -263,41 +263,42 @@ XPTInterfaceInfoManager::GetNameForIID(c
     }
 
     return entry->GetName(_retval);
 }
 
 static PLDHashOperator
 xpti_ArrayAppender(const char* name, xptiInterfaceEntry* entry, void* arg)
 {
-    nsCOMArray<nsIInterfaceInfo>* array = static_cast<nsCOMArray<nsIInterfaceInfo>*>(arg);
+    nsISupportsArray* array = (nsISupportsArray*) arg;
 
     nsCOMPtr<nsIInterfaceInfo> ii;
-    if (NS_SUCCEEDED(EntryToInfo(entry, getter_AddRefs(ii)))) {
-      bool scriptable = false;
-      ii->IsScriptable(&scriptable);
-      if (scriptable) {
+    if (NS_SUCCEEDED(EntryToInfo(entry, getter_AddRefs(ii))))
         array->AppendElement(ii);
-      }
-    }
     return PL_DHASH_NEXT;
 }
 
 /* nsIEnumerator enumerateInterfaces (); */
-void
-XPTInterfaceInfoManager::GetScriptableInterfaces(nsCOMArray<nsIInterfaceInfo>& aInterfaces)
+NS_IMETHODIMP
+XPTInterfaceInfoManager::EnumerateInterfaces(nsIEnumerator **_retval)
 {
     // I didn't want to incur the size overhead of using nsHashtable just to
     // make building an enumerator easier. So, this code makes a snapshot of 
     // the table using an nsISupportsArray and builds an enumerator for that.
     // We can afford this transient cost.
 
+    nsCOMPtr<nsISupportsArray> array;
+    NS_NewISupportsArray(getter_AddRefs(array));
+    if (!array)
+        return NS_ERROR_UNEXPECTED;
+
     ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor);
-    aInterfaces.SetCapacity(mWorkingSet.mNameTable.Count());
-    mWorkingSet.mNameTable.EnumerateRead(xpti_ArrayAppender, &aInterfaces);
+    mWorkingSet.mNameTable.EnumerateRead(xpti_ArrayAppender, array);
+
+    return array->Enumerate(_retval);
 }
 
 struct ArrayAndPrefix
 {
     nsISupportsArray* array;
     const char*       prefix;
     uint32_t          length;
 };
--- a/xpcom/tests/Makefile.in
+++ b/xpcom/tests/Makefile.in
@@ -51,17 +51,16 @@ CPP_UNIT_TESTS = \
                  TestDeque.cpp \
                  TestFile.cpp \
                  TestHashtables.cpp \
                  TestID.cpp \
                  TestObserverArray.cpp \
                  TestObserverService.cpp \
                  TestPipe.cpp \
                  TestRefPtr.cpp \
-                 TestSettingsAPI.cpp \
                  TestTextFormatter.cpp \
                  TestTArray.cpp \
                  $(NULL)
 
 ifdef MOZ_MEMORY
 CPP_UNIT_TESTS += TestJemalloc.cpp
 endif