Merge mozilla-central and birch
authorEd Morley <emorley@mozilla.com>
Tue, 16 Jul 2013 10:02:38 +0100
changeset 138704 8a97675fcb697644c10e8de01362cfe35032f76b
parent 138615 60e5fd5a379caaf2cb6dd42e701cc98a78797ded (diff)
parent 138703 db224479321587d79ecc82539ffae0041167f8cf (current diff)
child 138705 20525d0bddae158299f6aac312200f6f7d31284c
push id24964
push userryanvm@gmail.com
push dateTue, 16 Jul 2013 20:04:09 +0000
treeherderautoland@fd10ead17ace [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central and birch
--- a/accessible/src/base/nsAccessiblePivot.cpp
+++ b/accessible/src/base/nsAccessiblePivot.cpp
@@ -411,37 +411,60 @@ nsAccessiblePivot::MovePivotInternal(Acc
   mPosition = aPosition;
   int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
   mStartOffset = mEndOffset = -1;
 
   return NotifyOfPivotChange(oldPosition, oldStart, oldEnd, aReason);
 }
 
 Accessible*
+nsAccessiblePivot::AdjustStartPosition(Accessible* aAccessible,
+                                       RuleCache& aCache,
+                                       uint16_t* aFilterResult,
+                                       nsresult* aResult)
+{
+  Accessible* matched = aAccessible;
+  *aResult = aCache.ApplyFilter(aAccessible, aFilterResult);
+
+  if (aAccessible != mRoot && aAccessible != mModalRoot) {
+    for (Accessible* temp = aAccessible->Parent();
+         temp && temp != mRoot && temp != mModalRoot; temp = temp->Parent()) {
+      uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
+      *aResult = aCache.ApplyFilter(temp, &filtered);
+      NS_ENSURE_SUCCESS(*aResult, nullptr);
+      if (filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) {
+        *aFilterResult = filtered;
+        matched = temp;
+      }
+    }
+  }
+
+  return matched;
+}
+
+Accessible*
 nsAccessiblePivot::SearchBackward(Accessible* aAccessible,
                                   nsIAccessibleTraversalRule* aRule,
                                   bool aSearchCurrent,
                                   nsresult* aResult)
 {
   *aResult = NS_OK;
 
   // Initial position could be unset, in that case return null.
   if (!aAccessible)
     return nullptr;
 
   RuleCache cache(aRule);
-  Accessible* accessible = aAccessible;
-
   uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
+  Accessible* accessible = AdjustStartPosition(aAccessible, cache,
+                                               &filtered, aResult);
+  NS_ENSURE_SUCCESS(*aResult, nullptr);
 
-  if (aSearchCurrent) {
-    *aResult = cache.ApplyFilter(accessible, &filtered);
-    NS_ENSURE_SUCCESS(*aResult, nullptr);
-    if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
-      return accessible;
+  if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)) {
+    return accessible;
   }
 
   Accessible* root = GetActiveRoot();
   while (accessible != root) {
     Accessible* parent = accessible->Parent();
     int32_t idxInParent = accessible->IndexInParent();
     while (idxInParent > 0) {
       if (!(accessible = parent->GetChildAt(--idxInParent)))
@@ -487,17 +510,17 @@ nsAccessiblePivot::SearchForward(Accessi
 
   // Initial position could be not set, in that case begin search from root.
   Accessible* root = GetActiveRoot();
   Accessible* accessible = (!aAccessible) ? root : aAccessible;
 
   RuleCache cache(aRule);
 
   uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
-  *aResult = cache.ApplyFilter(accessible, &filtered);
+  accessible = AdjustStartPosition(accessible, cache, &filtered, aResult);
   NS_ENSURE_SUCCESS(*aResult, nullptr);
   if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH))
     return accessible;
 
   while (true) {
     Accessible* firstChild = nullptr;
     while (!(filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) &&
            (firstChild = accessible->FirstChild())) {
--- a/accessible/src/base/nsAccessiblePivot.h
+++ b/accessible/src/base/nsAccessiblePivot.h
@@ -11,16 +11,17 @@
 
 #include "Accessible-inl.h"
 #include "nsAutoPtr.h"
 #include "nsTObserverArray.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
 
 class nsIAccessibleTraversalRule;
+class RuleCache;
 
 /**
  * Class represents an accessible pivot.
  */
 class nsAccessiblePivot MOZ_FINAL : public nsIAccessiblePivot
 {
 public:
   typedef mozilla::a11y::Accessible Accessible;
@@ -86,16 +87,28 @@ private:
   }
 
   /*
    * Update the pivot, and notify observers. Return true if it moved.
    */
   bool MovePivotInternal(Accessible* aPosition, PivotMoveReason aReason);
 
   /*
+   * Get initial node we should start a search from with a given rule.
+   *
+   * When we do a move operation from one position to another,
+   * the initial position can be inside of a subtree that is ignored by
+   * the given rule. We need to step out of the ignored subtree and start
+   * the search from there.
+   *
+   */
+  Accessible* AdjustStartPosition(Accessible* aAccessible, RuleCache& aCache,
+                                  uint16_t* aFilterResult, nsresult* aResult);
+
+  /*
    * The root accessible.
    */
   nsRefPtr<Accessible> mRoot;
 
   /*
    * The temporary modal root accessible.
    */
   nsRefPtr<Accessible> mModalRoot;
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -1215,17 +1215,51 @@ HyperTextAccessible::GetTextAfterOffset(
         if (*aStartOffset != offset) {
           *aStartOffset = *aEndOffset;
           *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eEndWord);
         }
       }
       return GetText(*aStartOffset, *aEndOffset, aText);
 
     case BOUNDARY_LINE_START:
+      if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
+        offset = AdjustCaretOffset(offset);
+
+      // Down arrow, home key, down arrow, home key.
+      *aStartOffset = FindLineBoundary(offset, eDirNext, eSelectLine);
+      if (*aStartOffset != CharacterCount()) {
+        *aStartOffset = FindLineBoundary(*aStartOffset, eDirPrevious, eSelectBeginLine);
+        *aEndOffset = FindLineBoundary(*aStartOffset, eDirNext, eSelectLine);
+        if (*aEndOffset != CharacterCount())
+          *aEndOffset = FindLineBoundary(*aEndOffset, eDirPrevious, eSelectBeginLine);
+      } else {
+        *aEndOffset = CharacterCount();
+      }
+      return GetText(*aStartOffset, *aEndOffset, aText);
+
     case BOUNDARY_LINE_END:
+      if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
+        offset = AdjustCaretOffset(offset);
+
+      // Empty last line doesn't have own frame (a previous line contains '\n'
+      // character instead) thus we can't operate on last line separately
+      // from the previous line.
+      if (IsEmptyLastLineOffset(offset)) {
+        *aStartOffset = *aEndOffset = offset;
+        return NS_OK;
+      }
+
+      // End key, down arrow, end key.
+      *aStartOffset = FindLineBoundary(offset, eDirNext, eSelectEndLine);
+      *aEndOffset = FindLineBoundary(*aStartOffset, eDirNext, eSelectLine);
+      if (*aEndOffset != CharacterCount())
+        *aEndOffset = FindLineBoundary(*aEndOffset, eDirNext, eSelectEndLine);
+
+      return GetText(*aStartOffset, *aEndOffset, aText);
+
     case BOUNDARY_ATTRIBUTE_RANGE:
       return GetTextHelper(eGetAfter, aBoundaryType, aOffset,
                            aStartOffset, aEndOffset, aText);
 
     default:
       return NS_ERROR_INVALID_ARG;
   }
 }
--- a/accessible/tests/mochitest/pivot.js
+++ b/accessible/tests/mochitest/pivot.js
@@ -71,32 +71,42 @@ var ObjectTraversalRule =
 
 /**
  * A checker for virtual cursor changed events.
  */
 function VCChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets, aPivotMoveMethod)
 {
   this.__proto__ = new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc);
 
+  this.match = function VCChangedChecker_check(aEvent)
+  {
+    var event = null;
+    try {
+      event = aEvent.QueryInterface(nsIAccessibleVirtualCursorChangeEvent);
+    } catch (e) {
+      return false;
+    }
+
+    var expectedReason = VCChangedChecker.methodReasonMap[aPivotMoveMethod] ||
+      nsIAccessiblePivot.REASON_NONE;
+
+    return event.reason == expectedReason;
+  };
+
   this.check = function VCChangedChecker_check(aEvent)
   {
     SimpleTest.info("VCChangedChecker_check");
 
     var event = null;
     try {
       event = aEvent.QueryInterface(nsIAccessibleVirtualCursorChangeEvent);
     } catch (e) {
       SimpleTest.ok(false, "Does not support correct interface: " + e);
     }
 
-    SimpleTest.is(
-      event.reason,
-      VCChangedChecker.methodReasonMap[aPivotMoveMethod],
-      'wrong move reason');
-
     var position = aDocAcc.virtualCursor.position;
     var idMatches = position && position.DOMNode.id == aIdOrNameOrAcc;
     var nameMatches = position && position.name == aIdOrNameOrAcc;
     var accMatches = position == aIdOrNameOrAcc;
 
     SimpleTest.ok(idMatches || nameMatches || accMatches, "id or name matches",
                   "expecting " + aIdOrNameOrAcc + ", got '" +
                   prettyName(position));
@@ -191,19 +201,24 @@ function setVCRangeInvoker(aDocAcc, aTex
  */
 function setVCPosInvoker(aDocAcc, aPivotMoveMethod, aRule, aIdOrNameOrAcc)
 {
   var expectMove = (aIdOrNameOrAcc != false);
   this.invoke = function virtualCursorChangedInvoker_invoke()
   {
     VCChangedChecker.
       storePreviousPosAndOffset(aDocAcc.virtualCursor);
-    var moved = aDocAcc.virtualCursor[aPivotMoveMethod](aRule);
-    SimpleTest.ok((expectMove && moved) || (!expectMove && !moved),
-                  "moved pivot");
+    if (aPivotMoveMethod && aRule) {
+      var moved = aDocAcc.virtualCursor[aPivotMoveMethod](aRule);
+      SimpleTest.is(!!moved, !!expectMove,
+                    "moved pivot with " + aPivotMoveMethod +
+                    " to " + aIdOrNameOrAcc);
+    } else {
+      aDocAcc.virtualCursor.position = getAccessible(aIdOrNameOrAcc);
+    }
   };
 
   this.getID = function setVCPosInvoker_getID()
   {
     return "Do " + (expectMove ? "" : "no-op ") + aPivotMoveMethod;
   };
 
   if (expectMove) {
@@ -442,16 +457,16 @@ function removeVCRootInvoker(aRootNode)
   ];
 }
 
 /**
  * A debug utility for writing proper sequences for queueTraversalSequence.
  */
 function dumpTraversalSequence(aPivot, aRule)
 {
-  var sequence = []
+  var sequence = [];
   if (aPivot.moveFirst(aRule)) {
     do {
       sequence.push("'" + prettyName(aPivot.position) + "'");
     } while (aPivot.moveNext(aRule))
   }
   SimpleTest.info("\n[" + sequence.join(", ") + "]\n");
 }
--- a/accessible/tests/mochitest/pivot/doc_virtualcursor.html
+++ b/accessible/tests/mochitest/pivot/doc_virtualcursor.html
@@ -10,17 +10,17 @@
   <p id="paragraph-1">
     Lorem ipsum <strong>dolor</strong> sit amet. Integer vitae urna
     leo, id <a href="#">semper</a> nulla.
   </p>
   <h2 id="heading-2-2" aria-hidden="undefined">Second Section Title</h2>
   <p id="paragraph-2" aria-hidden="">
     Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.</p>
   <p id="paragraph-3" aria-hidden="true">
-    Maybe it was the other <i>George Michael</i>.
+    <a href="#" id="hidden-link">Maybe</a> it was the other <i>George Michael</i>.
     You know, the <a href="#">singer-songwriter</a>.
   </p>
   <iframe
      src="data:text/html,<html><body>An <i>embedded</i> document.</body></html>">
   </iframe>
   <div id="hide-me">Hide me</div>
   <p id="links" aria-hidden="false">
     <a href="http://mozilla.org" title="Link 1 title">Link 1</a>
--- a/accessible/tests/mochitest/pivot/test_virtualcursor.html
+++ b/accessible/tests/mochitest/pivot/test_virtualcursor.html
@@ -90,16 +90,22 @@
         gQueue, docAcc, ObjectTraversalRule,
         getAccessible(doc.getElementById('paragraph-1')),
         ['Lorem ipsum ', 'dolor', ' sit amet. Integer vitae urna leo, id ',
          'semper', ' nulla. ']);
 
       gQueue.push(new setModalRootInvoker(docAcc, docAcc.parent,
                                           NS_ERROR_INVALID_ARG));
 
+      // Put cursor in an ignored subtree
+      gQueue.push(new setVCPosInvoker(docAcc, null, null,
+                                      getAccessible(doc.getElementById("hidden-link"))));
+      // Next item shoud be outside of that subtree
+      gQueue.push(new setVCPosInvoker(docAcc, "moveNext", ObjectTraversalRule, "An "));
+
       gQueue.invoke();
     }
 
     SimpleTest.waitForExplicitFinish();
     addLoadEvent(function () {
       /* We open a new browser because we need to test with a top-level content
          document. */
       openBrowserWindow(
--- a/accessible/tests/mochitest/text/test_atcaretoffset.html
+++ b/accessible/tests/mochitest/text/test_atcaretoffset.html
@@ -39,17 +39,17 @@
       this.__proto__ = new synthFocus("textarea");
 
       this.finalCheck = function moveToLastLineEnd_finalCheck()
       {
         testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_START, "", 15, 15,
                             ["textarea"]);
 
         testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_END, "", 15, 15,
-                            "textarea", kOk, kTodo, kTodo);
+                            [ "textarea" ]);
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_START, "words", 10, 15,
                          [ "textarea" ]);
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_END, "words", 10, 15,
                          [ "textarea" ]);
 
         testTextBeforeOffset(kCaretOffset, BOUNDARY_LINE_START, "two ", 6, 10,
@@ -67,20 +67,20 @@
 
     function moveToLastLineStart()
     {
       this.__proto__ = new moveToLineStart("textarea", 10);
 
       this.finalCheck = function moveToLastLineStart_finalCheck()
       {
         testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_START, "", 15, 15,
-                            "textarea", kTodo, kTodo, kOk);
+                            [ "textarea" ]);
 
         testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_END, "", 15, 15,
-                            "textarea", kTodo, kTodo, kOk);
+                            [ "textarea" ]);
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_START, "words", 10, 15,
                          [ "textarea" ]);
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_END, "words", 10, 15,
                          [ "textarea" ]);
 
         testTextBeforeOffset(kCaretOffset, BOUNDARY_LINE_START, "two ", 6, 10,
@@ -99,20 +99,20 @@
     function moveToMiddleLineStart()
     {
       this.__proto__ = new synthUpKey("textarea",
                                       new caretMoveChecker(6, "textarea"));
 
       this.finalCheck = function moveToMiddleLineStart_finalCheck()
       {
         testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_START, "words", 10, 15,
-                            "textarea", kTodo, kTodo, kTodo);
+                            [ "textarea" ]);
 
         testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_END, "words", 10, 15,
-                            "textarea", kTodo, kTodo, kTodo);
+                            [ "textarea" ]);
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_START, "two ", 6, 10,
                          [ "textarea" ]);
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_END, "\ntwo ", 5, 10,
                          [ "textarea" ]);
 
         testTextBeforeOffset(kCaretOffset, BOUNDARY_LINE_START, "aword\n", 0, 6,
@@ -130,20 +130,20 @@
 
     function moveToMiddleLineEnd()
     {
       this.__proto__ = new moveToLineEnd("textarea", 10);
 
       this.finalCheck = function moveToMiddleLineEnd_finalCheck()
       {
         testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_START, "words", 10, 15,
-                            "textarea", kTodo, kTodo, kTodo);
+                            [ "textarea" ]);
 
         testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_END, "words", 10, 15,
-                            "textarea", kTodo, kTodo, kOk);
+                            [ "textarea" ]);
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_START, "two ", 6, 10,
                          [ "textarea" ]);
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_END, "\ntwo ", 5, 10,
                          [ "textarea" ]);
 
         testTextBeforeOffset(kCaretOffset, BOUNDARY_LINE_START, "aword\n", 0, 6,
@@ -161,20 +161,20 @@
 
     function moveToFirstLineStart()
     {
       this.__proto__ = new moveToTextStart("textarea");
 
       this.finalCheck = function moveToFirstLineStart_finalCheck()
       {
         testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_START, "two ", 6, 10,
-                            "textarea", kTodo, kTodo, kTodo);
+                            [ "textarea" ]);
 
-        testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_END, "aword", 0, 5,
-                            "textarea", kOk, kOk, kOk);
+        testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_END, "\ntwo ", 5, 10,
+                            [ "textarea" ]);
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_START, "aword\n", 0, 6,
                          [ "textarea" ]);
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_END, "aword", 0, 5,
                          [ "textarea" ]);
 
         testTextBeforeOffset(kCaretOffset, BOUNDARY_LINE_START, "", 0, 0,
@@ -192,20 +192,20 @@
 
     function moveToFirstLineEnd()
     {
       this.__proto__ = new moveToLineEnd("textarea", 5);
 
       this.finalCheck = function moveToFirstLineStart_finalCheck()
       {
         testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_START, "two ", 6, 10,
-                            "textarea", kTodo, kTodo, kTodo);
+                            [ "textarea" ]);
 
         testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_END, "\ntwo ", 5, 10,
-                            "textarea", kTodo, kTodo, kTodo);
+                            [ "textarea" ]);
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_START, "aword\n", 0, 6,
                          [ "textarea" ]);
 
         testTextAtOffset(kCaretOffset, BOUNDARY_LINE_END, "aword", 0, 5,
                          [ "textarea" ]);
 
         testTextBeforeOffset(kCaretOffset, BOUNDARY_LINE_START, "", 0, 0,
@@ -219,18 +219,16 @@
       {
         return "move to first line end";
       }
     }
 
     var gQueue = null;
     function doTest()
     {
-      SimpleTest.expectAssertions(1);
-
       gQueue = new eventQueue();
       gQueue.push(new moveToLastLineEnd());
       gQueue.push(new moveToLastLineStart());
       gQueue.push(new moveToMiddleLineStart());
       gQueue.push(new moveToMiddleLineEnd());
       gQueue.push(new moveToFirstLineStart());
       gQueue.push(new moveToFirstLineEnd());
       gQueue.invoke(); // will call SimpleTest.finish();
--- a/accessible/tests/mochitest/text/test_multiline.html
+++ b/accessible/tests/mochitest/text/test_multiline.html
@@ -11,102 +11,45 @@
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../text.js"></script>
   <script type="application/javascript">
 
     function doTest()
     {
-      SimpleTest.expectAssertions(5);
-
       // __o__n__e__w__o__r__d__\n
       //  0  1  2  3  4  5  6  7
       // __\n
       //  8
       // __t__w__o__ __w__o__r__d__s__\n
       //  9 10 11 12 13 14 15 16 17 18
 
       ////////////////////////////////////////////////////////////////////////
       // getText
 
       var IDs = ["div", "divbr", "editable", "editablebr", "textarea"];
 
       ////////////////////////////////////////////////////////////////////////
       // getTextAfterOffset
 
       // BOUNDARY_LINE_START
-      testTextAfterOffset(0, BOUNDARY_LINE_START, "\n", 8, 9,
-                          "div", kTodo, kTodo, kTodo,
-                          "divbr", kTodo, kTodo, kTodo,
-                          "editable", kTodo, kTodo, kTodo,
-                          "editablebr", kTodo, kTodo, kTodo,
-                          "textarea", kTodo, kTodo, kTodo);
-      testTextAfterOffset(7, BOUNDARY_LINE_START, "\n", 8, 9,
-                          "div", kOk, kTodo, kTodo,
-                          "divbr", kOk, kTodo, kTodo,
-                          "editable", kOk, kTodo, kTodo,
-                          "editablebr", kOk, kTodo, kTodo,
-                          "textarea", kOk, kTodo, kTodo);
-      testTextAfterOffset(8, BOUNDARY_LINE_START, "two words\n", 9, 19,
-                          "div", kTodo, kTodo, kTodo,
-                          "divbr", kTodo, kTodo, kTodo,
-                          "editable", kTodo, kTodo, kTodo,
-                          "editablebr", kTodo, kTodo, kTodo,
-                          "textarea", kTodo, kTodo, kTodo);
-      testTextAfterOffset(9, BOUNDARY_LINE_START, "", 19, 19,
-                          "div", kTodo, kTodo, kTodo,
-                          "divbr", kTodo, kTodo, kTodo,
-                          "editable", kTodo, kTodo, kTodo,
-                          "editablebr", kTodo, kTodo, kTodo,
-                          "textarea", kTodo, kTodo, kTodo);
-      testTextAfterOffset(19, BOUNDARY_LINE_START, "", 19, 19,
-                          "div", kOk, kOk, kTodo,
-                          "divbr", kOk, kOk, kTodo,
-                          "editable", kOk, kOk, kTodo,
-                          "editablebr", kOk, kOk, kTodo,
-                          "textarea", kOk, kOk, kTodo);
+      testTextAfterOffset(0, BOUNDARY_LINE_START, "\n", 8, 9, IDs);
+      testTextAfterOffset(7, BOUNDARY_LINE_START, "\n", 8, 9, IDs);
+      testTextAfterOffset(8, BOUNDARY_LINE_START, "two words\n", 9, 19, IDs);
+      testTextAfterOffset(9, BOUNDARY_LINE_START, "", 19, 19, IDs);
+      testTextAfterOffset(19, BOUNDARY_LINE_START, "", 19, 19, IDs);
 
       // BOUNDARY_LINE_END
-      testTextAfterOffset(0, BOUNDARY_LINE_END, "\n", 7, 8,
-                          "div", kTodo, kTodo, kTodo,
-                          "divbr", kTodo, kTodo, kTodo,
-                          "editable", kTodo, kTodo, kTodo,
-                          "editablebr", kTodo, kTodo, kTodo,
-                          "textarea", kTodo, kTodo, kTodo);
-      testTextAfterOffset(7, BOUNDARY_LINE_END, "\n", 7, 8,
-                          "div", kOk, kOk, kOk,
-                          "divbr", kOk, kOk, kOk,
-                          "editable", kOk, kOk, kOk,
-                          "editablebr", kOk, kOk, kOk,
-                          "textarea", kOk, kOk, kOk);
-      testTextAfterOffset(8, BOUNDARY_LINE_END, "\ntwo words", 8, 18,
-                          "div", kOk, kOk, kOk,
-                          "divbr", kOk, kOk, kOk,
-                          "editable", kOk, kOk, kOk,
-                          "editablebr", kOk, kOk, kOk,
-                          "textarea", kOk, kOk, kOk);
-      testTextAfterOffset(9, BOUNDARY_LINE_END, "\n", 18, 19,
-                          "div", kTodo, kTodo, kTodo,
-                          "divbr", kTodo, kTodo, kTodo,
-                          "editable", kTodo, kTodo, kTodo,
-                          "editablebr", kTodo, kTodo, kTodo,
-                          "textarea", kTodo, kTodo, kTodo);
-      testTextAfterOffset(18, BOUNDARY_LINE_END, "\n", 18, 19,
-                          "div", kOk, kOk, kOk,
-                          "divbr", kOk, kOk, kOk,
-                          "editable", kOk, kOk, kOk,
-                          "editablebr", kOk, kOk, kOk,
-                          "textarea", kOk, kOk, kOk);
-      testTextAfterOffset(19, BOUNDARY_LINE_END, "", 19, 19,
-                          "div", kOk, kTodo, kTodo,
-                          "divbr", kOk, kTodo, kTodo,
-                          "editable", kOk, kTodo, kTodo,
-                          "editablebr", kOk, kTodo, kTodo,
-                          "textarea", kOk, kTodo, kTodo);
+      testTextAfterOffset(0, BOUNDARY_LINE_END, "\n", 7, 8, IDs);
+      testTextAfterOffset(7, BOUNDARY_LINE_END, "\n", 7, 8, IDs);
+      testTextAfterOffset(8, BOUNDARY_LINE_END, "\ntwo words", 8, 18, IDs);
+      testTextAfterOffset(9, BOUNDARY_LINE_END, "\n", 18, 19, IDs);
+      testTextAfterOffset(18, BOUNDARY_LINE_END, "\n", 18, 19, IDs);
+      testTextAfterOffset(19, BOUNDARY_LINE_END, "", 19, 19, IDs);
 
       ////////////////////////////////////////////////////////////////////////
       // getTextBeforeOffset
 
       // BOUNDARY_LINE_START
       testTextBeforeOffset(0, BOUNDARY_LINE_START, "", 0, 0, IDs);
       testTextBeforeOffset(8, BOUNDARY_LINE_START, "oneword\n", 0, 8, IDs);
       testTextBeforeOffset(9, BOUNDARY_LINE_START, "\n", 8, 9, IDs);
@@ -161,16 +104,21 @@
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=853340">
     Bug 853340
   </a>
   <a target="_blank"
      title="getTextBeforeOffset for word boundaries: evolving"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=855732">
     Bug 855732
   </a>
+  <a target="_blank"
+     title=" getTextAfterOffset for line boundary on new rails"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=882292">
+    Bug 882292
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
 
   <div id="div">oneword
 
 two words
 </div>
--- a/accessible/tests/mochitest/text/test_singleline.html
+++ b/accessible/tests/mochitest/text/test_singleline.html
@@ -6,76 +6,38 @@
 
   <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="../text.js"></script>
   <script type="application/javascript">
-    if (navigator.platform.startsWith("Mac")) {
-      SimpleTest.expectAssertions(0, 4);
-    } else {
-      SimpleTest.expectAssertions(4);
-    }
-
     function doTest()
     {
       // __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__
       //  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
 
       ////////////////////////////////////////////////////////////////////////
       // getTextAfterOffset
 
       var IDs = [ "input", "div", "editable", "textarea" ];
       var regularIDs = [ "input", "div", "editable" ];
 
       // BOUNDARY_LINE_START
-      testTextAfterOffset(0, BOUNDARY_LINE_START, "", 15, 15,
-                          "input", kTodo, kTodo, kOk,
-                          "div", kTodo, kTodo, kOk,
-                          "editable", kTodo, kTodo, kOk,
-                          "textarea", kTodo, kTodo, kOk);
-      testTextAfterOffset(1, BOUNDARY_LINE_START, "", 15, 15,
-                          "input", kTodo, kTodo, kOk,
-                          "div", kTodo, kTodo, kOk,
-                          "editable", kTodo, kTodo, kOk,
-                          "textarea", kTodo, kTodo, kOk);
-      testTextAfterOffset(14, BOUNDARY_LINE_START, "", 15, 15,
-                          "input", kTodo, kTodo, kOk,
-                          "div", kTodo, kTodo, kOk,
-                          "editable", kTodo, kTodo, kOk,
-                          "textarea", kTodo, kTodo, kOk);
-      testTextAfterOffset(15, BOUNDARY_LINE_START, "", 15, 15,
-                          "input", kOk, kOk, kOk,
-                          "div", kOk, kOk, kOk,
-                          "editable", kOk, kOk, kOk,
-                          "textarea", kOk, kOk, kOk);
+      testTextAfterOffset(0, BOUNDARY_LINE_START, "", 15, 15, IDs);
+      testTextAfterOffset(1, BOUNDARY_LINE_START, "", 15, 15, IDs);
+      testTextAfterOffset(14, BOUNDARY_LINE_START, "", 15, 15, IDs);
+      testTextAfterOffset(15, BOUNDARY_LINE_START, "", 15, 15, IDs);
 
       // BOUNDARY_LINE_END
-      testTextAfterOffset(0, BOUNDARY_LINE_END, "", 15, 15,
-                          "input", kTodo, kTodo, kOk,
-                          "div", kTodo, kTodo, kOk,
-                          "editable", kTodo, kTodo, kOk,
-                          "textarea", kTodo, kTodo, kOk);
-      testTextAfterOffset(1, BOUNDARY_LINE_END, "", 15, 15,
-                          "input", kTodo, kTodo, kOk,
-                          "div", kTodo, kTodo, kOk,
-                          "editable", kTodo, kTodo, kOk,
-                          "textarea", kTodo, kTodo, kOk);
-      testTextAfterOffset(14, BOUNDARY_LINE_END, "", 15, 15,
-                          "input", kTodo, kTodo, kOk,
-                          "div", kTodo, kTodo, kOk,
-                          "editable", kTodo, kTodo, kOk,
-                          "textarea", kTodo, kTodo, kOk);
-      testTextAfterOffset(15, BOUNDARY_LINE_END, "", 15, 15,
-                          "input", kOk, kTodo, kTodo,
-                          "div", kOk, kTodo, kTodo,
-                          "editable", kOk, kTodo, kTodo,
-                          "textarea", kOk, kTodo, kTodo);
+      testTextAfterOffset(0, BOUNDARY_LINE_END, "", 15, 15, IDs);
+      testTextAfterOffset(1, BOUNDARY_LINE_END, "", 15, 15, IDs);
+      testTextAfterOffset(14, BOUNDARY_LINE_END, "", 15, 15, IDs);
+      testTextAfterOffset(15, BOUNDARY_LINE_END, "", 15, 15, IDs);
 
       ////////////////////////////////////////////////////////////////////////
       // getTextBeforeOffset
 
       var IDs = [ "input", "div", "editable", "textarea" ];
 
       // BOUNDARY_LINE_START
       testTextBeforeOffset(0, BOUNDARY_LINE_START, "", 0, 0, IDs);
@@ -126,16 +88,21 @@
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=853340">
     Bug 853340
   </a>
   <a target="_blank"
      title="getTextBeforeOffset for word boundaries: evolving"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=855732">
     Bug 855732
   </a>
+  <a target="_blank"
+     title=" getTextAfterOffset for line boundary on new rails"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=882292">
+    Bug 882292
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <input id="input" value="hello my friend"/>
   <div id="div">hello my friend</div>
   <div id="editable" contenteditable="true">hello my friend</div>
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -554,20 +554,23 @@ pref("dom.experimental_forms", true);
 pref("gfx.gralloc.enabled", false);
 
 // XXXX REMOVE FOR PRODUCTION. Turns on GC and CC logging
 pref("javascript.options.mem.log", false);
 
 // Increase mark slice time from 10ms to 30ms
 pref("javascript.options.mem.gc_incremental_slice_ms", 30);
 
-pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 150);
+// Increase time to get more high frequency GC on benchmarks from 1000ms to 1500ms
+pref("javascript.options.mem.gc_high_frequency_time_limit_ms", 1500);
+
+pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 300);
 pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 120);
 pref("javascript.options.mem.gc_high_frequency_high_limit_mb", 40);
-pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 10);
+pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 0);
 pref("javascript.options.mem.gc_low_frequency_heap_growth", 120);
 pref("javascript.options.mem.high_water_mark", 6);
 pref("javascript.options.mem.gc_allocation_threshold_mb", 1);
 pref("javascript.options.mem.gc_decommit_threshold_mb", 1);
 
 // Show/Hide scrollbars when active/inactive
 pref("ui.showHideScrollbars", 1);
 
--- a/browser/Makefile.in
+++ b/browser/Makefile.in
@@ -14,8 +14,11 @@ ifdef MAKENSISU
 # For Windows build the uninstaller during the application build since the
 # uninstaller is included with the application for mar file generation.
 libs::
 	$(MAKE) -C installer/windows uninstaller
 ifdef MOZ_MAINTENANCE_SERVICE
 	$(MAKE) -C installer/windows maintenanceservice_installer
 endif
 endif
+
+check::
+	$(PYTHON) $(topsrcdir)/build/compare-mozconfig/compare-mozconfigs-wrapper.py
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3663,20 +3663,18 @@ var XULBrowserWindow = {
   get isImage () {
     delete this.isImage;
     return this.isImage = document.getElementById("isImage");
   },
 
   init: function () {
     this.throbberElement = document.getElementById("navigator-throbber");
 
-    // Initialize the security button's state and tooltip text.  Remember to reset
-    // _hostChanged, otherwise onSecurityChange will short circuit.
+    // Initialize the security button's state and tooltip text.
     var securityUI = gBrowser.securityUI;
-    this._hostChanged = true;
     this.onSecurityChange(null, null, securityUI.state);
   },
 
   destroy: function () {
     // XXXjag to avoid leaks :-/, see bug 60729
     delete this.throbberElement;
     delete this.stopCommand;
     delete this.reloadCommand;
@@ -3857,17 +3855,16 @@ var XULBrowserWindow = {
         this.stopCommand.setAttribute("disabled", "true");
         CombinedStopReload.switchToReload(aRequest instanceof Ci.nsIRequest);
       }
     }
   },
 
   onLocationChange: function (aWebProgress, aRequest, aLocationURI, aFlags) {
     var location = aLocationURI ? aLocationURI.spec : "";
-    this._hostChanged = true;
 
     // Hide the form invalid popup.
     if (gFormSubmitObserver.panel) {
       gFormSubmitObserver.panel.hidePopup();
     }
 
     let pageTooltip = document.getElementById("aHTMLTooltip");
     let tooltipNode = pageTooltip.triggerNode;
@@ -3999,47 +3996,28 @@ var XULBrowserWindow = {
 
   onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
     this.status = aMessage;
     this.updateStatusField();
   },
 
   // Properties used to cache security state used to update the UI
   _state: null,
-  _hostChanged: false, // onLocationChange will flip this bit
+  _lastLocation: null,
 
   onSecurityChange: function (aWebProgress, aRequest, aState) {
     // Don't need to do anything if the data we use to update the UI hasn't
     // changed
+    let uri = gBrowser.currentURI;
+    let spec = uri.spec;
     if (this._state == aState &&
-        !this._hostChanged) {
-#ifdef DEBUG
-      try {
-        var contentHost = gBrowser.contentWindow.location.host;
-        if (this._host !== undefined && this._host != contentHost) {
-            Components.utils.reportError(
-              "ASSERTION: browser.js host is inconsistent. Content window has " +
-              "<" + contentHost + "> but cached host is <" + this._host + ">.\n"
-            );
-        }
-      } catch (ex) {}
-#endif
+        this._lastLocation == spec)
       return;
-    }
     this._state = aState;
-
-#ifdef DEBUG
-    try {
-      this._host = gBrowser.contentWindow.location.host;
-    } catch(ex) {
-      this._host = null;
-    }
-#endif
-
-    this._hostChanged = false;
+    this._lastLocation = spec;
 
     // aState is defined as a bitmask that may be extended in the future.
     // We filter out any unknown bits before testing for known values.
     const wpl = Components.interfaces.nsIWebProgressListener;
     const wpl_security_bits = wpl.STATE_IS_SECURE |
                               wpl.STATE_IS_BROKEN |
                               wpl.STATE_IS_INSECURE;
     var level;
@@ -4058,17 +4036,16 @@ var XULBrowserWindow = {
       // anymore, but still set it for third-party themes.
       if (gURLBar)
         gURLBar.setAttribute("level", level);
     } else {
       if (gURLBar)
         gURLBar.removeAttribute("level");
     }
 
-    let uri = gBrowser.currentURI;
     try {
       uri = Services.uriFixup.createExposableURI(uri);
     } catch (e) {}
     gIdentityHandler.checkIdentity(this._state, uri);
   },
 
   // simulate all change notifications after switching tabs
   onUpdateCurrentBrowser: function XWB_onUpdateCurrentBrowser(aStateFlags, aStatus, aMessage, aTotalProgress) {
--- a/browser/base/content/test/browser_CTP_drag_drop.js
+++ b/browser/base/content/test/browser_CTP_drag_drop.js
@@ -82,17 +82,17 @@ function part7() {
   ok(PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser), "Should have a click-to-play notification in the tab in the new window");
   ok(!PopupNotifications.getNotification("click-to-play-plugins", gBrowser.selectedBrowser), "Should not have a click-to-play notification in the old window now");
 
   let plugin = gNewWindow.gBrowser.selectedBrowser.contentDocument.getElementById("test");
   let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
   ok(!objLoadingContent.activated, "plugin should not be activated");
 
   EventUtils.synthesizeMouseAtCenter(plugin, {}, gNewWindow.gBrowser.selectedBrowser.contentWindow);
-  let condition = function() !PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser).dismissed;
+  let condition = function() !PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser).dismissed && gNewWindow.PopupNotifications.panel.firstChild;
   waitForCondition(condition, part8, "waited too long for plugin to activate");
 }
 
 function part8() {
   // Click the activate button on doorhanger to make sure it works
   gNewWindow.PopupNotifications.panel.firstChild._primaryButton.click();
 
   let plugin = gNewWindow.gBrowser.selectedBrowser.contentDocument.getElementById("test");
--- a/browser/base/content/test/browser_bug882977.js
+++ b/browser/base/content/test/browser_bug882977.js
@@ -29,12 +29,11 @@ function test() {
       browser.removeEventListener("load", onLoad, true);
       checkIdentityMode(win);
     }, true);
   });
 }
 
 function checkIdentityMode(win) {
   let identityMode = win.document.getElementById("identity-box").className;
-  is(identityMode, "unknownIdentity", "Identity should be chromeUI but is currently " +
-                                      "shown as unknownIdentity for new windows.");
+  is(identityMode, "chromeUI", "Identity state should be chromeUI for about:home in a new window");
   finish();
 }
index afce613e2fd2d6dedb69c453ea978d9aeb7692ef..28f13894ed2873e7f2bbc76a31dea75b61c81956
GIT binary patch
literal 1800
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
index 531ff5dec0cd353a41a0c7799bcefcc47fbcd8d8..ff0e6cad2daf18e83e0350d514272b3e56b0231b
GIT binary patch
literal 4802
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
index afce613e2fd2d6dedb69c453ea978d9aeb7692ef..feba1607a9bb7fa8a5be6f335cdeb32fc3e70636
GIT binary patch
literal 1624
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
WVxXp!azq>e0000<MNUMnLSTZ|8Wx!V
index 531ff5dec0cd353a41a0c7799bcefcc47fbcd8d8..9b2d6bc66f21ff97506d6f09497049c5af18e4ab
GIT binary patch
literal 4283
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
index a39e493c1eb1eaacc6b40c32fb6eebe683021b3f..feba1607a9bb7fa8a5be6f335cdeb32fc3e70636
GIT binary patch
literal 1624
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
WVxXp!azq>e0000<MNUMnLSTZ|8Wx!V
index 794d54ecae5277de16d1b9ae6f030c7d7b0678f7..9b2d6bc66f21ff97506d6f09497049c5af18e4ab
GIT binary patch
literal 4283
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
--- a/browser/components/places/tests/chrome/test_bug631374_tags_selector_scroll.xul
+++ b/browser/components/places/tests/chrome/test_bug631374_tags_selector_scroll.xul
@@ -113,25 +113,30 @@
 
           // Doing this backwords tests more interesting paths.
           for (let i = tags.length - 1; i >= 0 ; i -= 2) {
             tagsSelector.selectedIndex = i;
             let listItem = tagsSelector.selectedItem;
             SimpleTest.isnot(listItem, null, "Valid listItem found");
 
             tagsSelector.ensureElementIsVisible(listItem);
-            let visibleIndex = tagsSelector.getIndexOfFirstVisibleRow();
+            let firstVisibleTag = tags[tagsSelector.getIndexOfFirstVisibleRow()];
 
             SimpleTest.ok(listItem.checked, "Item is checked " + i);
             let selectedTag = listItem.label;
 
             // Uncheck the tag.
             listItem.checked = false;
-            SimpleTest.is(tagsSelector.getIndexOfFirstVisibleRow(),
-                          Math.max(visibleIndex -1, 0),
+
+            // Ensure the first visible tag is still visible in the list.
+            let firstVisibleIndex = tagsSelector.getIndexOfFirstVisibleRow();
+            let lastVisibleIndex = firstVisibleIndex + tagsSelector.getNumberOfVisibleRows() -1;
+            let expectedTagIndex = tags.indexOf(firstVisibleTag);
+            SimpleTest.ok(expectedTagIndex >= firstVisibleIndex &&
+                          expectedTagIndex <= lastVisibleIndex,
                           "Scroll position is correct");
 
             // The listbox is rebuilt, so we have to get the new element.
             let newItem = tagsSelector.selectedItem;
             SimpleTest.isnot(newItem, null, "Valid new listItem found");
             SimpleTest.ok(newItem.checked, "New listItem is checked " + i);
             SimpleTest.is(tagsSelector.selectedItem.label,
                           tags[Math.min(i + 1, tags.length - 2)],
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/whitelist
@@ -0,0 +1,90 @@
+# 'nightly' contains things that are in nightly mozconfigs and allowed to be missing from release builds.
+# Other keys in whitelist contain things are in that branches mozconfigs and allowed to be missing from nightly builds.
+whitelist = {
+    'release': {},
+    'nightly': {},
+    }
+
+all_platforms = ['win32', 'linux32', 'linux64', 'macosx-universal']
+
+for platform in all_platforms:
+    whitelist['nightly'][platform] = [
+        'ac_add_options --enable-update-channel=nightly',
+        'ac_add_options --enable-profiling',
+        'mk_add_options CLIENT_PY_ARGS="--hg-options=\'--verbose --time\' --hgtool=../tools/buildfarm/utils/hgtool.py --skip-chatzilla --skip-comm --skip-inspector --skip-venkman --tinderbox-print"'
+    ]
+
+for platform in ['linux32', 'linux64', 'macosx-universal']:
+    whitelist['nightly'][platform] += [
+        'ac_add_options --enable-codesighs',
+        'mk_add_options MOZ_MAKE_FLAGS="-j4"',
+    ]
+
+for platform in ['linux32', 'linux64', 'macosx-universal', 'win32']:
+    whitelist['nightly'][platform] += ['ac_add_options --enable-signmar']
+    whitelist['nightly'][platform] += ['ac_add_options --enable-js-diagnostics']
+
+whitelist['nightly']['linux32'] += [
+    'CXX=$REAL_CXX',
+    'CXX="ccache $REAL_CXX"',
+    'CC="ccache $REAL_CC"',
+    'mk_add_options PROFILE_GEN_SCRIPT=@TOPSRCDIR@/build/profile_pageloader.pl',
+    'ac_add_options --with-ccache=/usr/bin/ccache',
+    'export MOZILLA_OFFICIAL=1',
+    'export MOZ_TELEMETRY_REPORTING=1',
+    "mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'",
+    'STRIP_FLAGS="--strip-debug"',
+    'ac_add_options --disable-elf-hack # --enable-elf-hack conflicts with --enable-profiling',
+]
+
+whitelist['nightly']['linux64'] += [
+    'export MOZILLA_OFFICIAL=1',
+    'export MOZ_TELEMETRY_REPORTING=1',
+    "mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'",
+    'STRIP_FLAGS="--strip-debug"',
+    'ac_add_options --with-ccache=/usr/bin/ccache',
+    'ac_add_options --disable-elf-hack # --enable-elf-hack conflicts with --enable-profiling',
+]
+
+whitelist['nightly']['macosx-universal'] += [
+    'ac_add_options --with-macbundlename-prefix=Firefox',
+    'mk_add_options MOZ_MAKE_FLAGS="-j12"',
+    'ac_add_options --with-ccache',
+    'ac_add_options --disable-install-strip',
+    'ac_add_options --enable-instruments',
+    'ac_add_options --enable-dtrace',
+]
+
+whitelist['nightly']['win32'] += [
+    '. $topsrcdir/configs/mozilla2/win32/include/choose-make-flags',
+    'mk_add_options MOZ_MAKE_FLAGS=-j1',
+    'if test "$IS_NIGHTLY" != ""; then',
+    'ac_add_options --disable-auto-deps',
+    'fi',
+    'ac_add_options --enable-metro',
+]
+
+for platform in all_platforms:
+    whitelist['release'][platform] = [
+        'ac_add_options --enable-update-channel=release',
+        'ac_add_options --enable-official-branding',
+        'mk_add_options MOZ_MAKE_FLAGS="-j4"',
+        'export BUILDING_RELEASE=1',
+    ]
+whitelist['release']['win32'] += ['mk_add_options MOZ_PGO=1']
+whitelist['release']['linux32'] += [
+    'export MOZILLA_OFFICIAL=1',
+    'export MOZ_TELEMETRY_REPORTING=1',
+    'mk_add_options MOZ_PGO=1',
+    "mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'",
+]
+whitelist['release']['linux64'] += [
+    'export MOZILLA_OFFICIAL=1',
+    'export MOZ_TELEMETRY_REPORTING=1',
+    'mk_add_options MOZ_PGO=1',
+    "mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'",
+]
+
+if __name__ == '__main__':
+    import pprint
+    pprint.pprint(whitelist)
--- a/browser/metro/base/content/ContextCommands.js
+++ b/browser/metro/base/content/ContextCommands.js
@@ -156,17 +156,17 @@ var ContextCommands = {
     aRichListItem.setAttribute("searchString", "");
     BrowserUI.newTab(defaultURI, Browser.selectedTab);
   },
 
   // Link specific
 
   openLinkInNewTab: function cc_openLinkInNewTab() {
     Browser.addTab(ContextMenuUI.popupState.linkURL, false, Browser.selectedTab);
-    ContextUI.peekTabs();
+    ContextUI.peekTabs(kOpenInNewTabAnimationDelayMsec);
   },
 
   copyLink: function cc_copyLink() {
     this.clipboard.copyString(ContextMenuUI.popupState.linkURL,
                               this.docRef);
   },
 
   bookmarkLink: function cc_bookmarkLink() {
--- a/browser/metro/base/content/ContextUI.js
+++ b/browser/metro/base/content/ContextUI.js
@@ -5,22 +5,16 @@
 // Fired when any context ui is displayed
 const kContextUIShowEvent = "MozContextUIShow";
 // Fired when any context ui is dismissed
 const kContextUIDismissEvent = "MozContextUIDismiss";
 // Fired when the tabtray is displayed
 const kContextUITabsShowEvent = "MozContextUITabsShow";
 // add more as needed...
 
-// delay for ContextUI's dismissTabsWithDelay
-const kHideContextAndTrayDelayMsec = 3000;
-
-// delay when showing the tab bar briefly as a new tab opens
-const kNewTabAnimationDelayMsec = 1000;
-
 /*
  * Manages context UI (navbar, tabbar, appbar) and track visibility. Also
  * tracks events that summon and hide the context UI.
  */
 var ContextUI = {
   _expandable: true,
   _hidingId: 0,
 
@@ -155,63 +149,46 @@ var ContextUI = {
     }
 
     return dismissed;
   },
 
   /*
    * Briefly show the tab bar and then hide it. Fires context ui events.
    */
-  peekTabs: function peekTabs() {
-    if (this.tabbarVisible) {
-      setTimeout(function () {
-        ContextUI.dismissTabsWithDelay(kNewTabAnimationDelayMsec);
-      }, 0);
-    } else {
-      BrowserUI.setOnTabAnimationEnd(function () {
-        ContextUI.dismissTabsWithDelay(kNewTabAnimationDelayMsec);
-      });
+  peekTabs: function peekTabs(aDelay) {
+    if (!this.tabbarVisible)
       this.displayTabs();
-    }
+
+    ContextUI.dismissTabsWithDelay(aDelay);
   },
 
   /*
    * Dismiss tab bar after a delay. Fires context ui events.
    */
   dismissTabsWithDelay: function (aDelay) {
-    aDelay = aDelay || kHideContextAndTrayDelayMsec;
+    aDelay = aDelay || kNewTabAnimationDelayMsec;
     this._clearDelayedTimeout();
     this._hidingId = setTimeout(function () {
         ContextUI.dismissTabs();
       }, aDelay);
   },
 
-  // Cancel any pending delayed dismiss
-  cancelDismiss: function cancelDismiss() {
-    this._clearDelayedTimeout();
-  },
-
   // Display the nav bar
   displayNavbar: function () {
     this._clearDelayedTimeout();
     Elements.navbar.show();
   },
 
   // Display the tab tray
   displayTabs: function () {
     this._clearDelayedTimeout();
     this._setIsExpanded(true);
   },
 
-  // Display the app bar
-  displayContextAppbar: function () {
-    this._clearDelayedTimeout();
-    Elements.contextappbar.show();
-  },
-
   // Dismiss the navbar if visible.
   dismissNavbar: function dismissNavbar() {
     Elements.navbar.dismiss();
   },
 
   // Dismiss the tabstray if visible.
   dismissTabs: function dimissTabs() {
     this._clearDelayedTimeout();
@@ -307,25 +284,19 @@ var ContextUI = {
         break;
       case "KeyboardChanged":
         this.dismissTabs();
         break;
       case "mousedown":
         if (aEvent.button == 0 && this.isVisible)
           this.dismiss();
         break;
-      case 'URLChanged':
-        this.dismissTabs();
-        break;
-      case 'TabSelect':
-        this.dismissTabs();
-        break;
 
-      case 'ToolPanelShown':
-      case 'ToolPanelHidden':
+      case "ToolPanelShown":
+      case "ToolPanelHidden":
       case "touchstart":
       case "AlertActive":
         this.dismiss();
         break;
     }
   },
 
   _fire: function (name) {
--- a/browser/metro/base/content/WebProgress.js
+++ b/browser/metro/base/content/WebProgress.js
@@ -79,18 +79,20 @@ const WebProgress = {
       identityBox.className = aTab._identityState;
     }
   },
 
   _locationChange: function _locationChange(aJson, aTab) {
     let spec = aJson.location;
     let location = spec.split("#")[0]; // Ignore fragment identifier changes.
 
-    if (aTab == Browser.selectedTab)
+    if (aTab == Browser.selectedTab) {
       BrowserUI.updateURI();
+      BrowserUI.update();
+    }
 
     let locationHasChanged = (location != aTab.browser.lastLocation);
     if (locationHasChanged) {
       Browser.getNotificationBox(aTab.browser).removeTransientNotifications();
       aTab.resetZoomLevel();
       aTab.browser.lastLocation = location;
       aTab.browser.userTypedValue = "";
       aTab.browser.appIcon = { href: null, size:-1 };
@@ -118,32 +120,25 @@ const WebProgress = {
       aTab.scrolledAreaChanged(true);
       aTab.updateThumbnailSource();
     });
   },
 
   _networkStart: function _networkStart(aJson, aTab) {
     aTab.startLoading();
 
-    if (aTab == Browser.selectedTab) {
+    if (aTab == Browser.selectedTab)
       BrowserUI.update(TOOLBARSTATE_LOADING);
-
-      // We should at least show something in the URLBar until
-      // the load has progressed further along
-      if (aTab.browser.currentURI.spec == "about:blank")
-        BrowserUI.updateURI({ captionOnly: true });
-    }
   },
 
   _networkStop: function _networkStop(aJson, aTab) {
     aTab.endLoading();
 
-    if (aTab == Browser.selectedTab) {
+    if (aTab == Browser.selectedTab)
       BrowserUI.update(TOOLBARSTATE_LOADED);
-    }
   },
 
   _windowStart: function _windowStart(aJson, aTab) {
     this._progressStart(aJson, aTab);
   },
 
   _windowStop: function _windowStop(aJson, aTab) {
     this._progressStop(aJson, aTab);
@@ -157,17 +152,17 @@ const WebProgress = {
 
     this._progressActive = true;
 
     // display the track
     Elements.progressContainer.removeAttribute("collapsed");
 
     // 'Whoosh' in
     this._progressCount = kProgressMarginStart;
-    Elements.progress.style.width = this._progressCount + "%"; 
+    Elements.progress.style.width = this._progressCount + "%";
     Elements.progress.removeAttribute("fade");
 
     // Create a pulse timer to keep things moving even if we don't
     // collect any state changes.
     setTimeout(function() {
       WebProgress._progressStepTimer();
     }, kHeartbeatDuration, this);
   },
@@ -199,26 +194,26 @@ const WebProgress = {
     setTimeout(function() {
       WebProgress._progressStepTimer();
     }, kHeartbeatDuration, this);
   },
 
   _progressStop: function _progressStop(aJson, aTab) {
     this._progressActive = false;
     // 'Whoosh out' and fade
-    Elements.progress.style.width = "100%"; 
+    Elements.progress.style.width = "100%";
     Elements.progress.setAttribute("fade", true);
   },
 
   _progressTransEnd: function _progressTransEnd(data) {
     if (!Elements.progress.hasAttribute("fade"))
       return;
     // Close out fade finished, reset
     if (data.propertyName == "opacity") {
-      Elements.progress.style.width = "0px"; 
+      Elements.progress.style.width = "0px";
       Elements.progressContainer.setAttribute("collapsed", true);
     }
   },
 
   _onTabSelect: function(aEvent) {
     let identityBox = document.getElementById("identity-box-inner");
     let tab = Browser.getTabFromChrome(aEvent.originalTarget);
     identityBox.className = tab._identityState || "";
--- a/browser/metro/base/content/appbar.js
+++ b/browser/metro/base/content/appbar.js
@@ -110,17 +110,17 @@ var Appbar = {
                                      null, null);
         if (uri.schemeIs('http') || uri.schemeIs('https')) {
           typesArray.push("view-on-desktop");
         }
       } catch(ex) {
       }
 
       var x = this.menuButton.getBoundingClientRect().left;
-      var y = Elements.navbar.getBoundingClientRect().top;
+      var y = Elements.toolbar.getBoundingClientRect().top;
       ContextMenuUI.showContextMenu({
         json: {
           types: typesArray,
           string: '',
           xPos: x,
           yPos: y,
           leftAligned: true,
           bottomAligned: true
--- a/browser/metro/base/content/bindings/grid.xml
+++ b/browser/metro/base/content/bindings/grid.xml
@@ -81,22 +81,30 @@
               this._fireOnSelectionChange();
             }
           ]]>
         </body>
       </method>
 
       <method name="handleItemClick">
         <parameter name="aItem"/>
+        <parameter name="aEvent"/>
         <body>
           <![CDATA[
             if(!this.isBound)
               return;
-            if (this.controller)
-              this.controller.handleItemClick(aItem);
+
+            if ("single" == this.getAttribute("seltype")) {
+              // we'll republish this as a selectionchange event on the grid
+              aEvent.stopPropagation();
+              this.selectItem(aItem);
+            }
+
+	    if (this.controller && this.controller.handleItemClick)
+              this.controller.handleItemClick(aItem, aEvent);
           ]]>
         </body>
       </method>
 
       <method name="handleItemContextMenu">
         <parameter name="aItem"/>
         <parameter name="aEvent"/>
         <body>
rename from browser/metro/base/content/bindings/autocomplete.xml
rename to browser/metro/base/content/bindings/urlbar.xml
--- a/browser/metro/base/content/bindings/autocomplete.xml
+++ b/browser/metro/base/content/bindings/urlbar.xml
@@ -9,294 +9,639 @@
 %browserDTD;
 ]>
 
 <bindings
     xmlns="http://www.mozilla.org/xbl"
     xmlns:xbl="http://www.mozilla.org/xbl"
     xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
-  <binding id="autocomplete" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
-    <implementation>
+  <binding id="urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
+    <implementation implements="nsIObserver">
       <constructor>
-          <![CDATA[
-            this.minResultsForPopup = 0;
-            this.popup._input = this;
-          ]]>
+        <![CDATA[
+          this._mayFormat = Services.prefs.getBoolPref("browser.urlbar.formatting.enabled");
+          this._mayTrimURLs = Services.prefs.getBoolPref("browser.urlbar.trimURLs");
+          this._maySelectAll = Services.prefs.getBoolPref("browser.urlbar.doubleClickSelectsAll");
+
+          Services.prefs.addObserver("browser.urlbar.formatting.enabled", this, false);
+          Services.prefs.addObserver("browser.urlbar.trimURLs", this, false);
+          Services.prefs.addObserver("browser.urlbar.doubleClickSelectsAll", this, false);
+
+          this.inputField.controllers.insertControllerAt(0, this._copyCutValueController);
+
+          this.minResultsForPopup = 0;
+          this.popup._input = this;
+        ]]>
       </constructor>
 
+      <destructor>
+        <![CDATA[
+          Services.prefs.removeObserver("browser.urlbar.formatting.enabled", this);
+          Services.prefs.removeObserver("browser.urlbar.trimURLs", this);
+          Services.prefs.removeObserver("browser.urlbar.doubleClickSelectsAll", this);
+        ]]>
+      </destructor>
+
+      <field name="_mayFormat"/>
+      <field name="_mayTrimURLs"/>
+      <field name="_maySelectAll"/>
+      <field name="_lastKnownGoodURL"/>
+
       <method name="openPopup">
         <body>
           <![CDATA[
             this.popup.openAutocompletePopup(this, null);
           ]]>
         </body>
       </method>
 
       <method name="closePopup">
         <body>
           <![CDATA[
             this.popup.closePopup(this, null);
           ]]>
         </body>
       </method>
 
+      <!-- URI Display: Domain Highlighting -->
+
+      <method name="_clearFormatting">
+        <body>
+          <![CDATA[
+            if (!this._mayFormat)
+              return;
+
+            let controller = this.editor.selectionController;
+            let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
+            selection.removeAllRanges();
+          ]]>
+        </body>
+      </method>
+
       <method name="formatValue">
         <body>
           <![CDATA[
-            BrowserUI.formatURI();
+            if (!this._mayFormat || this.isEditing)
+              return;
+
+            let controller = this.editor.selectionController;
+            let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
+            selection.removeAllRanges();
+
+            let textNode = this.editor.rootElement.firstChild;
+            let value = textNode.textContent;
+
+            let protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/);
+            if (protocol &&
+                ["http:", "https:", "ftp:"].indexOf(protocol[0]) == -1)
+              return;
+            let matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
+            if (!matchedURL)
+              return;
+
+            let [, preDomain, domain] = matchedURL;
+            let baseDomain = domain;
+            let subDomain = "";
+            // getBaseDomainFromHost doesn't recognize IPv6 literals in brackets as IPs (bug 667159)
+            if (domain[0] != "[") {
+              try {
+                baseDomain = Services.eTLD.getBaseDomainFromHost(domain);
+                if (!domain.endsWith(baseDomain)) {
+                  // getBaseDomainFromHost converts its resultant to ACE.
+                  let IDNService = Cc["@mozilla.org/network/idn-service;1"]
+                                   .getService(Ci.nsIIDNService);
+                  baseDomain = IDNService.convertACEtoUTF8(baseDomain);
+                }
+              } catch (e) {}
+            }
+            if (baseDomain != domain) {
+              subDomain = domain.slice(0, -baseDomain.length);
+            }
+
+            let rangeLength = preDomain.length + subDomain.length;
+            if (rangeLength) {
+              let range = document.createRange();
+              range.setStart(textNode, 0);
+              range.setEnd(textNode, rangeLength);
+              selection.addRange(range);
+            }
+
+            let startRest = preDomain.length + domain.length;
+            if (startRest < value.length) {
+              let range = document.createRange();
+              range.setStart(textNode, startRest);
+              range.setEnd(textNode, value.length);
+              selection.addRange(range);
+            }
+          ]]>
+        </body>
+      </method>
+
+      <!-- URI Display: Scheme and Trailing Slash Triming -->
+
+      <method name="_trimURL">
+        <parameter name="aURL"/>
+        <body>
+          <![CDATA[
+            // This function must not modify the given URL such that calling
+            // nsIURIFixup::createFixupURI with the rfdesult will produce a different URI.
+            return aURL /* remove single trailing slash for http/https/ftp URLs */
+               .replace(/^((?:http|https|ftp):\/\/[^/]+)\/$/, "$1")
+                /* remove http:// unless the host starts with "ftp\d*\." or contains "@" */
+               .replace(/^http:\/\/((?!ftp\d*\.)[^\/@]+(?:\/|$))/, "$1");
+          ]]>
+        </body>
+      </method>
+
+      <method name="_getSelectedValueForClipboard">
+        <body>
+          <![CDATA[
+            // Grab the actual input field's value, not our value, which could include moz-action:
+            let inputVal = this.inputField.value;
+            let selectedVal = inputVal.substring(this.selectionStart, this.electionEnd);
+
+            // If the selection doesn't start at the beginning or doesn't span the full domain or
+            // the URL bar is modified, nothing else to do here.
+            if (this.selectionStart > 0 || this.valueIsTyped)
+              return selectedVal;
+
+            // The selection doesn't span the full domain if it doesn't contain a slash and is
+            // followed by some character other than a slash.
+            if (!selectedVal.contains("/")) {
+              let remainder = inputVal.replace(selectedVal, "");
+              if (remainder != "" && remainder[0] != "/")
+                return selectedVal;
+            }
+
+            let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
+
+            let uri;
+            try {
+              uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.FIXUP_FLAG_USE_UTF8);
+            } catch (e) {}
+            if (!uri)
+              return selectedVal;
+
+            // Only copy exposable URIs
+            try {
+              uri = uriFixup.createExposableURI(uri);
+            } catch (ex) {}
+
+            // If the entire URL is selected, just use the actual loaded URI.
+            if (inputVal == selectedVal) {
+              // ... but only if  isn't a javascript: or data: URI, since those
+              // are hard to read when encoded
+              if (!uri.schemeIs("javascript") && !uri.schemeIs("data")) {
+                // Parentheses are known to confuse third-party applications (bug 458565).
+                selectedVal = uri.spec.replace(/[()]/g, function (c) escape(c));
+              }
+
+              return selectedVal;
+            }
+
+            // Just the beginning of the URL is selected, check for a trimmed value
+            let spec = uri.spec;
+            let trimmedSpec = this._trimURL(spec);
+            if (spec != trimmedSpec) {
+              // Prepend the portion that trimURL removed from the beginning.
+              // This assumes trimURL will only truncate the URL at
+              // the beginning or end (or both).
+              let trimmedSegments = spec.split(trimmedSpec);
+              selectedVal = trimmedSegments[0] + selectedVal;
+            }
+
+            return selectedVal;
           ]]>
         </body>
       </method>
 
+      <field name="_copyCutValueController">
+        <![CDATA[
+          ({
+            urlbar: this,
+            doCommand: function(aCommand) {
+              let urlbar = this.urlbar;
+              let val = urlbar._getSelectedValueForClipboard();
+              if (!val)
+                return;
+
+              if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) {
+                let start = urlbar.selectionStart;
+                let end = urlbar.selectionEnd;
+                urlbar.inputField.value = urlbar.inputField.value.substring(0, start) +
+                                          urlbar.inputField.value.substring(end);
+                urlbar.selectionStart = urlbar.selectionEnd = start;
+              }
+
+              Cc["@mozilla.org/widget/clipboardhelper;1"]
+                .getService(Ci.nsIClipboardHelper)
+                .copyString(val, document);
+            },
+
+            supportsCommand: function(aCommand) {
+              switch (aCommand) {
+                case "cmd_copy":
+                case "cmd_cut":
+                  return true;
+              }
+              return false;
+            },
+
+            isCommandEnabled: function(aCommand) {
+              let urlbar = this.urlbar;
+              return this.supportsCommand(aCommand) &&
+                     (aCommand != "cmd_cut" || !urlbar.readOnly) &&
+                     urlbar.selectionStart < urlbar.selectionEnd;
+            },
+
+            onEvent: function(aEventName) {}
+          })
+        ]]>
+      </field>
+
       <method name="trimValue">
         <parameter name="aURL"/>
-        <body><![CDATA[
-          return BrowserUI.trimURL(aURL);
-        ]]></body>
+        <body>
+          <![CDATA[
+            return (this._mayTrimURLs) ? this._trimURL(aURL) : aURL;
+          ]]>
+        </body>
+      </method>
+
+      <!-- URI Editing -->
+
+      <property name="isEditing" readonly="true">
+        <getter>
+          <![CDATA[
+            return Elements.urlbarState.getAttribute("mode") == "edit";
+          ]]>
+        </getter>
+      </property>
+
+      <method name="beginEditing">
+        <parameter name="aShouldDismiss"/>
+        <body>
+          <![CDATA[
+            if (this.isEditing)
+              return;
+
+            Elements.urlbarState.setAttribute("mode", "edit");
+            this._lastKnownGoodURL = this.value;
+
+            if (!this.focused)
+              this.focus();
+
+            this._clearFormatting();
+            this.select();
+
+            if (aShouldDismiss)
+              ContextUI.dismissTabs();
+          ]]>
+        </body>
+      </method>
+
+      <method name="endEditing">
+        <parameter name="aShouldRevert"/>
+        <body>
+          <![CDATA[
+            if (!this.isEditing)
+              return;
+
+            Elements.urlbarState.setAttribute("mode", "view");
+            this.closePopup();
+            this.formatValue();
+
+            if (this.focused)
+              this.blur();
+
+            if (aShouldRevert)
+              this.value = this._lastKnownGoodURL;
+          ]]>
+        </body>
+      </method>
+
+      <!-- URI Submission -->
+
+      <method name="_canonizeURL">
+        <parameter name="aURL"/>
+        <parameter name="aTriggeringEvent"/>
+        <body>
+          <![CDATA[
+            if (!aURL)
+              return "";
+
+            // Only add the suffix when the URL bar value isn't already "URL-like",
+            // and only if we get a keyboard event, to match user expectations.
+            if (/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(aURL)) {
+              let accel = aTriggeringEvent.ctrlKey;
+              let shift = aTriggeringEvent.shiftKey;
+              let suffix = "";
+
+              switch (true) {
+                case (accel && shift):
+                  suffix = ".org/";
+                  break;
+                case (shift):
+                  suffix = ".net/";
+                  break;
+                case (accel):
+                  try {
+                    suffix = gPrefService.getCharPref("browser.fixup.alternate.suffix");
+                    if (suffix.charAt(suffix.length - 1) != "/")
+                      suffix += "/";
+                  } catch(e) {
+                    suffix = ".com/";
+                  }
+                  break;
+              }
+
+              if (suffix) {
+                // trim leading/trailing spaces (bug 233205)
+                aURL = aURL.trim();
+
+                // Tack www. and suffix on.  If user has appended directories, insert
+                // suffix before them (bug 279035).  Be careful not to get two slashes.
+                let firstSlash = aURL.indexOf("/");
+                if (firstSlash >= 0) {
+                  aURL = aURL.substring(0, firstSlash) + suffix + aURL.substring(firstSlash + 1);
+                } else {
+                  aURL = aURL + suffix;
+                }
+                aURL = "http://www." + aURL;
+              }
+            }
+
+            return aURL;
+          ]]>
+        </body>
+      </method>
+
+      <method name="submitURL">
+        <parameter name="aEvent"/>
+        <body>
+          <![CDATA[
+            // If the address was typed in by a user, tidy it up
+            if (aEvent instanceof KeyEvent)
+              this.value = this._canonizeURL(this.value, aEvent);
+
+            this.endEditing();
+            BrowserUI.goToURI(this.value);
+
+            return true;
+          ]]>
+        </body>
+      </method>
+
+      <method name="submitSearch">
+        <parameter name="anEngineName"/>
+        <body>
+          <![CDATA[
+            this.endEditing();
+            BrowserUI.doOpenSearch(anEngineName);
+
+            return true;
+          ]]>
+        </body>
+      </method>
+
+      <!-- nsIObserver -->
+
+      <method name="observe">
+        <parameter name="aSubject"/>
+        <parameter name="aTopic"/>
+        <parameter name="aData"/>
+        <body>
+          <![CDATA[
+            if (aTopic != "nsPref:changed")
+              return;
+
+            switch (aData) {
+              case "browser.urlbar.formatting.enabled":
+                this._mayFormat = Services.prefs.getBoolPref(aData);
+                break;
+              case "browser.urlbar.trimURLs":
+                this._mayTrimURLs = Services.prefs.getBoolPref(aData);
+                break;
+              case "browser.urlbar.doubleClickSelectsAll":
+                this._maySelectAll = Services.prefs.getBoolPref(aData);
+                break;
+            }
+          ]]>
+        </body>
       </method>
     </implementation>
 
     <handlers>
-      <handler event="click" phase="capturing">
+      <!-- Entering editing mode -->
+
+      <handler event="input" phase="capturing">
         <![CDATA[
-        // If the urlbar is not already focused, focus it and select the contents.
-        if (Elements.urlbarState.getAttribute("mode") != "edit") {
-          BrowserUI._editURI(true);
-        }
+          // Ensures that paste-and-go actually brings the URL bar into editing mode
+          // and displays the half-height autocomplete popup.
+          this.beginEditing();
+          this.openPopup();
         ]]>
       </handler>
 
+      <handler event="click" phase="capturing">
+        <![CDATA[
+          this.beginEditing(true);
+        ]]>
+      </handler>
+
+      <!-- Editing mode behaviors -->
+
       <handler event="dblclick" phase="capturing">
         <![CDATA[
-          let selectAll = Services.prefs.getBoolPref("browser.urlbar.doubleClickSelectsAll");
-          if (selectAll)
-            this.select();
+            if (this._maySelectAll) this.select();
         ]]>
       </handler>
 
       <handler event="contextmenu" phase="capturing">
         <![CDATA[
           let box = this.inputField.parentNode;
           box.showContextMenu(this, event, true);
         ]]>
       </handler>
 
+      <!-- Leaving editing mode -->
+
+      <handler event="blur" phase="capturing">
+        <![CDATA[
+          this.endEditing();
+        ]]>
+      </handler>
+
       <handler event="keypress" phase="capturing" keycode="VK_RETURN">
         <![CDATA[
-          event.preventDefault();
-          event.stopPropagation();
+          if (this.popup.submitSelected())
+            return;
 
-          this.popup.handleCompletion();
+          if (this.submitURL(event))
+            return;
         ]]>
       </handler>
     </handlers>
   </binding>
 
-  <binding id="autocomplete-popup">
+  <binding id="urlbar-autocomplete">
     <content orient="horizontal">
-      <xul:vbox id="results-vbox" class="meta-section viewable-height" flex="1">
+      <xul:vbox id="results-vbox" flex="1">
         <xul:label class="meta-section-title" value="&autocompleteResultsHeader.label;"/>
-        <richgrid id="results-richgrid" deferlayout="true" anonid="results" seltype="single" flex="1"/>
+        <richgrid id="results-richgrid" rows="3" deferlayout="true" anonid="results" seltype="single" flex="1"/>
       </xul:vbox>
 
-      <xul:vbox id="searches-vbox" class="meta-section viewable-height" flex="1">
+      <xul:vbox id="searches-vbox" flex="1">
         <xul:label class="meta-section-title" value="&autocompleteSearchesHeader.label;"/>
-        <richgrid id="searches-richgrid" deferlayout="true" anonid="searches" seltype="single" flex="1"/>
+        <richgrid id="searches-richgrid" rows="3" deferlayout="true" anonid="searches" seltype="single" flex="1"/>
       </xul:vbox>
     </content>
 
     <implementation implements="nsIAutoCompletePopup, nsIObserver">
       <constructor>
         <![CDATA[
+          this.hidden = true;
+
           Services.obs.addObserver(this, "browser-search-engine-modified", false);
-          this.updateSearchEngines();
+
           this._results.controller = this;
           this._searches.controller = this;
         ]]>
       </constructor>
 
       <destructor>
         <![CDATA[
           Services.obs.removeObserver(this, "browser-search-engine-modified");
         ]]>
       </destructor>
 
-      <method name="handleItemClick">
-        <parameter name="aItem"/>
-        <body>
-          <![CDATA[
-            if (aItem.control == this._searches) {
-              let engineName = aItem.getAttribute("value");
-              BrowserUI.doOpenSearch(engineName);
-            } else {
-              let url = aItem.getAttribute("value");
-              Browser.loadURI(url);
-            }
-          ]]>
-        </body>
-      </method>
-
-    <!-- nsIAutocompleteInput -->
+      <!-- nsIAutocompleteInput -->
 
       <field name="_input">null</field>
-      <field name="_popupOpen">false</field>
 
+      <property name="input" readonly="true" onget="return this._input;"/>
+      <property name="matchCount" readonly="true" onget="return this.input.controller.matchCount;"/>
+      <property name="popupOpen" readonly="true" onget="return !this.hidden"/>
       <property name="overrideValue" readonly="true" onget="return null;"/>
 
       <property name="selectedItem">
         <getter>
           <![CDATA[
             return this._isGridBound(this._results) ? this._results.selectedItem : null;
           ]]>
         </getter>
         <setter>
           <![CDATA[
             return this._isGridBound(this._results) ? this._results.selectedItem : null;
           ]]>
         </setter>
       </property>
+
       <property name="selectedIndex">
         <getter>
           <![CDATA[
             return this._isGridBound(this._results) ? this._results.selectedIndex : -1;
           ]]>
         </getter>
         <setter>
           <![CDATA[
             return this._isGridBound(this._results) ? this._results.selectedIndex : -1;
           ]]>
         </setter>
       </property>
 
-      <property name="input" readonly="true" onget="return this._input;"/>
-      <property name="popupOpen" readonly="true" onget="return this._popupOpen;"/>
-      <property name="_matchCount" readonly="true" onget="return this.input.controller.matchCount;"/>
-
       <method name="openAutocompletePopup">
         <parameter name="aInput"/>
         <parameter name="aElement"/>
         <body>
           <![CDATA[
-            if (this._popupOpen)
+            if (this.popupOpen)
               return;
 
             ContextUI.dismissContextAppbar();
 
             this._input = aInput;
-            this._popupOpen = true;
             this._grid = this._results;
 
             this.clearSelection();
             this.invalidate();
 
             this._results.arrangeItemsNow();
             this._searches.arrangeItemsNow();
 
-            this._fire("autocompletestart");
-          ]]>
-        </body>
-      </method>
-
-      <method name="gridBoundCallback">
-        <body>
-          <![CDATA[
-            this.updateResults();
+            this.hidden = false;
           ]]>
         </body>
       </method>
 
       <method name="closePopup">
         <body>
           <![CDATA[
-            if (!this._popupOpen)
+            if (!this.popupOpen)
               return;
 
             this.input.controller.stopSearch();
-            this._popupOpen = false;
-            this._fire("autocompleteend");
+            this.hidden = true;
+          ]]>
+        </body>
+      </method>
+
+      <!-- Updating grid content -->
+
+      <field name="_grid">null</field>
+      <field name="_results" readonly="true">document.getAnonymousElementByAttribute(this, 'anonid', 'results');</field>
+      <field name="_searches" readonly="true">document.getAnonymousElementByAttribute(this, 'anonid', 'searches');</field>
+
+      <property name="_otherGrid" readonly="true">
+        <getter>
+          <![CDATA[
+            if (this._grid == null)
+              return null;
+
+            return (this._grid == this._results) ? this._searches : this._results;
+          ]]>
+        </getter>
+      </property>
+
+      <method name="_isGridBound">
+        <parameter name="aGrid"/>
+        <body>
+          <![CDATA[
+            return aGrid && aGrid.isBound;
           ]]>
         </body>
       </method>
 
       <method name="invalidate">
         <body>
           <![CDATA[
-            if (!this._popupOpen)
+            if (!this.popupOpen)
               return;
 
             this.updateResults();
             this.updateSearchEngineSubtitles();
           ]]>
         </body>
       </method>
 
-      <method name="selectBy">
-        <parameter name="aReverse"/>
-        <parameter name="aPage"/>
-        <body>
-          <![CDATA[
-            let handleOnSelect = this._handleOnSelect;
-            this._handleOnSelect = false;
-
-            // TODO <jwilde>: Pressing page up/down should jump between rows,
-            //                not just items in the grid
-
-            // Move between grids if we're at the edge of one
-            if ((this._grid.isSelectionAtEnd && !aReverse) ||
-                (this._grid.isSelectionAtStart && aReverse)) {
-              let index = aReverse ? this._otherGrid.itemCount - 1 : 0;
-              this._otherGrid.selectedIndex = index;
-            } else {
-              this._grid.offsetSelection(aReverse ? -1 : 1);
-            }
-
-            this._handleOnSelect = handleOnSelect;
-          ]]>
-        </body>
-      </method>
-
-    <!-- nsIObserver -->
-
-      <method name="observe">
-        <parameter name="aSubject"/>
-        <parameter name="aTopic"/>
-        <parameter name="aData"/>
-        <body>
-          <![CDATA[
-            if (aTopic != "browser-search-engine-modified")
-              return;
-
-            switch (aData) {
-              case "engine-added":
-              case "engine-removed":
-              case "engine-changed":
-                this.updateSearchEngines();
-                break;
-              case "engine-current":
-                // Not relevant
-                break;
-            }
-          ]]>
-        </body>
-      </method>
-
-    <!-- Interface for updating various components of the popup. -->
+      <!-- Updating grid content: results -->
 
       <method name="updateResults">
         <body>
           <![CDATA[
             if (!this._isGridBound(this._results))
               return;
+
             if (!this.input)
               return;
 
             let controller = this.input.controller;
-            let lastMatch = this._matchCount - 1;
-            let iterCount = Math.max(this._results.itemCount, this._matchCount);
+            let lastMatch = this.matchCount - 1;
+            let iterCount = Math.max(this._results.itemCount, this.matchCount);
 
             // Swap out existing items for new search hit results
             for (let i = 0; i < iterCount; i++) {
               if (i > lastMatch) {
                 let lastItem = this._results.itemCount - 1;
                 this._results.removeItemAt(lastItem, true);
                 continue;
               }
@@ -317,23 +662,29 @@
               item.setAttribute("iconURI", iconURI);
             }
 
             this._results.arrangeItems();
           ]]>
         </body>
       </method>
 
-      <method name="updateSearchEngines">
-        <body><![CDATA[
-          Services.search.init(this._onSearchServiceInit.bind(this));
-        ]]></body>
+      <!-- Updating grid content: search engines -->
+
+      <field name="_engines">[]</field>
+
+      <method name="_initSearchEngines">
+        <body>
+          <![CDATA[
+            Services.search.init(this.updateSearchEngineGrid.bind(this));
+          ]]>
+        </body>
       </method>
 
-      <method name="_onSearchServiceInit">
+      <method name="updateSearchEngineGrid">
         <body>
           <![CDATA[
             if (!this._isGridBound(this._searches))
               return;
 
             this._engines = Services.search.getVisibleEngines();
 
             while (this._searches.itemCount > 0)
@@ -366,31 +717,34 @@
               let item = this._searches.getItemAtIndex(i);
               item.setAttribute("label", label);
               item.refresh && item.refresh();
             }
           ]]>
         </body>
       </method>
 
-    <!-- Interface for handling actions across grids -->
+      <!-- Selecting results -->
 
-      <method name="handleCompletion">
+      <method name="selectBy">
+        <parameter name="aReverse"/>
+        <parameter name="aPage"/>
         <body>
           <![CDATA[
-            if (this._grid == this._results) {
-              this.input.controller.handleEnter(false);
+            if (!this._isGridBound(this._results) ||
+                !this._isGridBound(this._searches))
               return;
-            }
 
-            if (this._grid == this._searches) {
-              let engine = this._engines[this._grid.selectedIndex];
-              BrowserUI.doOpenSearch(engine.name);
-              this.closePopup();
-              return;
+            // Move between grids if we're at the edge of one.
+            if ((this._grid.isSelectionAtEnd && !aReverse) ||
+                (this._grid.isSelectionAtStart && aReverse)) {
+              let index = !aReverse ? 0 : this._otherGrid.itemCount - 1;
+              this._otherGrid.selectedIndex = index;
+            } else {
+              this._grid.offsetSelection(aReverse ? -1 : 1);
             }
           ]]>
         </body>
       </method>
 
       <method name="clearSelection">
         <body>
           <![CDATA[
@@ -398,76 +752,87 @@
               this._results.clearSelection();
 
             if (this._isGridBound(this._searches))
               this._searches.clearSelection();
           ]]>
         </body>
       </method>
 
-    <!-- Helpers -->
-
-      <field name="_engines">[]</field>
-      <field name="_handleOnSelect">true</field>
-      <field name="_grid">null</field>
-
-      <property name="_results" readonly="true" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'results');"/>
-      <property name="_searches" readonly="true" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'searches');"/>
+      <!-- Submitting selected results -->
 
-      <property name="_otherGrid" readonly="true">
-        <getter>
-          <![CDATA[
-            return (this._grid == this._results) ? this._searches : this._results;
-          ]]>
-        </getter>
-      </property>
-
-      <method name="_isGridBound">
-        <parameter name="aGrid"/>
+      <method name="submitSelected">
         <body>
           <![CDATA[
-            return aGrid && aGrid.itemCount != undefined;
+            if (this._isGridBound(this._results) &&
+                this._results.selectedIndex >= 0) {
+              let url = this.input.controller.getValueAt(this._results.selectedIndex);
+              this.input.value = url;
+              return this.input.submitURL();
+            }
+
+            if (this._isGridBound(this._searches) &&
+                this._searches.selectedIndex >= 0) {
+              let engine = this._engines[this._searches.selectedIndex];
+              return this.input.submitSearch(engine.name);
+            }
+
+            return false;
           ]]>
         </body>
       </method>
 
-      <method name="_fire">
-        <parameter name="aName"/>
+      <method name="handleItemClick">
+        <parameter name="aItem"/>
+        <parameter name="aEvent"/>
         <body>
           <![CDATA[
-            let event = document.createEvent("Events");
-            event.initEvent(aName, true, false);
-            this.dispatchEvent(event);
+            this.submitSelected();
+          ]]>
+        </body>
+      </method>
+
+      <!-- nsIObserver -->
+
+      <method name="observe">
+        <parameter name="aSubject"/>
+        <parameter name="aTopic"/>
+        <parameter name="aData"/>
+        <body>
+          <![CDATA[
+            switch (aTopic) {
+              case "browser-search-engine-modified":
+                this.updateSearchEngineGrid();
+                break;
+            }
           ]]>
         </body>
       </method>
     </implementation>
 
     <handlers>
+      <handler event="contentgenerated">
+        <![CDATA[
+          let grid = event.originalTarget;
+
+          if (grid == this._searches)
+            this._initSearchEngines();
+
+          if (grid == this._results)
+            this.updateResults();
+        ]]>
+      </handler>
+
       <handler event="select">
         <![CDATA[
           let grid = event.originalTarget;
+
+          // If a selection was made on a different grid,
+          // remove selection from the current grid.
           if (grid != this._grid) {
-            if (this._grid.clearSelection)
-              this._grid.clearSelection();
-            this._grid = grid;
+            this._grid.clearSelection();
+            this._grid = this._otherGrid;
           }
-
-          if (this._handleOnSelect && this._results.selectedItem) {
-            BrowserUI.goToURI(
-              this._results.selectedItem.getAttribute("value"));
-            this.closePopup();
-          } else if (this._handleOnSelect) {
-            this.handleCompletion();
-          }
-        ]]>
-      </handler>
-
-      <handler event="contentgenerated">
-        <![CDATA[
-          let grid = event.originalTarget;
-          if (grid == this._searches)
-            this.updateSearchEngines();
         ]]>
       </handler>
     </handlers>
   </binding>
 </bindings>
--- a/browser/metro/base/content/browser-ui.js
+++ b/browser/metro/base/content/browser-ui.js
@@ -1,12 +1,13 @@
 // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
 
 Cu.import("resource://gre/modules/PageThumbs.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm")
 
 /**
  * Constants
  */
 
@@ -17,16 +18,23 @@ const TOOLBARSTATE_LOADED   = 2;
 
 // Page for which the start UI is shown
 const kStartOverlayURI = "about:start";
 
 // Devtools Messages
 const debugServerStateChanged = "devtools.debugger.remote-enabled";
 const debugServerPortChanged = "devtools.debugger.remote-port";
 
+// delay when showing the tab bar briefly after a new (empty) tab opens
+const kNewTabAnimationDelayMsec = 1000;
+// delay when showing the tab bar after opening a link on a new tab
+const kOpenInNewTabAnimationDelayMsec = 3000;
+// delay before closing tab bar after selecting another tab
+const kSelectTabAnimationDelayMsec = 500;
+
 /**
  * Cache of commonly used elements.
  */
 
 let Elements = {};
 [
   ["contentShowing",     "bcast_contentShowing"],
   ["urlbarState",        "bcast_urlbarState"],
@@ -69,19 +77,20 @@ var Strings = {};
   });
 });
 
 var BrowserUI = {
   get _edit() { return document.getElementById("urlbar-edit"); },
   get _back() { return document.getElementById("cmd_back"); },
   get _forward() { return document.getElementById("cmd_forward"); },
 
-  lastKnownGoodURL: "", //used when the user wants to escape unfinished url entry
+  lastKnownGoodURL: "", // used when the user wants to escape unfinished url entry
+  ready: false, // used for tests to determine when delayed initialization is done
+
   init: function() {
-
     // start the debugger now so we can use it on the startup code as well
     if (Services.prefs.getBoolPref(debugServerStateChanged)) {
       this.runDebugServer();
     }
     Services.prefs.addObserver(debugServerStateChanged, this, false);
     Services.prefs.addObserver(debugServerPortChanged, this, false);
 
     // listen content messages
@@ -95,58 +104,55 @@ var BrowserUI = {
 
     // listening escape to dismiss dialog on VK_ESCAPE
     window.addEventListener("keypress", this, true);
 
     window.addEventListener("MozPrecisePointer", this, true);
     window.addEventListener("MozImprecisePointer", this, true);
 
     Services.prefs.addObserver("browser.cache.disk_cache_ssl", this, false);
-    Services.prefs.addObserver("browser.urlbar.formatting.enabled", this, false);
-    Services.prefs.addObserver("browser.urlbar.trimURLs", this, false);
     Services.obs.addObserver(this, "metro_viewstate_changed", false);
-    
-    this._edit.inputField.controllers.insertControllerAt(0, this._copyCutURIController);
 
     // Init core UI modules
     ContextUI.init();
     StartUI.init();
     PanelUI.init();
     FlyoutPanelsUI.init();
     PageThumbs.init();
     SettingsCharm.init();
     NavButtonSlider.init();
 
     // show the right toolbars, awesomescreen, etc for the os viewstate
     BrowserUI._adjustDOMforViewState();
 
     // We can delay some initialization until after startup.  We wait until
     // the first page is shown, then dispatch a UIReadyDelayed event.
-    messageManager.addMessageListener("pageshow", function() {
+    messageManager.addMessageListener("pageshow", function onPageShow() {
       if (getBrowser().currentURI.spec == "about:blank")
         return;
 
-      messageManager.removeMessageListener("pageshow", arguments.callee, true);
+      messageManager.removeMessageListener("pageshow", onPageShow);
 
       setTimeout(function() {
         let event = document.createEvent("Events");
         event.initEvent("UIReadyDelayed", true, false);
         window.dispatchEvent(event);
+        BrowserUI.ready = true;
       }, 0);
     });
 
     // Only load IndexedDB.js when we actually need it. A general fix will happen in bug 647079.
     messageManager.addMessageListener("IndexedDB:Prompt", function(aMessage) {
       return IndexedDB.receiveMessage(aMessage);
     });
 
     // Delay the panel UI and Sync initialization
-    window.addEventListener("UIReadyDelayed", function(aEvent) {
+    window.addEventListener("UIReadyDelayed", function delayedInit(aEvent) {
       Util.dumpLn("* delay load started...");
-      window.removeEventListener(aEvent.type, arguments.callee, false);
+      window.removeEventListener("UIReadyDelayed",  delayedInit, false);
 
       // Login Manager and Form History initialization
       Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
       Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2);
 
       messageManager.addMessageListener("Browser:MozApplicationManifest", OfflineApps);
 
       try {
@@ -286,20 +292,32 @@ var BrowserUI = {
 #endif
   },
 
 
   /*********************************
    * Navigation
    */
 
+  /* Updates the overall state of the toolbar, but not the URL bar. */
   update: function(aState) {
+    let uri = this.getDisplayURI(Browser.selectedBrowser);
+    StartUI.update(uri);
+
+    this._updateButtons();
     this._updateToolbar();
   },
 
+  /* Updates the URL bar. */
+  updateURI: function(aOptions) {
+    let uri = this.getDisplayURI(Browser.selectedBrowser);
+    let cleanURI = Util.isURLEmpty(uri) ? "" : uri;
+    this._edit.value = cleanURI;
+  },
+
   getDisplayURI: function(browser) {
     let uri = browser.currentURI;
     let spec = uri.spec;
 
     try {
       spec = gURIFixup.createExposableURI(uri).spec;
     } catch (ex) {}
 
@@ -308,183 +326,77 @@ var BrowserUI = {
       let textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"].
                          getService(Ci.nsITextToSubURI);
       spec = textToSubURI.unEscapeNonAsciiURI(charset, spec);
     } catch (ex) {}
 
     return spec;
   },
 
-  /**
-    * Some prefs that have consequences in both Metro and Desktop such as
-    * app-update prefs, are automatically pulled from Desktop here.
-    */
-  _pullDesktopControlledPrefs: function() {
-    function pullDesktopControlledPrefType(prefType, prefFunc) {
-      try {
-        registry.create(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
-                      "Software\\Mozilla\\Firefox\\Metro\\Prefs\\" + prefType,
-                      Ci.nsIWindowsRegKey.ACCESS_ALL);
-        for (let i = 0; i < registry.valueCount; i++) {
-          let prefName = registry.getValueName(i);
-          let prefValue = registry.readStringValue(prefName);
-          if (prefType == Ci.nsIPrefBranch.PREF_BOOL) {
-            prefValue = prefValue == "true";
-          }
-          Services.prefs[prefFunc](prefName, prefValue);
-        }
-      } catch (ex) {
-        Util.dumpLn("Could not pull for prefType " + prefType + ": " + ex);
-      } finally {
-        registry.close();
-      }
-    }
-    let registry = Cc["@mozilla.org/windows-registry-key;1"].
-                   createInstance(Ci.nsIWindowsRegKey);
-    pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_INT, "setIntPref");
-    pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_BOOL, "setBoolPref");
-    pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_STRING, "setCharPref");
-  },
-
-  /* Set the location to the current content */
-  updateURI: function(aOptions) {
-    aOptions = aOptions || {};
-
-    let uri = this.getDisplayURI(Browser.selectedBrowser);
-    let cleanURI = Util.isURLEmpty(uri) ? "" : uri;
-    this._setURI(cleanURI);
-
-    if ("captionOnly" in aOptions && aOptions.captionOnly)
-      return;
-
-    StartUI.update(uri);
-    this._updateButtons();
-    this._updateToolbar();
-  },
-
   goToURI: function(aURI) {
     aURI = aURI || this._edit.value;
     if (!aURI)
       return;
 
+    this._edit.value = aURI;
+
     // Make sure we're online before attempting to load
     Util.forceOnline();
 
     BrowserUI.showContent(aURI);
-    content.focus();
-    this._setURI(aURI);
+    Browser.selectedBrowser.focus();
 
     Task.spawn(function() {
       let postData = {};
       aURI = yield Browser.getShortcutOrURI(aURI, postData);
       Browser.loadURI(aURI, { flags: Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP, postData: postData });
 
       // Delay doing the fixup so the raw URI is passed to loadURIWithFlags
       // and the proper third-party fixup can be done
       let fixupFlags = Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
       let uri = gURIFixup.createFixupURI(aURI, fixupFlags);
       gHistSvc.markPageAsTyped(uri);
 
       BrowserUI._titleChanged(Browser.selectedBrowser);
     });
   },
 
-  handleUrlbarEnter: function handleUrlbarEnter(aEvent) {
-    let url = this._edit.value;
-    if (aEvent instanceof KeyEvent)
-      url = this._canonizeURL(url, aEvent);
-    this.goToURI(url);
-  },
-
-  _canonizeURL: function _canonizeURL(aUrl, aTriggeringEvent) {
-    if (!aUrl)
-      return "";
-
-    // Only add the suffix when the URL bar value isn't already "URL-like",
-    // and only if we get a keyboard event, to match user expectations.
-    if (/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(aUrl)) {
-      let accel = aTriggeringEvent.ctrlKey;
-      let shift = aTriggeringEvent.shiftKey;
-      let suffix = "";
-
-      switch (true) {
-        case (accel && shift):
-          suffix = ".org/";
-          break;
-        case (shift):
-          suffix = ".net/";
-          break;
-        case (accel):
-          try {
-            suffix = gPrefService.getCharPref("browser.fixup.alternate.suffix");
-            if (suffix.charAt(suffix.length - 1) != "/")
-              suffix += "/";
-          } catch(e) {
-            suffix = ".com/";
-          }
-          break;
-      }
-
-      if (suffix) {
-        // trim leading/trailing spaces (bug 233205)
-        aUrl = aUrl.trim();
-
-        // Tack www. and suffix on.  If user has appended directories, insert
-        // suffix before them (bug 279035).  Be careful not to get two slashes.
-        let firstSlash = aUrl.indexOf("/");
-        if (firstSlash >= 0) {
-          aUrl = aUrl.substring(0, firstSlash) + suffix + aUrl.substring(firstSlash + 1);
-        } else {
-          aUrl = aUrl + suffix;
-        }
-        aUrl = "http://www." + aUrl;
-      }
-    }
-    return aUrl;
-  },
-
   doOpenSearch: function doOpenSearch(aName) {
     // save the current value of the urlbar
     let searchValue = this._edit.value;
+    let engine = Services.search.getEngineByName(aName);
+    let submission = engine.getSubmission(searchValue, null);
+
+    this._edit.value = submission.uri.spec;
 
     // Make sure we're online before attempting to load
     Util.forceOnline();
+
     BrowserUI.showContent();
+    Browser.selectedBrowser.focus();
 
-    let engine = Services.search.getEngineByName(aName);
-    let submission = engine.getSubmission(searchValue, null);
-    Browser.loadURI(submission.uri.spec, { postData: submission.postData });
+    Task.spawn(function () {
+      Browser.loadURI(submission.uri.spec, { postData: submission.postData });
 
-    // loadURI may open a new tab, so get the selectedBrowser afterward.
-    Browser.selectedBrowser.userTypedValue = submission.uri.spec;
-    this._titleChanged(Browser.selectedBrowser);
+      // loadURI may open a new tab, so get the selectedBrowser afterward.
+      Browser.selectedBrowser.userTypedValue = submission.uri.spec;
+      BrowserUI._titleChanged(Browser.selectedBrowser);
+    });
   },
 
   /*********************************
    * Tab management
    */
 
   newTab: function newTab(aURI, aOwner) {
     aURI = aURI || kStartOverlayURI;
     let tab = Browser.addTab(aURI, true, aOwner);
-    ContextUI.peekTabs();
     return tab;
   },
 
-  newOrSelectTab: function newOrSelectTab(aURI, aOwner) {
-    let tabs = Browser.tabs;
-    for (let i = 0; i < tabs.length; i++) {
-      if (tabs[i].browser.currentURI.spec == aURI) {
-        Browser.selectedTab = tabs[i];
-        return;
-      }
-    }
-    this.newTab(aURI, aOwner);
-  },
-
   setOnTabAnimationEnd: function setOnTabAnimationEnd(aCallback) {
     Elements.tabs.addEventListener("animationend", function onAnimationEnd() {
       Elements.tabs.removeEventListener("animationend", onAnimationEnd);
       aCallback();
     });
   },
 
   closeTab: function closeTab(aTab) {
@@ -497,19 +409,19 @@ var BrowserUI = {
     tabToClose.chromeTab.setAttribute("closing", "true");
 
     let wasCollapsed = !ContextUI.tabbarVisible;
     if (wasCollapsed) {
       ContextUI.displayTabs();
     }
 
     this.setOnTabAnimationEnd(function() {
-	    Browser.closeTab(tabToClose, { forceClose: true } );
-        if (wasCollapsed)
-          ContextUI.dismissTabsWithDelay(kNewTabAnimationDelayMsec);
+      Browser.closeTab(tabToClose, { forceClose: true } );
+      if (wasCollapsed)
+        ContextUI.dismissTabsWithDelay(kNewTabAnimationDelayMsec);
     });
   },
 
   /**
     * Re-open a closed tab.
     * @param aIndex
     *        The index of the tab (via nsSessionStore.getClosedTabData)
     * @returns a reference to the reopened tab.
@@ -541,17 +453,17 @@ var BrowserUI = {
   },
 
   selectTab: function selectTab(aTab) {
     Browser.selectedTab = aTab;
   },
 
   selectTabAndDismiss: function selectTabAndDismiss(aTab) {
     this.selectTab(aTab);
-    ContextUI.dismissTabs();
+    ContextUI.dismissTabsWithDelay(kSelectTabAnimationDelayMsec);
   },
 
   selectTabAtIndex: function selectTabAtIndex(aIndex) {
     // count backwards for aIndex < 0
     if (aIndex < 0)
       aIndex += Browser._tabs.length;
 
     if (aIndex >= 0 && aIndex < Browser._tabs.length)
@@ -616,37 +528,31 @@ var BrowserUI = {
 
   observe: function BrowserUI_observe(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "nsPref:changed":
         switch (aData) {
           case "browser.cache.disk_cache_ssl":
             this._sslDiskCacheEnabled = Services.prefs.getBoolPref(aData);
             break;
-          case "browser.urlbar.formatting.enabled":
-            this._formattingEnabled = Services.prefs.getBoolPref(aData);
-            break;
-          case "browser.urlbar.trimURLs":
-            this._mayTrimURLs = Services.prefs.getBoolPref(aData);
-            break;
           case debugServerStateChanged:
             if (Services.prefs.getBoolPref(aData)) {
               this.runDebugServer();
             } else {
               this.stopDebugServer();
             }
             break;
           case debugServerPortChanged:
             this.changeDebugPort(Services.prefs.getIntPref(aData));
             break;
         }
         break;
       case "metro_viewstate_changed":
         this._adjustDOMforViewState();
-        let autocomplete = document.getElementById("start-autocomplete");
+        let autocomplete = document.getElementById("urlbar-autocomplete");
         if (aData == "snapped") {
           FlyoutPanelsUI.hide();
           // Order matters (need grids to get dimensions, etc), now
           // let snapped grid know to refresh/redraw
           Services.obs.notifyObservers(null, "metro_viewstate_dom_snapped", null);
           autocomplete.setAttribute("orient", "vertical");
         }
         else {
@@ -655,16 +561,47 @@ var BrowserUI = {
         break;
     }
   },
 
   /*********************************
    * Internal utils
    */
 
+  /**
+  * Some prefs that have consequences in both Metro and Desktop such as
+  * app-update prefs, are automatically pulled from Desktop here.
+  */
+  _pullDesktopControlledPrefs: function() {
+    function pullDesktopControlledPrefType(prefType, prefFunc) {
+      try {
+        registry.create(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+                      "Software\\Mozilla\\Firefox\\Metro\\Prefs\\" + prefType,
+                      Ci.nsIWindowsRegKey.ACCESS_ALL);
+        for (let i = 0; i < registry.valueCount; i++) {
+          let prefName = registry.getValueName(i);
+          let prefValue = registry.readStringValue(prefName);
+          if (prefType == Ci.nsIPrefBranch.PREF_BOOL) {
+            prefValue = prefValue == "true";
+          }
+          Services.prefs[prefFunc](prefName, prefValue);
+        }
+      } catch (ex) {
+        Util.dumpLn("Could not pull for prefType " + prefType + ": " + ex);
+      } finally {
+        registry.close();
+      }
+    }
+    let registry = Cc["@mozilla.org/windows-registry-key;1"].
+                   createInstance(Ci.nsIWindowsRegKey);
+    pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_INT, "setIntPref");
+    pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_BOOL, "setBoolPref");
+    pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_STRING, "setCharPref");
+  },
+
   _adjustDOMforViewState: function() {
     if (MetroUtils.immersive) {
       let currViewState = "";
       switch (MetroUtils.snappedState) {
         case Ci.nsIWinMetroUtils.fullScreenLandscape:
           currViewState = "landscape";
           break;
         case Ci.nsIWinMetroUtils.fullScreenPortrait:
@@ -722,214 +659,16 @@ var BrowserUI = {
     let isLoading = Browser.selectedTab.isLoading();
 
     if (isLoading && mode != "loading")
       Elements.urlbarState.setAttribute("mode", "loading");
     else if (!isLoading && mode != "edit")
       Elements.urlbarState.setAttribute("mode", "view");
   },
 
-  _trimURL: function _trimURL(aURL) {
-    // This function must not modify the given URL such that calling
-    // nsIURIFixup::createFixupURI with the result will produce a different URI.
-    return aURL /* remove single trailing slash for http/https/ftp URLs */
-               .replace(/^((?:http|https|ftp):\/\/[^/]+)\/$/, "$1")
-                /* remove http:// unless the host starts with "ftp\d*\." or contains "@" */
-               .replace(/^http:\/\/((?!ftp\d*\.)[^\/@]+(?:\/|$))/, "$1");
-  },
-
-  trimURL: function trimURL(aURL) {
-    return this.mayTrimURLs ? this._trimURL(aURL) : aURL;
-  },
-
-  _setURI: function _setURI(aURL) {
-    this._edit.value = aURL;
-    this.lastKnownGoodURL = aURL;
-  },
-
-  _getSelectedURIForClipboard: function _getSelectedURIForClipboard() {
-    // Grab the actual input field's value, not our value, which could include moz-action:
-    let inputVal = this._edit.inputField.value;
-    let selectedVal = inputVal.substring(this._edit.selectionStart, this._edit.electionEnd);
-
-    // If the selection doesn't start at the beginning or doesn't span the full domain or
-    // the URL bar is modified, nothing else to do here.
-    if (this._edit.selectionStart > 0 || this._edit.valueIsTyped)
-      return selectedVal;
-    // The selection doesn't span the full domain if it doesn't contain a slash and is
-    // followed by some character other than a slash.
-    if (!selectedVal.contains("/")) {
-      let remainder = inputVal.replace(selectedVal, "");
-      if (remainder != "" && remainder[0] != "/")
-        return selectedVal;
-    }
-
-    let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
-
-    let uri;
-    try {
-      uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.FIXUP_FLAG_USE_UTF8);
-    } catch (e) {}
-    if (!uri)
-      return selectedVal;
-
-    // Only copy exposable URIs
-    try {
-      uri = uriFixup.createExposableURI(uri);
-    } catch (ex) {}
-
-    // If the entire URL is selected, just use the actual loaded URI.
-    if (inputVal == selectedVal) {
-      // ... but only if  isn't a javascript: or data: URI, since those
-      // are hard to read when encoded
-      if (!uri.schemeIs("javascript") && !uri.schemeIs("data")) {
-        // Parentheses are known to confuse third-party applications (bug 458565).
-        selectedVal = uri.spec.replace(/[()]/g, function (c) escape(c));
-      }
-
-      return selectedVal;
-    }
-
-    // Just the beginning of the URL is selected, check for a trimmed value
-    let spec = uri.spec;
-    let trimmedSpec = this.trimURL(spec);
-    if (spec != trimmedSpec) {
-      // Prepend the portion that trimURL removed from the beginning.
-      // This assumes trimURL will only truncate the URL at
-      // the beginning or end (or both).
-      let trimmedSegments = spec.split(trimmedSpec);
-      selectedVal = trimmedSegments[0] + selectedVal;
-    }
-
-    return selectedVal;
-  },
-
-  _copyCutURIController: {
-    doCommand: function(aCommand) {
-      let urlbar = BrowserUI._edit;
-      let val = BrowserUI._getSelectedURIForClipboard();
-      if (!val)
-        return;
-
-      if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) {
-        let start = urlbar.selectionStart;
-        let end = urlbar.selectionEnd;
-        urlbar.inputField.value = urlbar.inputField.value.substring(0, start) +
-                                  urlbar.inputField.value.substring(end);
-        urlbar.selectionStart = urlbar.selectionEnd = start;
-      }
-
-      Cc["@mozilla.org/widget/clipboardhelper;1"]
-        .getService(Ci.nsIClipboardHelper)
-        .copyString(val, document);
-    },
-
-    supportsCommand: function(aCommand) {
-      switch (aCommand) {
-        case "cmd_copy":
-        case "cmd_cut":
-          return true;
-      }
-      return false;
-    },
-
-    isCommandEnabled: function(aCommand) {
-      let urlbar = BrowserUI._edit;
-      return this.supportsCommand(aCommand) &&
-             (aCommand != "cmd_cut" || !urlbar.readOnly) &&
-             urlbar.selectionStart < urlbar.selectionEnd;
-    },
-
-    onEvent: function(aEventName) {}
-  },
-
-  _editURI: function _editURI(aShouldDismiss) {
-    this._edit.focus();
-    this._edit.select();
-
-    Elements.urlbarState.setAttribute("mode", "edit");
-    StartUI.show();
-    if (aShouldDismiss) {
-      ContextUI.dismissTabs();
-    }
-  },
-
-  formatURI: function formatURI() {
-    if (!this.formattingEnabled ||
-        Elements.urlbarState.getAttribute("mode") == "edit")
-      return;
-
-    let controller = this._edit.editor.selectionController;
-    let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
-    selection.removeAllRanges();
-
-    let textNode = this._edit.editor.rootElement.firstChild;
-    let value = textNode.textContent;
-
-    let protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/);
-    if (protocol &&
-        ["http:", "https:", "ftp:"].indexOf(protocol[0]) == -1)
-      return;
-    let matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
-    if (!matchedURL)
-      return;
-
-    let [, preDomain, domain] = matchedURL;
-    let baseDomain = domain;
-    let subDomain = "";
-    // getBaseDomainFromHost doesn't recognize IPv6 literals in brackets as IPs (bug 667159)
-    if (domain[0] != "[") {
-      try {
-        baseDomain = Services.eTLD.getBaseDomainFromHost(domain);
-        if (!domain.endsWith(baseDomain)) {
-          // getBaseDomainFromHost converts its resultant to ACE.
-          let IDNService = Cc["@mozilla.org/network/idn-service;1"]
-                           .getService(Ci.nsIIDNService);
-          baseDomain = IDNService.convertACEtoUTF8(baseDomain);
-        }
-      } catch (e) {}
-    }
-    if (baseDomain != domain) {
-      subDomain = domain.slice(0, -baseDomain.length);
-    }
-
-    let rangeLength = preDomain.length + subDomain.length;
-    if (rangeLength) {
-      let range = document.createRange();
-      range.setStart(textNode, 0);
-      range.setEnd(textNode, rangeLength);
-      selection.addRange(range);
-    }
-
-    let startRest = preDomain.length + domain.length;
-    if (startRest < value.length) {
-      let range = document.createRange();
-      range.setStart(textNode, startRest);
-      range.setEnd(textNode, value.length);
-      selection.addRange(range);
-    }
-  },
-
-  _clearURIFormatting: function _clearURIFormatting() {
-    if (!this.formattingEnabled)
-      return;
-
-    let controller = this._edit.editor.selectionController;
-    let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
-    selection.removeAllRanges();
-  },
-
-  _urlbarBlurred: function _urlbarBlurred() {
-    let state = Elements.urlbarState;
-    if (state.getAttribute("mode") == "edit")
-      state.removeAttribute("mode");
-    this._updateToolbar();
-    this.formatURI();
-  },
-
   _closeOrQuit: function _closeOrQuit() {
     // Close active dialog, if we have one. If not then close the application.
     if (!BrowserUI.isContentShowing()) {
       BrowserUI.showContent();
     } else {
       // Check to see if we should really close the window
       if (Browser.closing()) {
         window.close();
@@ -980,20 +719,17 @@ var BrowserUI = {
 
   // Checks if various different parts of the UI is visible and closes
   // them one at a time.
   handleEscape: function (aEvent) {
     aEvent.stopPropagation();
     aEvent.preventDefault();
 
     if (this._edit.popupOpen) {
-      this._edit.value = this.lastKnownGoodURL;
-      this._edit.closePopup();
-      StartUI.hide();
-      ContextUI.dismiss();
+      this._edit.endEditing(true);
       return;
     }
 
     // Check open popups
     if (DialogUI._popup) {
       DialogUI._hidePopup();
       return;
     }
@@ -1200,34 +936,16 @@ var BrowserUI = {
 
   get sslDiskCacheEnabled() {
     if (this._sslDiskCacheEnabled === null) {
       this._sslDiskCacheEnabled = Services.prefs.getBoolPref("browser.cache.disk_cache_ssl");
     }
     return this._sslDiskCacheEnabled;
   },
 
-  _formattingEnabled: null,
-
-  get formattingEnabled() {
-    if (this._formattingEnabled === null) {
-      this._formattingEnabled = Services.prefs.getBoolPref("browser.urlbar.formatting.enabled");
-    }
-    return this._formattingEnabled;
-  },
-
-  _mayTrimURLs: null,
-
-  get mayTrimURLs() {
-    if (this._mayTrimURLs === null) {
-      this._mayTrimURLs = Services.prefs.getBoolPref("browser.urlbar.trimURLs");
-    }
-    return this._mayTrimURLs;
-  },
-
   supportsCommand : function(cmd) {
     var isSupported = false;
     switch (cmd) {
       case "cmd_back":
       case "cmd_forward":
       case "cmd_reload":
       case "cmd_forceReload":
       case "cmd_stop":
@@ -1297,17 +1015,17 @@ var BrowserUI = {
       case "cmd_go":
         this.goToURI();
         break;
       case "cmd_home":
         this.goToURI(Browser.getHomePage());
         break;
       case "cmd_openLocation":
         ContextUI.displayNavbar();
-        this._editURI(true);
+        this._edit.beginEditing(true);
         break;
       case "cmd_addBookmark":
         ContextUI.displayNavbar();
         Appbar.onStarButton(true);
         break;
       case "cmd_bookmarks":
         PanelUI.show("bookmarks-container");
         break;
@@ -1327,17 +1045,18 @@ var BrowserUI = {
         // Only close one window
         this._closeOrQuit();
         break;
       case "cmd_close":
         this._closeOrQuit();
         break;
       case "cmd_newTab":
         this.newTab();
-        this._editURI(false);
+        this._edit.beginEditing(false);
+        ContextUI.peekTabs(kNewTabAnimationDelayMsec);
         break;
       case "cmd_closeTab":
         this.closeTab();
         break;
       case "cmd_undoCloseTab":
         this.undoCloseTab();
         break;
       case "cmd_sanitize":
@@ -1367,35 +1086,32 @@ var BrowserUI = {
   },
 
   crashReportingPrefChanged: function crashReportingPrefChanged(aState) {
     CrashReporter.submitReports = aState;
   }
 };
 
 var StartUI = {
-  get isVisible() { return this.isStartPageVisible || this.isFiltering; },
+  get isVisible() { return this.isStartPageVisible; },
   get isStartPageVisible() { return Elements.windowState.hasAttribute("startpage"); },
-  get isFiltering() { return Elements.windowState.hasAttribute("filtering"); },
 
   get maxResultsPerSection() {
     return Services.prefs.getIntPref("browser.display.startUI.maxresults");
   },
 
   sections: [
     "TopSitesStartView",
     "TopSitesSnappedView",
     "BookmarksStartView",
     "HistoryStartView",
     "RemoteTabsStartView"
   ],
 
   init: function init() {
-    Elements.startUI.addEventListener("autocompletestart", this, false);
-    Elements.startUI.addEventListener("autocompleteend", this, false);
     Elements.startUI.addEventListener("contextmenu", this, false);
     Elements.startUI.addEventListener("click", this, false);
     Elements.startUI.addEventListener("MozMousePixelScroll", this, false);
 
     this.sections.forEach(function (sectionName) {
       let section = window[sectionName];
       if (section.init)
         section.init();
@@ -1424,44 +1140,24 @@ var StartUI = {
     this.sections.forEach(function (sectionName) {
       let section = window[sectionName];
       if (section.show)
         section.show();
     });
     return true;
   },
 
-  /** Show the autocomplete popup */
-  filter: function filter() {
-    if (this.isFiltering)
-      return;
-
-    BrowserUI._edit.openPopup();
-    Elements.windowState.setAttribute("filtering", "true");
-  },
-
-  /** Hide the autocomplete popup */
-  unfilter: function unfilter() {
-    if (!this.isFiltering)
-      return;
-
-    BrowserUI._edit.closePopup();
-    Elements.windowState.removeAttribute("filtering");
-  },
-
   /** Hide the Firefox start page */
   hide: function hide(aURI) {
     aURI = aURI || Browser.selectedBrowser.currentURI.spec;
     if (!this.isStartPageVisible || this.isStartURI(aURI))
       return false;
 
     Elements.contentShowing.removeAttribute("disabled");
     Elements.windowState.removeAttribute("startpage");
-
-    this.unfilter();
     return true;
   },
 
   /** Is the current tab supposed to show the Firefox start page? */
   isStartURI: function isStartURI(aURI) {
     aURI = aURI || Browser.selectedBrowser.currentURI.spec;
     return aURI == kStartOverlayURI || aURI == "about:home";
   },
@@ -1483,22 +1179,16 @@ var StartUI = {
       // Advanced notice to CAO, so we can shuffle the nav bar in advance
       // of the keyboard transition.
       ContentAreaObserver.navBarWillBlur();
     }
   },
 
   handleEvent: function handleEvent(aEvent) {
     switch (aEvent.type) {
-      case "autocompletestart":
-        this.filter();
-        break;
-      case "autocompleteend":
-        this.unfilter();
-        break;
       case "contextmenu":
         let event = document.createEvent("Events");
         event.initEvent("MozEdgeUICompleted", true, false);
         window.dispatchEvent(event);
         break;
       case "click":
         this.onClick(aEvent);
         break;
--- a/browser/metro/base/content/browser.css
+++ b/browser/metro/base/content/browser.css
@@ -90,21 +90,21 @@ setting[type="menulist"] {
 }
 
 #chrome-selection-overlay,
 #content-selection-overlay {
   -moz-binding: url("chrome://browser/content/bindings/selectionoverlay.xml#selection-binding");
 }
 
 #urlbar-edit {
-  -moz-binding: url("chrome://browser/content/bindings/autocomplete.xml#autocomplete");
+  -moz-binding: url("chrome://browser/content/bindings/urlbar.xml#urlbar");
 }
 
-#start-autocomplete {
-  -moz-binding: url("chrome://browser/content/bindings/autocomplete.xml#autocomplete-popup");
+#urlbar-autocomplete {
+  -moz-binding: url("chrome://browser/content/bindings/urlbar.xml#urlbar-autocomplete");
 }
 
 richgrid {
   -moz-binding: url("chrome://browser/content/bindings/grid.xml#richgrid");
 }
 
 richgriditem {
   -moz-binding: url("chrome://browser/content/bindings/grid.xml#richgrid-item");
--- a/browser/metro/base/content/browser.js
+++ b/browser/metro/base/content/browser.js
@@ -569,16 +569,17 @@ var Browser = {
       tab.active = true;
 
     if (isFirstTab) {
       // Don't waste time at startup updating the whole UI; just display the URL.
       BrowserUI._titleChanged(browser);
     } else {
       // Update all of our UI to reflect the new tab's location
       BrowserUI.updateURI();
+      BrowserUI.update();
 
       let event = document.createEvent("Events");
       event.initEvent("TabSelect", true, false);
       event.lastTab = lastTab;
       tab.chromeTab.dispatchEvent(event);
     }
 
     tab.lastSelected = Date.now();
@@ -1573,17 +1574,17 @@ Tab.prototype = {
   },
 
   _copyHistoryFrom: function _copyHistoryFrom(tab) {
     let otherHistory = tab._browser._webNavigation.sessionHistory;
     let history = this._browser._webNavigation.sessionHistory;
 
     // Ensure that history is initialized
     history.QueryInterface(Ci.nsISHistoryInternal);
-    
+
     for (let i = 0, length = otherHistory.index; i <= length; i++)
       history.addEntry(otherHistory.getEntryAtIndex(i, false), true);
   },
 
   _loadUsingParams: function _loadUsingParams(aBrowser, aURI, aParams) {
     let flags = aParams.flags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
     let postData = ("postData" in aParams && aParams.postData) ? aParams.postData.value : null;
     let referrerURI = "referrerURI" in aParams ? aParams.referrerURI : null;
--- a/browser/metro/base/content/browser.xul
+++ b/browser/metro/base/content/browser.xul
@@ -174,17 +174,18 @@
     <key id="key_selectLastTab" oncommand="BrowserUI.selectTabAtIndex(-1);" key="9" modifiers="accel"/>
   </keyset>
 
   <stack id="stack" flex="1">
     <!-- Page Area -->
     <vbox id="page">
       <vbox id="tray" class="tray-toolbar" observes="bcast_windowState" >
         <!-- Tabs -->
-        <hbox id="tabs-container" observes="bcast_windowState">
+        <!-- onclick handler to work around bug 837242 -->
+        <hbox id="tabs-container" observes="bcast_windowState" onclick="void(0);">
           <box id="tabs" flex="1"
                 observes="bcast_preciseInput"
                 onselect="BrowserUI.selectTabAndDismiss(this);"
                 onclosetab="BrowserUI.closeTab(this);"/>
           <vbox id="tabs-controls">
             <toolbarbutton id="newtab-button" command="cmd_newTab" label="&newtab.label;"/>
           </vbox>
         </hbox>
@@ -223,18 +224,16 @@
               <label class="meta-section-title" value="&snappedBookmarksHeader.label;"
                 onclick="PanelUI.show('bookmarks-container');"/>
               <label class="meta-section-title" value="&snappedHistoryHeader.label;"
                 onclick="PanelUI.show('history-container');" inputProcessing="true"/>
               <label id="snappedRemoteTabsLabel" class="meta-section-title" value="&snappedRemoteTabsHeader.label;"
                 onclick="PanelUI.show('remotetabs-container');" inputProcessing="true"/>
             </scrollbox>
           </hbox>
-          <!-- Autocompletion interface -->
-          <box id="start-autocomplete" observes="bcast_windowState"/>
         </hbox>
       </vbox> <!-- end tray -->
 
       <!-- Content viewport -->
       <stack id="content-viewport">
         <deck id="browsers" flex="1" observes="bcast_preciseInput"/>
         <box id="vertical-scroller" class="scroller" orient="vertical" end="0" top="0"/>
         <box id="horizontal-scroller" class="scroller" orient="horizontal" left="0" bottom="0"/>
@@ -251,54 +250,58 @@
               observes="cmd_back"></html:div>
 
     <!-- Navbar -->
     <appbar id="navbar" mousethrough="never" observes="bcast_windowState">
       <hbox id="progress-container" layer="true">
         <hbox id="progress-control" />
       </hbox>
 
-      <!-- Main Toolbar -->
-      <toolbar id="toolbar" observes="bcast_windowState" flex="1">
-        <observes element="bcast_windowState" attribute="*"/>
-        <observes element="bcast_urlbarState" attribute="*"/>
+      <vbox id="toolbar-autocomplete" flex="1">
+        <!-- Autocomplete -->
+        <scrollbox flex="1">
+          <hbox id="urlbar-autocomplete" observes="bcast_windowState"/>
+        </scrollbox>
 
-        <toolbarbutton id="back-button" class="appbar-primary" command="cmd_back"/>
-        <toolbarbutton id="forward-button" class="appbar-primary" command="cmd_forward"/>
+        <!-- Main Toolbar -->
+        <toolbar id="toolbar" observes="bcast_windowState" flex="1">
+          <observes element="bcast_windowState" attribute="*"/>
+          <observes element="bcast_urlbarState" attribute="*"/>
+
+          <toolbarbutton id="back-button" class="appbar-primary" command="cmd_back"/>
+          <toolbarbutton id="forward-button" class="appbar-primary" command="cmd_forward"/>
 
-        <hbox id="urlbar-container" flex="1" observes="bcast_urlbarState">
-          <hbox id="urlbar" flex="1">
-            <box id="identity-box" role="button">
-              <hbox id="identity-box-inner" align="center" mousethrough="always">
-                <image id="identity-icon"/>
-              </hbox>
-            </box>
+          <hbox id="urlbar-container" flex="1" observes="bcast_urlbarState">
+            <hbox id="urlbar" flex="1">
+              <box id="identity-box" role="button">
+                <hbox id="identity-box-inner" align="center" mousethrough="always">
+                  <image id="identity-icon"/>
+                </hbox>
+              </box>
 
-            <textbox id="urlbar-edit"
-                     type="url"
-                     class="uri-element"
-                     autocompletesearch="history"
-                     autocompletepopup="start-autocomplete"
-                     completeselectedindex="true"
-                     placeholder="&urlbar.emptytext;"
-                     flex="1"
-                     onpaste="this.focus();"
-                     ontextentered="BrowserUI.handleUrlbarEnter(param);"
-                     onblur="BrowserUI._urlbarBlurred();"/>
+              <textbox id="urlbar-edit"
+                       type="url"
+                       class="uri-element"
+                       autocompletesearch="history"
+                       autocompletepopup="urlbar-autocomplete"
+                       completeselectedindex="true"
+                       placeholder="&urlbar.emptytext;"
+                       flex="1"/>
+            </hbox>
+
+            <toolbarbutton id="reload-button" oncommand="CommandUpdater.doCommand(event.shiftKey ? 'cmd_forceReload' : 'cmd_reload');"/>
+            <toolbarbutton id="stop-button" command="cmd_stop"/>
           </hbox>
 
-          <toolbarbutton id="reload-button" oncommand="CommandUpdater.doCommand(event.shiftKey ? 'cmd_forceReload' : 'cmd_reload');"/>
-          <toolbarbutton id="stop-button" command="cmd_stop"/>
-        </hbox>
-
-        <toolbarbutton id="download-button" oncommand="Appbar.onDownloadButton()"/>
-        <toolbarbutton id="star-button" class="appbar-primary" type="checkbox" oncommand="Appbar.onStarButton()"/>
-        <toolbarbutton id="pin-button" class="appbar-primary" type="checkbox" oncommand="Appbar.onPinButton()"/>
-        <toolbarbutton id="menu-button" class="appbar-primary" oncommand="Appbar.onMenuButton(event)"/>
-       </toolbar>
+          <toolbarbutton id="download-button" oncommand="Appbar.onDownloadButton()"/>
+          <toolbarbutton id="star-button" class="appbar-primary" type="checkbox" oncommand="Appbar.onStarButton()"/>
+          <toolbarbutton id="pin-button" class="appbar-primary" type="checkbox" oncommand="Appbar.onPinButton()"/>
+          <toolbarbutton id="menu-button" class="appbar-primary" oncommand="Appbar.onMenuButton(event)"/>
+        </toolbar>
+      </vbox>
     </appbar>
 
     <vbox id="panel-container" hidden="true" class="window-width window-height meta" observes="bcast_windowState">
       <hbox id="panel-header">
         <toolbarbutton id="panel-close-button" command="cmd_panel"/>
 
         <menulist id="panel-view-switcher" oncommand="PanelUI.switchPane(this.value);">
           <menupopup>
@@ -348,17 +351,17 @@
     </vbox>
 
     <!-- Find bar -->
     <appbar id="findbar" class="window-width findbar-box" orient="horizontal" pack="start">
       <textbox id="findbar-textbox" class="search-bar findbar-item" oncommand="FindHelperUI.search(this.value)" oninput="FindHelperUI.updateCommands(this.value);" type="search"/>
       <button class="findbar-item previous-button" command="cmd_findPrevious"/>
       <button class="findbar-item next-button" command="cmd_findNext"/>
       <spacer flex="1"/>
-      <button class="findbar-item close-button" command="cmd_findClose"/>
+      <button id="findbar-close" class="findbar-item close-button" command="cmd_findClose"/>
     </appbar>
 
     <!-- Context button bar -->
     <appbar id="contextappbar">
       <toolbar id="contextualactions-tray" flex="1">
         <toolbarbutton id="delete-selected-button" hidden="true" fade="true" oncommand="Appbar.dispatchContextualAction('delete')"/>
         <toolbarbutton id="restore-selected-button" hidden="true" fade="true" oncommand="Appbar.dispatchContextualAction('restore')"/>
         <toolbarbutton id="pin-selected-button" hidden="true" fade="true" oncommand="Appbar.dispatchContextualAction('pin')"/>
--- a/browser/metro/base/content/helperui/MenuUI.js
+++ b/browser/metro/base/content/helperui/MenuUI.js
@@ -333,43 +333,57 @@ var MenuControlUI = {
     this._menuPopup.hide();
   }
 };
 
 function MenuPopup(aPanel, aPopup) {
   this._panel = aPanel;
   this._popup = aPopup;
   this._wantTypeBehind = false;
+  this._willReshowPopup = false;
 
   window.addEventListener('MozAppbarShowing', this, false);
 }
 MenuPopup.prototype = {
   get _visible() { return !this._panel.hidden; },
   get _commands() { return this._popup.childNodes[0]; },
 
   show: function (aPositionOptions) {
-    if (this._visible)
-      return;
+    if (this._visible) {
+      this._willReshowPopup = true;
+      let self = this;
+      this._panel.addEventListener("transitionend", function () {
+        self._show(aPositionOptions);
+        self._panel.removeEventListener("transitionend", arguments.callee);
+      });
+    } else {
+      this._show(aPositionOptions);
+    }
+  },
 
+  _show: function (aPositionOptions) {
     window.addEventListener("keypress", this, true);
     window.addEventListener("mousedown", this, true);
     Elements.stack.addEventListener("PopupChanged", this, false);
     Elements.browsers.addEventListener("PanBegin", this, false);
 
     this._panel.hidden = false;
     this._position(aPositionOptions || {});
 
     let self = this;
     this._panel.addEventListener("transitionend", function () {
       self._panel.removeEventListener("transitionend", arguments.callee);
       self._panel.removeAttribute("showingfrom");
 
+      let eventName = self._willReshowPopup ? "popupmoved" : "popupshown";
       let event = document.createEvent("Events");
-      event.initEvent("popupshown", true, false);
-      document.dispatchEvent(event);
+      event.initEvent(eventName, true, false);
+      self._panel.dispatchEvent(event);
+
+      self._willReshowPopup = false;
     });
 
     let popupFrom = !aPositionOptions.bottomAligned ? "above" : "below";
     this._panel.setAttribute("showingfrom", popupFrom);
 
     // Ensure the panel actually gets shifted before getting animated
     setTimeout(function () {
       self._panel.setAttribute("showing", "true");
@@ -388,19 +402,21 @@ MenuPopup.prototype = {
     let self = this;
     this._panel.addEventListener("transitionend", function () {
       self._panel.removeEventListener("transitionend", arguments.callee);
       self._panel.removeAttribute("hiding");
       self._panel.hidden = true;
       self._popup.style.maxWidth = "none";
       self._popup.style.maxHeight = "none";
 
-      let event = document.createEvent("Events");
-      event.initEvent("popuphidden", true, false);
-      document.dispatchEvent(event);
+      if (!self._willReshowPopup) {
+        let event = document.createEvent("Events");
+        event.initEvent("popuphidden", true, false);
+        self._panel.dispatchEvent(event);
+      }
     });
 
     this._panel.setAttribute("hiding", "true");
     setTimeout(()=>this._panel.removeAttribute("showing"), 0);
   },
 
   _position: function _position(aPositionOptions) {
     let aX = aPositionOptions.xPos;
--- a/browser/metro/base/jar.mn
+++ b/browser/metro/base/jar.mn
@@ -16,17 +16,17 @@ chrome.jar:
   content/bindings/toggleswitch.xml            (content/bindings/toggleswitch.xml)
   content/bindings/browser.xml                 (content/bindings/browser.xml)
   content/bindings/browser.js                  (content/bindings/browser.js)
   content/bindings/downloads.xml               (content/bindings/downloads.xml)
   content/bindings/console.xml                 (content/bindings/console.xml)
   content/bindings/dialog.xml                  (content/bindings/dialog.xml)
   content/bindings/arrowbox.xml                (content/bindings/arrowbox.xml)
   content/bindings/grid.xml                    (content/bindings/grid.xml)
-  content/bindings/autocomplete.xml            (content/bindings/autocomplete.xml)
+  content/bindings/urlbar.xml                  (content/bindings/urlbar.xml)
   content/bindings/appbar.xml                  (content/bindings/appbar.xml)
   content/bindings/flyoutpanel.xml             (content/bindings/flyoutpanel.xml)
   content/bindings/selectionoverlay.xml        (content/bindings/selectionoverlay.xml)
   content/bindings/cssthrobber.xml             (content/bindings/cssthrobber.xml)
   content/bindings/popup.xml                   (content/bindings/popup.xml)
 
 * content/flyouts/flyoutUI.js                  (content/flyouts/flyoutUI.js)
 * content/flyouts/aboutFlyout.js               (content/flyouts/aboutFlyout.js)
--- a/browser/metro/base/tests/mochitest/Makefile.in
+++ b/browser/metro/base/tests/mochitest/Makefile.in
@@ -7,24 +7,27 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 BROWSER_TESTS = \
   head.js \
+  browser_urlbar.js \
   browser_bookmarks.js \
   browser_canonizeURL.js \
   browser_context_menu_tests.js \
   browser_context_menu_tests_01.html \
   browser_context_menu_tests_02.html \
   browser_context_menu_tests_03.html \
   browser_context_ui.js \
   browser_downloads.js \
+  browser_findbar.js \
+  browser_findbar.html \
   browser_history.js \
   browser_onscreen_keyboard.js \
   browser_onscreen_keyboard.html \
   browser_prefs_ui.js \
   browser_remotetabs.js \
   browser_tabs.js \
   browser_test.js \
   browser_tiles.js \
@@ -55,16 +58,17 @@ BROWSER_TESTS += \
   $(NULL)
 endif
 
 BROWSER_TEST_RESOURCES = \
   res/image01.png \
   res/textblock01.html \
   res/textinput01.html \
   res/textarea01.html \
+  res/testEngine.xml \
   $(NULL)
 
 libs:: $(BROWSER_TESTS)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/metro/$(relativesrcdir)
 
 libs:: $(BROWSER_TEST_RESOURCES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/metro/$(relativesrcdir)/res
 
--- a/browser/metro/base/tests/mochitest/browser_canonizeURL.js
+++ b/browser/metro/base/tests/mochitest/browser_canonizeURL.js
@@ -11,11 +11,11 @@ function test() {
     ["example", {ctrlKey: true}, "http://www.example.com/"],
     ["example.org", {ctrlKey: true}, "example.org"],
     ["example", {shiftKey: true}, "http://www.example.net/"],
     ["example", {shiftKey: true, ctrlKey: true}, "http://www.example.org/"],
     ["  example  ", {ctrlKey: true}, "http://www.example.com/"],
     [" example/a ", {ctrlKey: true}, "http://www.example.com/a"]
   ];
   for (let [input, modifiers, result] of testcases) {
-    is(BrowserUI._canonizeURL(input, modifiers), result, input + " -> " + result);
+    is(BrowserUI._edit._canonizeURL(input, modifiers), result, input + " -> " + result);
   }
 }
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/browser_findbar.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="UTF-8">
+    <title>Find bar tests</title>
+  </head>
+  <body>
+    <p>Find bar tests</title>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/browser_findbar.js
@@ -0,0 +1,56 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+function test() {
+  runTests();
+}
+
+gTests.push({
+  desc: "Access the find bar with the keyboard",
+  run: function() {
+    let tab = yield addTab(chromeRoot + "browser_findbar.html");
+    yield waitForCondition(() => BrowserUI.ready);
+    is(Elements.findbar.isShowing, false, "Find bar is hidden by default");
+
+    EventUtils.synthesizeKey("f", { accelKey: true });
+    yield waitForEvent(Elements.findbar, "transitionend");
+    is(Elements.findbar.isShowing, true, "Show find bar with Ctrl-F");
+
+    let textbox = document.getElementById("findbar-textbox");
+    is(textbox.value, "", "Find bar is empty");
+
+    EventUtils.sendString("bar");
+    is(textbox.value, "bar", "Type 'bar' into find bar");
+
+    EventUtils.synthesizeKey("VK_ESCAPE", { accelKey: true });
+    yield waitForEvent(Elements.findbar, "transitionend");
+    is(Elements.findbar.isShowing, false, "Hide find bar with Esc");
+
+    Browser.closeTab(tab);
+  }
+});
+
+gTests.push({
+  desc: "Show and hide the find bar with mouse",
+  run: function() {
+    let tab = yield addTab(chromeRoot + "browser_findbar.html");
+    yield waitForCondition(() => BrowserUI.ready);
+    is(Elements.findbar.isShowing, false, "Find bar is hidden by default");
+
+    ContextUI.displayNavbar();
+    EventUtils.sendMouseEvent({ type: "click" }, "menu-button");
+    EventUtils.sendMouseEvent({ type: "click" }, "context-findinpage");
+    yield waitForEvent(Elements.findbar, "transitionend");
+    is(Elements.findbar.isShowing, true, "Show find bar with menu item");
+
+    EventUtils.synthesizeMouse(document.getElementById("findbar-close"), 1, 1, {});
+    yield waitForEvent(Elements.findbar, "transitionend");
+    is(Elements.findbar.isShowing, false, "Hide find bar with close button");
+
+    Browser.closeTab(tab);
+  }
+});
--- a/browser/metro/base/tests/mochitest/browser_selection_urlbar.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_urlbar.js
@@ -51,28 +51,51 @@ gTests.push({
     ok(SelectionHelperUI.isCaretUIVisible, "caret ui active");
 
     // to the right
     let xpos = SelectionHelperUI.caretMark.xPos;
     let ypos = SelectionHelperUI.caretMark.yPos + 10;
     var touchdrag = new TouchDragAndHold();
     yield touchdrag.start(gWindow, xpos, ypos, 900, ypos);
     yield waitForCondition(function () {
-      return getTrimmedSelection(edit).toString() == 
+      return getTrimmedSelection(edit).toString() ==
         "mochitests/content/metro/browser/metro/base/tests/mochitest/res/textblock01.html";
     }, kCommonWaitMs, kCommonPollMs);
     touchdrag.end();
     yield waitForMs(100);
 
     ok(SelectionHelperUI.isSelectionUIVisible, "selection ui active");
 
     // taps on the urlbar-edit leak a ClientRect property on the window
     delete window.r;
   },
 });
 
+gTests.push({
+  desc: "bug 887120 - tap & hold to paste into urlbar",
+  run: function bug887120_test() {
+    gWindow = window;
+
+    yield showNavBar();
+    let edit = document.getElementById("urlbar-edit");
+
+    SpecialPowers.clipboardCopyString("mozilla");
+    sendContextMenuClickToElement(window, edit);
+    yield waitForEvent(document, "popupshown");
+
+    ok(ContextMenuUI._menuPopup._visible, "is visible");
+    let paste = document.getElementById("context-paste");
+    ok(!paste.hidden, "paste item is visible");
+
+    sendElementTap(window, paste);
+    ok(edit.popup.popupOpen, "bug: popup should be showing");
+
+    delete window.r;
+  }
+});
+
 function test() {
   if (!isLandscapeMode()) {
     todo(false, "browser_selection_tests need landscape mode to run.");
     return;
   }
   runTests();
 }
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/browser_urlbar.js
@@ -0,0 +1,232 @@
+// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+var gEdit = null;
+
+/*=============================================================================
+  Search engine mocking utilities
+=============================================================================*/
+
+var gEngine = null;
+
+const kSearchEngineName = "Foo";
+const kSearchEngineURI = chromeRoot + "res/testEngine.xml";
+
+/*
+ * addMockSearchDefault - adds a mock search engine to the top of the engine list.
+ */
+function addMockSearchDefault(aTimeoutMs) {
+  let deferred = Promise.defer();
+  let timeoutMs = aTimeoutMs || kDefaultWait;
+  let timerID = 0;
+
+  function engineAddObserver(aSubject, aTopic, aData) {
+    if (aData != "engine-added")
+      return;
+
+    gEngine = Services.search.getEngineByName(kSearchEngineName);
+    Services.obs.removeObserver(engineAddObserver, "browser-search-engine-modified");
+    clearTimeout(timerID);
+    gEngine.hidden = false;
+    ok(gEngine, "mock engine was added");
+    deferred.resolve();
+  }
+
+  if (gEngine) {
+    deferred.resolve();
+    return deferred.promise;
+  }
+
+  timerID = setTimeout(function ids_canceller() {
+    Services.obs.removeObserver(engineAddObserver, "browser-search-engine-modified");
+    deferred.reject(new Error("search add timeout"));
+  }, timeoutMs);
+
+  Services.obs.addObserver(engineAddObserver, "browser-search-engine-modified", false);
+  Services.search.addEngine(kSearchEngineURI, Ci.nsISearchEngine.DATA_XML,
+                            "data:image/x-icon,%00", false);
+  return deferred.promise;
+}
+
+/*
+ * removeMockSearchDefault - removes mock "Foo" search engine.
+ */
+
+function removeMockSearchDefault(aTimeoutMs) {
+  let deferred = Promise.defer();
+  let timeoutMs = aTimeoutMs || kDefaultWait;
+  let timerID = 0;
+
+  function engineRemoveObserver(aSubject, aTopic, aData) {
+    if (aData != "engine-removed")
+      return;
+
+    clearTimeout(timerID);
+    gEngine = null;
+    Services.obs.removeObserver(engineRemoveObserver, "browser-search-engine-modified");
+    deferred.resolve();
+  }
+
+  if (!gEngine) {
+    deferred.resolve();
+    return deferred.promise;
+  }
+
+  timerID = setTimeout(function ids_canceller() {
+    Services.obs.removeObserver(engineRemoveObserver, "browser-search-engine-modified");
+    deferred.reject(new Error("search remove timeout"));
+  }, timeoutMs);
+
+  Services.obs.addObserver(engineRemoveObserver, "browser-search-engine-modified", false);
+  Services.search.removeEngine(gEngine);
+  return deferred.promise;
+}
+
+/*=============================================================================
+  Test cases
+=============================================================================*/
+
+function test() {
+  runTests();
+}
+
+
+function setUp() {
+  if (!gEdit)
+    gEdit = document.getElementById("urlbar-edit");
+
+  yield addTab("about:start");
+  yield showNavBar();
+  yield waitForCondition(function () {
+    return StartUI.isStartPageVisible;
+  });
+}
+
+function tearDown() {
+  yield removeMockSearchDefault();
+  Browser.closeTab(Browser.selectedTab, { forceClose: true });
+  delete window.r;
+}
+
+gTests.push({
+  desc: "search engines update",
+  setUp: setUp,
+  tearDown: tearDown,
+  run: function testSearchEngine() {
+    // If the XBL hasn't initialized yet, open the popup so that it will.
+    if (gEdit.popup._searches == undefined) {
+      gEdit.openPopup();
+      gEdit.closePopup();
+    }
+
+    let numSearches = gEdit.popup._searches.itemCount;
+    function getEngineItem() {
+      return gEdit.popup._searches.querySelector("richgriditem[value="+kSearchEngineName+"]");
+    }
+
+    yield addMockSearchDefault();
+    ok(gEdit.popup._searches.itemCount == numSearches + 1, "added search engine count");
+    ok(getEngineItem(), "added search engine item");
+
+    yield removeMockSearchDefault();
+    ok(gEdit.popup._searches.itemCount == numSearches, "normal search engine count");
+    ok(!getEngineItem(), "added search engine item");
+  }
+});
+
+gTests.push({
+  desc: "display autocomplete while typing, handle enter",
+  setUp: setUp,
+  tearDown: tearDown,
+  run: function testUrlbarTyping() {
+    sendElementTap(window, gEdit);
+    ok(gEdit.isEditing, "focus urlbar: in editing mode");
+    ok(!gEdit.popup.popupOpen, "focus urlbar: popup not open yet");
+
+    EventUtils.sendString("about:blank", window);
+    let opened = yield waitForCondition(() => gEdit.popup.popupOpen);
+    ok(opened, "type in urlbar: popup opens");
+
+    EventUtils.synthesizeKey("VK_RETURN", {}, window);
+    let closed = yield waitForCondition(() => !gEdit.popup.popupOpen);
+    ok(closed, "hit enter in urlbar: popup closes, page loads");
+    ok(!gEdit.isEditing, "hit enter in urlbar: not in editing mode");
+  }
+});
+
+gTests.push({
+  desc: "display and select a search with keyboard",
+  setUp: setUp,
+  tearDown: tearDown,
+  run: function testSearchKeyboard() {
+    yield addMockSearchDefault();
+
+    sendElementTap(window, gEdit);
+    ok(gEdit.isEditing, "focus urlbar: in editing mode");
+    ok(!gEdit.popup.popupOpen, "focus urlbar: popup not open yet");
+
+    let search = "mozilla";
+    EventUtils.sendString(search, window);
+    yield waitForCondition(() => gEdit.popup.popupOpen);
+
+    // XXX We should probably change the keyboard selection behavior entirely,
+    // given that it makes little to no sense, but that's a job for a later patch.
+
+    EventUtils.synthesizeKey("VK_DOWN", {}, window);
+    is(gEdit.popup.selectedIndex, -1, "key select search: no result selected");
+    is(gEdit.popup._searches.selectedIndex, 0, "key select search: first search selected");
+
+    let engines = Services.search.getVisibleEngines();
+    for (let i = 0, max = engines.length - 1; i < max; i++) {
+      is(gEdit.popup._searches.selectedIndex, i, "key select search: next index");
+      EventUtils.synthesizeKey("VK_DOWN", {}, window);
+    }
+
+    let existingValue = gEdit.value;
+    EventUtils.synthesizeKey("VK_RETURN", {}, window);
+
+    yield waitForCondition(() => gEdit.value != existingValue);
+
+    let closed = yield waitForCondition(() => !gEdit.popup.popupOpen);
+    ok(closed, "hit enter in urlbar: popup closes, page loads");
+    ok(!gEdit.isEditing, "hit enter in urlbar: not in editing mode");
+
+    let searchSubmission = gEngine.getSubmission(search, null);
+    let trimmedSubmission = gEdit.trimValue(searchSubmission.uri.spec);
+    is(gEdit.value, trimmedSubmission, "hit enter in urlbar: search conducted");
+
+    yield removeMockSearchDefault();
+  }
+});
+
+gTests.push({
+  desc: "display and select a search with touch",
+  setUp: setUp,
+  tearDown: tearDown,
+  run: function testUrlbarSearchesTouch() {
+    yield addMockSearchDefault();
+
+    sendElementTap(window, gEdit);
+    ok(gEdit.isEditing, "focus urlbar: in editing mode");
+    ok(!gEdit.popup.popupOpen, "focus urlbar: popup not open yet");
+
+    let search = "mozilla";
+    EventUtils.sendString(search, window);
+    yield waitForCondition(() => gEdit.popup.popupOpen);
+
+    sendElementTap(window, gEdit.popup._searches.lastChild);
+
+    let closed = yield waitForCondition(() => !gEdit.popup.popupOpen);
+    ok(closed, "tap search option: popup closes, page loads");
+    ok(!gEdit.isEditing, "tap search option: not in editing mode");
+
+    let searchSubmission = gEngine.getSubmission(search, null);
+    let trimmedSubmission = gEdit.trimValue(searchSubmission.uri.spec);
+    is(gEdit.value, trimmedSubmission, "tap search option: search conducted");
+  }
+});
+
--- a/browser/metro/base/tests/mochitest/head.js
+++ b/browser/metro/base/tests/mochitest/head.js
@@ -703,18 +703,18 @@ TouchDragAndHold.prototype = {
     this._win = null;
   },
 };
 
 /*=============================================================================
   System utilities
 =============================================================================*/
 
- /*
- * emptyClipboard - clear the windows clipbaord.
+/*
+ * emptyClipboard - clear the windows clipboard.
  */
 function emptyClipboard() {
   Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard)
                                        .emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
 }
 
 /*
  * purgeEventQueue - purges the event queue on the calling thread.
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/res/testEngine.xml
@@ -0,0 +1,12 @@
+<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
+                       xmlns:moz="http://www.mozilla.org/2006/browser/search/">
+  <ShortName>Foo</ShortName>
+  <Description>Foo Search</Description>
+  <InputEncoding>utf-8</InputEncoding>
+  <Image width="16" height="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC</Image>
+  <Url type="text/html" method="GET" template="http://mochi.test:8888/browser/browser/components/search/test/?search">
+    <Param name="test" value="{searchTerms}"/>
+  </Url>
+  <moz:SearchForm>http://mochi.test:8888/browser/browser/components/search/test/</moz:SearchForm>
+  <moz:Alias>fooalias</moz:Alias>
+</OpenSearchDescription>
--- a/browser/metro/theme/browser.css
+++ b/browser/metro/theme/browser.css
@@ -243,17 +243,17 @@ documenttab[selected] .documenttab-selec
   padding: 0px;
   margin-top: -30px;
   margin-left: -18px;
   pointer-events: auto;
 }
 
 /* Toolbar ------------------------------------------------------------------ */
 
-#toolbar {
+#toolbar-autocomplete {
   background-color: @panel_light_color@;
 }
 
 #toolbar > #back-button {
   list-style-image: url(chrome://browser/skin/images/appbar-back.png);
   position: relative;
   z-index: 1;
   transition: opacity @forward_transition_length@ ease-out;
@@ -359,26 +359,26 @@ appbar {
   position: fixed;
   bottom: 0;
   width: 100%;
   transform: translateY(100%);
   transition: transform @metro_animation_duration@ @metro_animation_easing@;
   font-size: 0;
 }
 
-appbar > toolbar {
+appbar toolbar {
   -moz-appearance: none;
   -moz-box-align: center;
   border: 0;
   width: 100%;
   min-height: @toolbar_height@;
   font-size: 1rem;
 }
 
-appbar > toolbar > toolbarbutton {
+appbar toolbar > toolbarbutton {
   border: 0;
   margin: 0 @toolbar_horizontal_spacing@;
   padding: 0;
   /* Don't inherit background-color from toolbarbutton[checked="true"] */
   background-color: transparent;
 }
 
 appbar > toolbar > toolbarbutton[disabled] {
--- a/browser/metro/theme/platform.css
+++ b/browser/metro/theme/platform.css
@@ -189,37 +189,38 @@ menulist {
 }
 
 .menu-popup richlistitem {
   width: 100%;
   min-height: @touch_button_small@;
   min-width: @touch_action_minwidth@; /* keep the button from being too narrow */
   border: 0 none;
   -moz-box-align: center;
+  font-weight: 600;
 }
 
 .menu-popup richlistitem:not([disabled]):hover {
-  background-color: #dedad0;
+  background-color: #ccc;
   color: black;
 }
 
 .menu-popup richlistitem:not([disabled]):active {
-  background-color: @selected_color@ !important;
-  color: black;
+  background-color: black;
+  color: white;
 }
 
 .menu-popup > richlistbox[left-hand="true"] > richlistitem {
   padding-left: 50px;
 }
 
 .menu-popup > richlistbox[left-hand="false"] > richlistitem {
   padding-right: 50px;
 }
 
-/* form select popup */
+/* Additional styles applied to popups for form <select> elements. */
 
 #select-container {
   padding: 0;
   position: absolute;
   background-color: transparent;
 }
 
 #select-commands {
@@ -228,44 +229,44 @@ menulist {
   border: #000 solid @metro_border_thick@;
   display: -moz-box;
   -moz-user-focus: ignore;
   -moz-box-align: stretch;
   width: 100%;
 }
 
 /* listcell element doesn't have flex="1" so we need to force it */
-#select-commands .option-command > listcell {
+.option-command > listcell {
   -moz-box-flex: 1 !important;
 }
 
 .option-command {
   min-height: @touch_button_small@;
   min-width: @touch_action_minwidth@; /* keep the button from being too narrow */
   border: 0 none;
 }
 
 .option-command.selected {
-  background-color: @selected_color@ !important;
-  color: black;
+  background-color: #ff8000;
+  color: white;
 }
 
 .option-command.optgroup {
   font-weight: bold;
   font-style: italic;
   pointer-events: none;
 }
 
 .option-command:not([disabled]):hover {
-  background-color: #dedad0;
+  background-color: #f0f0f0;
   color: black;
 }
 
 .option-command:not([disabled]):active {
-  background-color: @selected_color@ !important;
+  background-color: #d3d3d3;
   color: black;
 }
 
 .select-popup > richlistbox > scrollbox {
   width: 100%;
   overflow-x: hidden !important;
 }
 
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -86,21 +86,16 @@ toolbarbutton.bookmark-item[open="true"]
   -moz-padding-end: 2px;
 }
 
 .bookmark-item > .toolbarbutton-icon {
   width: 16px;
   height: 16px;
 }
 
-#PlacesToolbarItems > .bookmark-item:not([image]):not([label=""]):not([container]) > .toolbarbutton-icon {
-  display: none;
-}
-
-
 /* Prevent [mode="icons"] from hiding the label */
 .bookmark-item > .toolbarbutton-text {
   display: -moz-box !important;
 }
 
 .bookmark-item > .toolbarbutton-menu-dropmarker {
   display: none;
 }
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -211,21 +211,16 @@ toolbarbutton.bookmark-item > menupopup 
 }
 
 .bookmark-item > .toolbarbutton-icon {
   width: 16px;
   min-height: 16px;
   max-height: 16px;
 }
 
-#PlacesToolbarItems > .bookmark-item:not([image]):not([label=""]):not([container]) > .toolbarbutton-icon {
-  display: none;
-}
-
-
 .bookmark-item > .toolbarbutton-icon[label]:not([label=""]),
 .bookmark-item > .toolbarbutton-icon[type="menu"] {
   -moz-margin-end: 5px;
 }
 
 .bookmark-item[container] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
--- a/browser/themes/shared/identity-block.inc.css
+++ b/browser/themes/shared/identity-block.inc.css
@@ -28,27 +28,27 @@
 %ifdef MOZ_OFFICIAL_BRANDING
   color: rgb(229,115,0);
   background-image: linear-gradient(rgba(229,114,0,0),
                                     rgba(229,114,0,.5) 35%,
                                     rgba(229,114,0,.5) 65%,
                                     rgba(229,114,0,0));
 %else
 %if MOZ_UPDATE_CHANNEL == aurora
-  color: hsl(214,90%,23%);
-  background-image: linear-gradient(hsla(208,99%,37%,0),
-                                    hsla(214,90%,23%,.5) 35%,
-                                    hsla(214,90%,23%,.5) 65%,
-                                    hsla(214,90%,23%,0));
+  color: rgb(51,30,84);
+  background-image: linear-gradient(rgba(51,30,84,0),
+                                    rgba(51,30,84,.5) 35%,
+                                    rgba(51,30,84,.5) 65%,
+                                    rgba(51,30,84,0));
 %else
-  color: hsl(211,33%,32%);
-  background-image: linear-gradient(hsla(211,33%,32%,0),
-                                    hsla(211,33%,32%,.5) 35%,
-                                    hsla(211,33%,32%,.5) 65%,
-                                    hsla(211,33%,32%,0));
+  color: rgb(0,33,71);
+  background-image: linear-gradient(rgba(0,33,71,0),
+                                    rgba(0,33,71,.5) 35%,
+                                    rgba(0,33,71,.5) 65%,
+                                    rgba(0,33,71,0));
 %endif
 %endif
 }
 
 /* page proxy icon */
 
 #page-proxy-favicon {
   width: 16px;
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -85,16 +85,28 @@
     border-color: hsla(210,54%,20%,.35) hsla(210,54%,20%,.37) hsla(210,54%,20%,.4);
   }
 
   #navigator-toolbox[tabsontop=true] #urlbar:not(:-moz-lwtheme)[focused],
   #navigator-toolbox[tabsontop=true] .searchbar-textbox:not(:-moz-lwtheme)[focused] {
     border-color: hsla(206,100%,60%,.65) hsla(206,100%,55%,.65) hsla(206,100%,50%,.65);
   }
 
+  #sidebar-header {
+    -moz-appearance: none;
+    color: black;
+    background-color: #EEF3FA;
+    border-bottom: none;
+    text-shadow: none;
+  }
+
+  #sidebar-title {
+    font-weight: bold;
+  }
+
   .sidebar-splitter {
     border: 0;
     -moz-border-end: 1px solid #A9B7C9;
     min-width: 0;
     width: 3px;
     background-color: transparent;
     -moz-margin-start: -3px;
     position: relative;
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -607,20 +607,16 @@ toolbarbutton.bookmark-item[open="true"]
   -moz-padding-end: 2px;
 }
 
 .bookmark-item:not(#bookmarks-menu-button) > .toolbarbutton-icon {
   width: 16px;
   height: 16px;
 }
 
-#PlacesToolbarItems > .bookmark-item:not([image]):not([label=""]):not([container]) > .toolbarbutton-icon {
-  display: none;
-}
-
 /* Prevent [mode="icons"] from hiding the label */
 .bookmark-item > .toolbarbutton-text {
   display: -moz-box !important;
 }
 
 .bookmark-item > .toolbarbutton-menu-dropmarker {
   display: none;
 }
--- a/browser/themes/windows/places/places-aero.css
+++ b/browser/themes/windows/places/places-aero.css
@@ -10,9 +10,13 @@
   #history-panel {
     background-color: #EEF3FA;
   }
 
   .sidebar-placesTree {
     background-color: transparent;
     border-top: none;
   }
+
+  .sidebar-placesTreechildren::-moz-tree-cell-text(leaf, hover) {
+    text-decoration: none;
+  }
 }
new file mode 100644
--- /dev/null
+++ b/build/compare-mozconfig/compare-mozconfigs-wrapper.py
@@ -0,0 +1,53 @@
+#!/usr/bin/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/.
+
+import subprocess
+import sys
+from os import path
+from buildconfig import substs
+
+def determine_platform():
+    platform_mapping = {'WINNT': {'x86_64': 'win64',
+                                  'i386': 'win32'},
+                        'Darwin': {'x86_64': 'macosx-universal',
+                                   'i386':'macosx-universal'},
+                        'Linux': {'x86_64': 'linux64',
+                                  'i386': 'linux32'}}
+
+    os_type = substs['OS_TARGET']
+    cpu_type = substs['TARGET_CPU']
+    return platform_mapping.get(os_type, {}).get(cpu_type, None)
+
+def main():
+    """ A wrapper script that calls compare-mozconfig.py
+    based on the platform that the machine is building for"""
+    platform = determine_platform()
+
+    if platform is not None:
+        python_exe = substs['PYTHON']
+        topsrcdir = substs['top_srcdir']
+
+        # construct paths and args for compare-mozconfig
+        browser_dir = path.join(topsrcdir, 'browser')
+        script_path = path.join(topsrcdir, 'build/compare-mozconfig/compare-mozconfigs.py')
+        whitelist_path = path.join(browser_dir, 'config/mozconfigs/whitelist')
+        beta_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'beta')
+        release_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'release')
+        nightly_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'nightly')
+
+        # compare beta vs nightly
+        ret_code = subprocess.call([python_exe, script_path, '--whitelist',
+                                    whitelist_path, '--no-download',
+                                    platform + ',' + beta_mozconfig_path +
+                                    ',' + nightly_mozconfig_path])
+
+        if ret_code > 0:
+            sys.exit(ret_code)
+
+        # compare release vs nightly
+        ret_code = subprocess.call([python_exe, script_path, '--whitelist',
+                                    whitelist_path, '--no-download',
+                                    platform + ',' + release_mozconfig_path +
+                                    ',' + nightly_mozconfig_path])
new file mode 100644
--- /dev/null
+++ b/build/compare-mozconfig/compare-mozconfigs.py
@@ -0,0 +1,68 @@
+#!/usr/bin/python
+import logging
+import os
+import site
+import sys
+import urllib2
+
+site.addsitedir(os.path.join(os.path.dirname(__file__), "../../lib/python"))
+
+from release.sanity import verify_mozconfigs
+from release.info import readConfig
+from util.hg import make_hg_url
+
+FAILURE_CODE = 1
+SUCCESS_CODE = 0
+
+def get_mozconfig(path, options):
+    """Consumes a path and returns a list of lines from
+    the mozconfig file. If download is required, the path
+    specified should be relative to the root of the hg
+    repository e.g browser/config/mozconfigs/linux32/nightly"""
+    if options.no_download:
+        return open(path, 'r').readlines()
+    else:
+        url = make_hg_url(options.hghost, options.branch, 'http',
+                    options.revision, path)
+        return urllib2.urlopen(url).readlines()
+
+if __name__ == '__main__':
+    from optparse import OptionParser
+    parser = OptionParser()
+
+    parser.add_option('--branch', dest='branch')
+    parser.add_option('--revision', dest='revision')
+    parser.add_option('--hghost', dest='hghost', default='hg.mozilla.org')
+    parser.add_option('--whitelist', dest='whitelist')
+    parser.add_option('--no-download', action='store_true', dest='no_download',
+                      default=False)
+    options, args = parser.parse_args()
+
+    logging.basicConfig(level=logging.INFO)
+
+    missing_args = options.branch is None or options.revision is None
+    if not options.no_download and missing_args:
+        logging.error('Not enough arguments to download mozconfigs')
+        sys.exit(FAILURE_CODE)
+
+    mozconfig_whitelist = readConfig(options.whitelist, ['whitelist'])
+
+    for arg in args:
+        platform, mozconfig_path, nightly_mozconfig_path = arg.split(',')
+
+        mozconfig_lines = get_mozconfig(mozconfig_path, options)
+        nightly_mozconfig_lines = get_mozconfig(nightly_mozconfig_path, options)
+
+        mozconfig_pair = (mozconfig_path, mozconfig_lines)
+        nightly_mozconfig_pair = (nightly_mozconfig_path,
+                                  nightly_mozconfig_lines)
+
+        passed = verify_mozconfigs(mozconfig_pair, nightly_mozconfig_pair,
+                                   platform, mozconfig_whitelist)
+
+        if passed:
+            logging.info('Mozconfig check passed!')
+        else:
+            logging.error('Mozconfig check failed!')
+            sys.exit(FAILURE_CODE)
+    sys.exit(SUCCESS_CODE)
--- a/build/dumbmake-dependencies
+++ b/build/dumbmake-dependencies
@@ -14,18 +14,17 @@ toolkit/library
   widget
   gfx
   toolkit/components/build
     toolkit/components
   security/build
   security/manager
   security/dbm
   security/nss
-  accessible/build
-    accessible
+  accessible
   dom
   content
   layout
   editor
   parser
   js/src
     js/xpconnect
       js/xpconnect/loader
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -126,39 +126,46 @@ def bootstrap(topsrcdir, mozilla_dir=Non
     state_env_dir = os.environ.get('MOZBUILD_STATE_PATH', None)
     if state_env_dir:
         if not os.path.exists(state_env_dir):
             print('Creating global state directory from environment variable: %s'
                 % state_env_dir)
             os.makedirs(state_env_dir, mode=0o770)
             print('Please re-run mach.')
             sys.exit(1)
+        state_dir = state_env_dir
     else:
         if not os.path.exists(state_user_dir):
             print(STATE_DIR_FIRST_RUN.format(userdir=state_user_dir))
             try:
                 for i in range(20, -1, -1):
                     time.sleep(1)
                     sys.stdout.write('%d ' % i)
                     sys.stdout.flush()
             except KeyboardInterrupt:
                 sys.exit(1)
 
             print('\nCreating default state directory: %s' % state_user_dir)
             os.mkdir(state_user_dir)
             print('Please re-run mach.')
             sys.exit(1)
+        state_dir = state_user_dir
 
     try:
         import mach.main
     except ImportError:
         sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS]
         import mach.main
 
+    def populate_context(context):
+        context.state_dir = state_dir
+
     mach = mach.main.Mach(topsrcdir)
+    mach.populate_context_handler = populate_context
+
     for category, meta in CATEGORIES.items():
         mach.define_category(category, meta['short'], meta['long'],
             meta['priority'])
 
     for path in MACH_MODULES:
         mach.load_commands_from_file(os.path.join(mozilla_dir, path))
 
     return mach
new file mode 100644
--- /dev/null
+++ b/build/release/info.py
@@ -0,0 +1,218 @@
+from datetime import datetime
+import os
+from os import path
+import re
+import shutil
+import sys
+from urllib2 import urlopen
+
+from release.paths import makeCandidatesDir
+
+import logging
+log = logging.getLogger(__name__)
+
+# If version has two parts with no trailing specifiers like "rc", we
+# consider it a "final" release for which we only create a _RELEASE tag.
+FINAL_RELEASE_REGEX = "^\d+\.\d+$"
+
+
+class ConfigError(Exception):
+    pass
+
+
+def getBuildID(platform, product, version, buildNumber, nightlyDir='nightly',
+               server='stage.mozilla.org'):
+    infoTxt = makeCandidatesDir(product, version, buildNumber, nightlyDir,
+                                protocol='http', server=server) + \
+        '%s_info.txt' % platform
+    try:
+        buildInfo = urlopen(infoTxt).read()
+    except:
+        log.error("Failed to retrieve %s" % infoTxt)
+        raise
+
+    for line in buildInfo.splitlines():
+        key, value = line.rstrip().split('=', 1)
+        if key == 'buildID':
+            return value
+
+
+def findOldBuildIDs(product, version, buildNumber, platforms,
+                    nightlyDir='nightly', server='stage.mozilla.org'):
+    ids = {}
+    if buildNumber <= 1:
+        return ids
+    for n in range(1, buildNumber):
+        for platform in platforms:
+            if platform not in ids:
+                ids[platform] = []
+            try:
+                id = getBuildID(platform, product, version, n, nightlyDir,
+                                server)
+                ids[platform].append(id)
+            except Exception, e:
+                log.error("Hit exception: %s" % e)
+    return ids
+
+
+def getReleaseConfigName(product, branch, version=None, staging=False):
+    # XXX: Horrible hack for bug 842741. Because Thunderbird release
+    # and esr both build out of esr17 repositories we'll bump the wrong
+    # config for release without this.
+    if product == 'thunderbird' and 'esr17' in branch and version and 'esr' not in version:
+        cfg = 'release-thunderbird-comm-release.py'
+    else:
+        cfg = 'release-%s-%s.py' % (product, branch)
+    if staging:
+        cfg = 'staging_%s' % cfg
+    return cfg
+
+
+def readReleaseConfig(configfile, required=[]):
+    return readConfig(configfile, keys=['releaseConfig'], required=required)
+
+
+def readBranchConfig(dir, localconfig, branch, required=[]):
+    shutil.copy(localconfig, path.join(dir, "localconfig.py"))
+    oldcwd = os.getcwd()
+    os.chdir(dir)
+    sys.path.append(".")
+    try:
+        return readConfig("config.py", keys=['BRANCHES', branch],
+                          required=required)
+    finally:
+        os.chdir(oldcwd)
+        sys.path.remove(".")
+
+
+def readConfig(configfile, keys=[], required=[]):
+    c = {}
+    execfile(configfile, c)
+    for k in keys:
+        c = c[k]
+    items = c.keys()
+    err = False
+    for key in required:
+        if key not in items:
+            err = True
+            log.error("Required item `%s' missing from %s" % (key, c))
+    if err:
+        raise ConfigError("Missing at least one item in config, see above")
+    return c
+
+
+def isFinalRelease(version):
+    return bool(re.match(FINAL_RELEASE_REGEX, version))
+
+
+def getBaseTag(product, version):
+    product = product.upper()
+    version = version.replace('.', '_')
+    return '%s_%s' % (product, version)
+
+
+def getTags(baseTag, buildNumber, buildTag=True):
+    t = ['%s_RELEASE' % baseTag]
+    if buildTag:
+        t.append('%s_BUILD%d' % (baseTag, int(buildNumber)))
+    return t
+
+
+def getRuntimeTag(tag):
+    return "%s_RUNTIME" % tag
+
+
+def getReleaseTag(tag):
+    return "%s_RELEASE" % tag
+
+
+def generateRelbranchName(version, prefix='GECKO'):
+    return '%s%s_%s_RELBRANCH' % (
+        prefix, version.replace('.', ''),
+        datetime.now().strftime('%Y%m%d%H'))
+
+
+def getReleaseName(product, version, buildNumber):
+    return '%s-%s-build%s' % (product.title(), version, str(buildNumber))
+
+
+def getRepoMatchingBranch(branch, sourceRepositories):
+    for sr in sourceRepositories.values():
+        if branch in sr['path']:
+            return sr
+    return None
+
+
+def fileInfo(filepath, product):
+    """Extract information about a release file.  Returns a dictionary with the
+    following keys set:
+    'product', 'version', 'locale', 'platform', 'contents', 'format',
+    'pathstyle'
+
+    'contents' is one of 'complete', 'installer'
+    'format' is one of 'mar' or 'exe'
+    'pathstyle' is either 'short' or 'long', and refers to if files are all in
+        one directory, with the locale as part of the filename ('short' paths,
+        firefox 3.0 style filenames), or if the locale names are part of the
+        directory structure, but not the file name itself ('long' paths,
+        firefox 3.5+ style filenames)
+    """
+    try:
+        # Mozilla 1.9.0 style (aka 'short') paths
+        # e.g. firefox-3.0.12.en-US.win32.complete.mar
+        filename = os.path.basename(filepath)
+        m = re.match("^(%s)-([0-9.]+)\.([-a-zA-Z]+)\.(win32)\.(complete|installer)\.(mar|exe)$" % product, filename)
+        if not m:
+            raise ValueError("Could not parse: %s" % filename)
+        return {'product': m.group(1),
+                'version': m.group(2),
+                'locale': m.group(3),
+                'platform': m.group(4),
+                'contents': m.group(5),
+                'format': m.group(6),
+                'pathstyle': 'short',
+                'leading_path': '',
+                }
+    except:
+        # Mozilla 1.9.1 and on style (aka 'long') paths
+        # e.g. update/win32/en-US/firefox-3.5.1.complete.mar
+        #      win32/en-US/Firefox Setup 3.5.1.exe
+        ret = {'pathstyle': 'long'}
+        if filepath.endswith('.mar'):
+            ret['format'] = 'mar'
+            m = re.search("update/(win32|linux-i686|linux-x86_64|mac|mac64)/([-a-zA-Z]+)/(%s)-(\d+\.\d+(?:\.\d+)?(?:\w+(?:\d+)?)?)\.(complete)\.mar" % product, filepath)
+            if not m:
+                raise ValueError("Could not parse: %s" % filepath)
+            ret['platform'] = m.group(1)
+            ret['locale'] = m.group(2)
+            ret['product'] = m.group(3)
+            ret['version'] = m.group(4)
+            ret['contents'] = m.group(5)
+            ret['leading_path'] = ''
+        elif filepath.endswith('.exe'):
+            ret['format'] = 'exe'
+            ret['contents'] = 'installer'
+            # EUballot builds use a different enough style of path than others
+            # that we can't catch them in the same regexp
+            if filepath.find('win32-EUballot') != -1:
+                ret['platform'] = 'win32'
+                m = re.search("(win32-EUballot/)([-a-zA-Z]+)/((?i)%s) Setup (\d+\.\d+(?:\.\d+)?(?:\w+\d+)?(?:\ \w+\ \d+)?)\.exe" % product, filepath)
+                if not m:
+                    raise ValueError("Could not parse: %s" % filepath)
+                ret['leading_path'] = m.group(1)
+                ret['locale'] = m.group(2)
+                ret['product'] = m.group(3).lower()
+                ret['version'] = m.group(4)
+            else:
+                m = re.search("(partner-repacks/[-a-zA-Z0-9_]+/|)(win32|mac|linux-i686)/([-a-zA-Z]+)/((?i)%s) Setup (\d+\.\d+(?:\.\d+)?(?:\w+(?:\d+)?)?(?:\ \w+\ \d+)?)\.exe" % product, filepath)
+                if not m:
+                    raise ValueError("Could not parse: %s" % filepath)
+                ret['leading_path'] = m.group(1)
+                ret['platform'] = m.group(2)
+                ret['locale'] = m.group(3)
+                ret['product'] = m.group(4).lower()
+                ret['version'] = m.group(5)
+        else:
+            raise ValueError("Unknown filetype for %s" % filepath)
+
+        return ret
new file mode 100644
--- /dev/null
+++ b/build/release/sanity.py
@@ -0,0 +1,124 @@
+import difflib
+import logging
+import re
+import urllib2
+from util.commands import run_cmd, get_output
+from util.hg import get_repo_name, make_hg_url
+from subprocess import CalledProcessError
+
+log = logging.getLogger(__name__)
+
+
+def check_buildbot():
+    """check if buildbot command works"""
+    try:
+        run_cmd(['buildbot', '--version'])
+    except CalledProcessError:
+        log.error("FAIL: buildbot command doesn't work", exc_info=True)
+        raise
+
+
+def find_version(contents, versionNumber):
+    """Given an open readable file-handle look for the occurrence
+       of the version # in the file"""
+    ret = re.search(re.compile(re.escape(versionNumber), re.DOTALL), contents)
+    return ret
+
+
+def locale_diff(locales1, locales2):
+    """ accepts two lists and diffs them both ways, returns any differences
+    found """
+    diff_list = [locale for locale in locales1 if not locale in locales2]
+    diff_list.extend(locale for locale in locales2 if not locale in locales1)
+    return diff_list
+
+
+def get_buildbot_username_param():
+    cmd = ['buildbot', 'sendchange', '--help']
+    output = get_output(cmd)
+    if "-W, --who=" in output:
+        return "--who"
+    else:
+        return "--username"
+
+
+def sendchange(branch, revision, username, master, products):
+    """Send the change to buildbot to kick off the release automation"""
+    if isinstance(products, basestring):
+        products = [products]
+    cmd = [
+        'buildbot',
+        'sendchange',
+        get_buildbot_username_param(),
+        username,
+        '--master',
+        master,
+        '--branch',
+        branch,
+        '-p',
+        'products:%s' % ','.join(products),
+        '-p',
+        'script_repo_revision:%s' % revision,
+        'release_build'
+    ]
+    logging.info("Executing: %s" % cmd)
+    run_cmd(cmd)
+
+
+def verify_mozconfigs(mozconfig_pair, nightly_mozconfig_pair, platform,
+                      mozconfigWhitelist={}):
+    """Compares mozconfig to nightly_mozconfig and compare to an optional
+    whitelist of known differences. mozconfig_pair and nightly_mozconfig_pair
+    are pairs containing the mozconfig's identifier and the list of lines in
+    the mozconfig."""
+
+    # unpack the pairs to get the names, the names are just for
+    # identifying the mozconfigs when logging the error messages
+    mozconfig_name, mozconfig_lines = mozconfig_pair
+    nightly_mozconfig_name, nightly_mozconfig_lines = nightly_mozconfig_pair
+
+    missing_args = mozconfig_lines == [] or nightly_mozconfig_lines == []
+    if missing_args:
+        log.info("Missing mozconfigs to compare for %s" % platform)
+        return False
+
+    success = True
+
+    diffInstance = difflib.Differ()
+    diff_result = diffInstance.compare(mozconfig_lines, nightly_mozconfig_lines)
+    diffList = list(diff_result)
+
+    for line in diffList:
+        clean_line = line[1:].strip()
+        if (line[0] == '-' or line[0] == '+') and len(clean_line) > 1:
+            # skip comment lines
+            if clean_line.startswith('#'):
+                continue
+            # compare to whitelist
+            message = ""
+            if line[0] == '-':
+                if platform in mozconfigWhitelist.get('release', {}):
+                    if clean_line in \
+                            mozconfigWhitelist['release'][platform]:
+                        continue
+            elif line[0] == '+':
+                if platform in mozconfigWhitelist.get('nightly', {}):
+                    if clean_line in \
+                            mozconfigWhitelist['nightly'][platform]:
+                        continue
+                    else:
+                        log.warning("%s not in %s %s!" % (
+                            clean_line, platform,
+                            mozconfigWhitelist['nightly'][platform]))
+            else:
+                log.error("Skipping line %s!" % line)
+                continue
+            message = "found in %s but not in %s: %s"
+            if line[0] == '-':
+                log.error(message % (mozconfig_name,
+                                     nightly_mozconfig_name, clean_line))
+            else:
+                log.error(message % (nightly_mozconfig_name,
+                                     mozconfig_name, clean_line))
+            success = False
+    return success
new file mode 100644
--- /dev/null
+++ b/build/util/hg.py
@@ -0,0 +1,611 @@
+"""Functions for interacting with hg"""
+import os
+import re
+import subprocess
+from urlparse import urlsplit
+from ConfigParser import RawConfigParser
+
+from util.commands import run_cmd, get_output, remove_path
+from util.retry import retry
+
+import logging
+log = logging.getLogger(__name__)
+
+
+class DefaultShareBase:
+    pass
+DefaultShareBase = DefaultShareBase()
+
+
+class HgUtilError(Exception):
+    pass
+
+
+def _make_absolute(repo):
+    if repo.startswith("file://"):
+        path = repo[len("file://"):]
+        repo = "file://%s" % os.path.abspath(path)
+    elif "://" not in repo:
+        repo = os.path.abspath(repo)
+    return repo
+
+
+def make_hg_url(hgHost, repoPath, protocol='https', revision=None,
+                filename=None):
+    """construct a valid hg url from a base hg url (hg.mozilla.org),
+    repoPath, revision and possible filename"""
+    base = '%s://%s' % (protocol, hgHost)
+    repo = '/'.join(p.strip('/') for p in [base, repoPath])
+    if not filename:
+        if not revision:
+            return repo
+        else:
+            return '/'.join([p.strip('/') for p in [repo, 'rev', revision]])
+    else:
+        assert revision
+        return '/'.join([p.strip('/') for p in [repo, 'raw-file', revision, filename]])
+
+
+def get_repo_name(repo):
+    return repo.rstrip('/').split('/')[-1]
+
+
+def get_repo_path(repo):
+    repo = _make_absolute(repo)
+    if repo.startswith("/"):
+        return repo.lstrip("/")
+    else:
+        return urlsplit(repo).path.lstrip("/")
+
+
+def get_revision(path):
+    """Returns which revision directory `path` currently has checked out."""
+    return get_output(['hg', 'parent', '--template', '{node|short}'], cwd=path)
+
+
+def get_branch(path):
+    return get_output(['hg', 'branch'], cwd=path).strip()
+
+
+def get_branches(path):
+    branches = []
+    for line in get_output(['hg', 'branches', '-c'], cwd=path).splitlines():
+        branches.append(line.split()[0])
+    return branches
+
+
+def is_hg_cset(rev):
+    """Retruns True if passed revision represents a valid HG revision
+    (long or short(er) 40 bit hex)"""
+    try:
+        int(rev, 16)
+        return True
+    except (TypeError, ValueError):
+        return False
+
+
+def hg_ver():
+    """Returns the current version of hg, as a tuple of
+    (major, minor, build)"""
+    ver_string = get_output(['hg', '-q', 'version'])
+    match = re.search("\(version ([0-9.]+)\)", ver_string)
+    if match:
+        bits = match.group(1).split(".")
+        if len(bits) < 3:
+            bits += (0,)
+        ver = tuple(int(b) for b in bits)
+    else:
+        ver = (0, 0, 0)
+    log.debug("Running hg version %s", ver)
+    return ver
+
+
+def purge(dest):
+    """Purge the repository of all untracked and ignored files."""
+    try:
+        run_cmd(['hg', '--config', 'extensions.purge=', 'purge',
+                 '-a', '--all', dest], cwd=dest)
+    except subprocess.CalledProcessError, e:
+        log.debug('purge failed: %s' % e)
+        raise
+
+
+def update(dest, branch=None, revision=None):
+    """Updates working copy `dest` to `branch` or `revision`.  If neither is
+    set then the working copy will be updated to the latest revision on the
+    current branch.  Local changes will be discarded."""
+    # If we have a revision, switch to that
+    if revision is not None:
+        cmd = ['hg', 'update', '-C', '-r', revision]
+        run_cmd(cmd, cwd=dest)
+    else:
+        # Check & switch branch
+        local_branch = get_output(['hg', 'branch'], cwd=dest).strip()
+
+        cmd = ['hg', 'update', '-C']
+
+        # If this is different, checkout the other branch
+        if branch and branch != local_branch:
+            cmd.append(branch)
+
+        run_cmd(cmd, cwd=dest)
+    return get_revision(dest)
+
+
+def clone(repo, dest, branch=None, revision=None, update_dest=True,
+          clone_by_rev=False, mirrors=None, bundles=None):
+    """Clones hg repo and places it at `dest`, replacing whatever else is
+    there.  The working copy will be empty.
+
+    If `revision` is set, only the specified revision and its ancestors will
+    be cloned.
+
+    If `update_dest` is set, then `dest` will be updated to `revision` if
+    set, otherwise to `branch`, otherwise to the head of default.
+
+    If `mirrors` is set, will try and clone from the mirrors before
+    cloning from `repo`.
+
+    If `bundles` is set, will try and download the bundle first and
+    unbundle it. If successful, will pull in new revisions from mirrors or
+    the master repo. If unbundling fails, will fall back to doing a regular
+    clone from mirrors or the master repo.
+
+    Regardless of how the repository ends up being cloned, the 'default' path
+    will point to `repo`.
+    """
+    if os.path.exists(dest):
+        remove_path(dest)
+
+    if bundles:
+        log.info("Attempting to initialize clone with bundles")
+        for bundle in bundles:
+            if os.path.exists(dest):
+                remove_path(dest)
+            init(dest)
+            log.info("Trying to use bundle %s", bundle)
+            try:
+                if not unbundle(bundle, dest):
+                    remove_path(dest)
+                    continue
+                adjust_paths(dest, default=repo)
+                # Now pull / update
+                return pull(repo, dest, update_dest=update_dest,
+                            mirrors=mirrors, revision=revision, branch=branch)
+            except Exception:
+                remove_path(dest)
+                log.exception("Problem unbundling/pulling from %s", bundle)
+                continue
+        else:
+            log.info("Using bundles failed; falling back to clone")
+
+    if mirrors:
+        log.info("Attempting to clone from mirrors")
+        for mirror in mirrors:
+            log.info("Cloning from %s", mirror)
+            try:
+                retval = clone(mirror, dest, branch, revision,
+                               update_dest=update_dest, clone_by_rev=clone_by_rev)
+                adjust_paths(dest, default=repo)
+                return retval
+            except:
+                log.exception("Problem cloning from mirror %s", mirror)
+                continue
+        else:
+            log.info("Pulling from mirrors failed; falling back to %s", repo)
+            # We may have a partial repo here; mercurial() copes with that
+            # We need to make sure our paths are correct though
+            if os.path.exists(os.path.join(dest, '.hg')):
+                adjust_paths(dest, default=repo)
+            return mercurial(repo, dest, branch, revision, autoPurge=True,
+                             update_dest=update_dest, clone_by_rev=clone_by_rev)
+
+    cmd = ['hg', 'clone']
+    if not update_dest:
+        cmd.append('-U')
+
+    if clone_by_rev:
+        if revision:
+            cmd.extend(['-r', revision])
+        elif branch:
+            # hg >= 1.6 supports -b branch for cloning
+            ver = hg_ver()
+            if ver >= (1, 6, 0):
+                cmd.extend(['-b', branch])
+
+    cmd.extend([repo, dest])
+    run_cmd(cmd)
+
+    if update_dest:
+        return update(dest, branch, revision)
+
+
+def common_args(revision=None, branch=None, ssh_username=None, ssh_key=None):
+    """Fill in common hg arguments, encapsulating logic checks that depend on
+       mercurial versions and provided arguments"""
+    args = []
+    if ssh_username or ssh_key:
+        opt = ['-e', 'ssh']
+        if ssh_username:
+            opt[1] += ' -l %s' % ssh_username
+        if ssh_key:
+            opt[1] += ' -i %s' % ssh_key
+        args.extend(opt)
+    if revision:
+        args.extend(['-r', revision])
+    elif branch:
+        if hg_ver() >= (1, 6, 0):
+            args.extend(['-b', branch])
+    return args
+
+
+def pull(repo, dest, update_dest=True, mirrors=None, **kwargs):
+    """Pulls changes from hg repo and places it in `dest`.
+
+    If `update_dest` is set, then `dest` will be updated to `revision` if
+    set, otherwise to `branch`, otherwise to the head of default.
+
+    If `mirrors` is set, will try and pull from the mirrors first before
+    `repo`."""
+
+    if mirrors:
+        for mirror in mirrors:
+            try:
+                return pull(mirror, dest, update_dest=update_dest, **kwargs)
+            except:
+                log.exception("Problem pulling from mirror %s", mirror)
+                continue
+        else:
+            log.info("Pulling from mirrors failed; falling back to %s", repo)
+
+    # Convert repo to an absolute path if it's a local repository
+    repo = _make_absolute(repo)
+    cmd = ['hg', 'pull']
+    # Don't pass -r to "hg pull", except when it's a valid HG revision.
+    # Pulling using tag names is dangerous: it uses the local .hgtags, so if
+    # the tag has moved on the remote side you won't pull the new revision the
+    # remote tag refers to.
+    pull_kwargs = kwargs.copy()
+    if 'revision' in pull_kwargs and \
+       not is_hg_cset(pull_kwargs['revision']):
+        del pull_kwargs['revision']
+
+    cmd.extend(common_args(**pull_kwargs))
+
+    cmd.append(repo)
+    run_cmd(cmd, cwd=dest)
+
+    if update_dest:
+        branch = None
+        if 'branch' in kwargs and kwargs['branch']:
+            branch = kwargs['branch']
+        revision = None
+        if 'revision' in kwargs and kwargs['revision']:
+            revision = kwargs['revision']
+        return update(dest, branch=branch, revision=revision)
+
+# Defines the places of attributes in the tuples returned by `out'
+REVISION, BRANCH = 0, 1
+
+
+def out(src, remote, **kwargs):
+    """Check for outgoing changesets present in a repo"""
+    cmd = ['hg', '-q', 'out', '--template', '{node} {branches}\n']
+    cmd.extend(common_args(**kwargs))
+    cmd.append(remote)
+    if os.path.exists(src):
+        try:
+            revs = []
+            for line in get_output(cmd, cwd=src).rstrip().split("\n"):
+                try:
+                    rev, branch = line.split()
+                # Mercurial displays no branch at all if the revision is on
+                # "default"
+                except ValueError:
+                    rev = line.rstrip()
+                    branch = "default"
+                revs.append((rev, branch))
+            return revs
+        except subprocess.CalledProcessError, inst:
+            # In some situations, some versions of Mercurial return "1"
+            # if no changes are found, so we need to ignore this return code
+            if inst.returncode == 1:
+                return []
+            raise
+
+
+def push(src, remote, push_new_branches=True, force=False, **kwargs):
+    cmd = ['hg', 'push']
+    cmd.extend(common_args(**kwargs))
+    if force:
+        cmd.append('-f')
+    if push_new_branches:
+        cmd.append('--new-branch')
+    cmd.append(remote)
+    run_cmd(cmd, cwd=src)
+
+
+def mercurial(repo, dest, branch=None, revision=None, update_dest=True,
+              shareBase=DefaultShareBase, allowUnsharedLocalClones=False,
+              clone_by_rev=False, mirrors=None, bundles=None, autoPurge=False):
+    """Makes sure that `dest` is has `revision` or `branch` checked out from
+    `repo`.
+
+    Do what it takes to make that happen, including possibly clobbering
+    dest.
+
+    If allowUnsharedLocalClones is True and we're trying to use the share
+    extension but fail, then we will be able to clone from the shared repo to
+    our destination.  If this is False, the default, then if we don't have the
+    share extension we will just clone from the remote repository.
+
+    If `clone_by_rev` is True, use 'hg clone -r <rev>' instead of 'hg clone'.
+    This is slower, but useful when cloning repos with lots of heads.
+
+    If `mirrors` is set, will try and use the mirrors before `repo`.
+
+    If `bundles` is set, will try and download the bundle first and
+    unbundle it instead of doing a full clone. If successful, will pull in
+    new revisions from mirrors or the master repo. If unbundling fails, will
+    fall back to doing a regular clone from mirrors or the master repo.
+    """
+    dest = os.path.abspath(dest)
+    if shareBase is DefaultShareBase:
+        shareBase = os.environ.get("HG_SHARE_BASE_DIR", None)
+
+    log.info("Reporting hg version in use")
+    cmd = ['hg', '-q', 'version']
+    run_cmd(cmd, cwd='.')
+
+    if shareBase:
+        # Check that 'hg share' works
+        try:
+            log.info("Checking if share extension works")
+            output = get_output(['hg', 'help', 'share'], dont_log=True)
+            if 'no commands defined' in output:
+                # Share extension is enabled, but not functional
+                log.info("Disabling sharing since share extension doesn't seem to work (1)")
+                shareBase = None
+            elif 'unknown command' in output:
+                # Share extension is disabled
+                log.info("Disabling sharing since share extension doesn't seem to work (2)")
+                shareBase = None
+        except subprocess.CalledProcessError:
+            # The command failed, so disable sharing
+            log.info("Disabling sharing since share extension doesn't seem to work (3)")
+            shareBase = None
+
+    # Check that our default path is correct
+    if os.path.exists(os.path.join(dest, '.hg')):
+        hgpath = path(dest, "default")
+
+        # Make sure that our default path is correct
+        if hgpath != _make_absolute(repo):
+            log.info("hg path isn't correct (%s should be %s); clobbering",
+                     hgpath, _make_absolute(repo))
+            remove_path(dest)
+
+    # If the working directory already exists and isn't using share we update
+    # the working directory directly from the repo, ignoring the sharing
+    # settings
+    if os.path.exists(dest):
+        if not os.path.exists(os.path.join(dest, ".hg")):
+            log.warning("%s doesn't appear to be a valid hg directory; clobbering", dest)
+            remove_path(dest)
+        elif not os.path.exists(os.path.join(dest, ".hg", "sharedpath")):
+            try:
+                if autoPurge:
+                    purge(dest)
+                return pull(repo, dest, update_dest=update_dest, branch=branch,
+                            revision=revision,
+                            mirrors=mirrors)
+            except subprocess.CalledProcessError:
+                log.warning("Error pulling changes into %s from %s; clobbering", dest, repo)
+                log.debug("Exception:", exc_info=True)
+                remove_path(dest)
+
+    # If that fails for any reason, and sharing is requested, we'll try to
+    # update the shared repository, and then update the working directory from
+    # that.
+    if shareBase:
+        sharedRepo = os.path.join(shareBase, get_repo_path(repo))
+        dest_sharedPath = os.path.join(dest, '.hg', 'sharedpath')
+
+        if os.path.exists(sharedRepo):
+            hgpath = path(sharedRepo, "default")
+
+            # Make sure that our default path is correct
+            if hgpath != _make_absolute(repo):
+                log.info("hg path isn't correct (%s should be %s); clobbering",
+                         hgpath, _make_absolute(repo))
+                # we need to clobber both the shared checkout and the dest,
+                # since hgrc needs to be in both places
+                remove_path(sharedRepo)
+                remove_path(dest)
+
+        if os.path.exists(dest_sharedPath):
+            # Make sure that the sharedpath points to sharedRepo
+            dest_sharedPath_data = os.path.normpath(
+                open(dest_sharedPath).read())
+            norm_sharedRepo = os.path.normpath(os.path.join(sharedRepo, '.hg'))
+            if dest_sharedPath_data != norm_sharedRepo:
+                # Clobber!
+                log.info("We're currently shared from %s, but are being requested to pull from %s (%s); clobbering",
+                         dest_sharedPath_data, repo, norm_sharedRepo)
+                remove_path(dest)
+
+        try:
+            log.info("Updating shared repo")
+            mercurial(repo, sharedRepo, branch=branch, revision=revision,
+                      update_dest=False, shareBase=None, clone_by_rev=clone_by_rev,
+                      mirrors=mirrors, bundles=bundles, autoPurge=False)
+            if os.path.exists(dest):
+                if autoPurge:
+                    purge(dest)
+                return update(dest, branch=branch, revision=revision)
+
+            try:
+                log.info("Trying to share %s to %s", sharedRepo, dest)
+                return share(sharedRepo, dest, branch=branch, revision=revision)
+            except subprocess.CalledProcessError:
+                if not allowUnsharedLocalClones:
+                    # Re-raise the exception so it gets caught below.
+                    # We'll then clobber dest, and clone from original repo
+                    raise
+
+                log.warning("Error calling hg share from %s to %s;"
+                            "falling back to normal clone from shared repo",
+                            sharedRepo, dest)
+                # Do a full local clone first, and then update to the
+                # revision we want
+                # This lets us use hardlinks for the local clone if the OS
+                # supports it
+                clone(sharedRepo, dest, update_dest=False,
+                      mirrors=mirrors, bundles=bundles)
+                return update(dest, branch=branch, revision=revision)
+        except subprocess.CalledProcessError:
+            log.warning(
+                "Error updating %s from sharedRepo (%s): ", dest, sharedRepo)
+            log.debug("Exception:", exc_info=True)
+            remove_path(dest)
+    # end if shareBase
+
+    if not os.path.exists(os.path.dirname(dest)):
+        os.makedirs(os.path.dirname(dest))
+
+    # Share isn't available or has failed, clone directly from the source
+    return clone(repo, dest, branch, revision,
+                 update_dest=update_dest, mirrors=mirrors,
+                 bundles=bundles, clone_by_rev=clone_by_rev)
+
+
+def apply_and_push(localrepo, remote, changer, max_attempts=10,
+                   ssh_username=None, ssh_key=None, force=False):
+    """This function calls `changer' to make changes to the repo, and tries
+       its hardest to get them to the origin repo. `changer' must be a
+       callable object that receives two arguments: the directory of the local
+       repository, and the attempt number. This function will push ALL
+       changesets missing from remote."""
+    assert callable(changer)
+    branch = get_branch(localrepo)
+    changer(localrepo, 1)
+    for n in range(1, max_attempts + 1):
+        new_revs = []
+        try:
+            new_revs = out(src=localrepo, remote=remote,
+                           ssh_username=ssh_username,
+                           ssh_key=ssh_key)
+            if len(new_revs) < 1:
+                raise HgUtilError("No revs to push")
+            push(src=localrepo, remote=remote, ssh_username=ssh_username,
+                 ssh_key=ssh_key, force=force)
+            return
+        except subprocess.CalledProcessError, e:
+            log.debug("Hit error when trying to push: %s" % str(e))
+            if n == max_attempts:
+                log.debug("Tried %d times, giving up" % max_attempts)
+                for r in reversed(new_revs):
+                    run_cmd(['hg', '--config', 'extensions.mq=', 'strip', '-n',
+                             r[REVISION]], cwd=localrepo)
+                raise HgUtilError("Failed to push")
+            pull(remote, localrepo, update_dest=False,
+                 ssh_username=ssh_username, ssh_key=ssh_key)
+            # After we successfully rebase or strip away heads the push is
+            # is attempted again at the start of the loop
+            try:
+                run_cmd(['hg', '--config', 'ui.merge=internal:merge',
+                         'rebase'], cwd=localrepo)
+            except subprocess.CalledProcessError, e:
+                log.debug("Failed to rebase: %s" % str(e))
+                update(localrepo, branch=branch)
+                for r in reversed(new_revs):
+                    run_cmd(['hg', '--config', 'extensions.mq=', 'strip', '-n',
+                             r[REVISION]], cwd=localrepo)
+                changer(localrepo, n + 1)
+
+
+def share(source, dest, branch=None, revision=None):
+    """Creates a new working directory in "dest" that shares history with
+       "source" using Mercurial's share extension"""
+    run_cmd(['hg', 'share', '-U', source, dest])
+    return update(dest, branch=branch, revision=revision)
+
+
+def cleanOutgoingRevs(reponame, remote, username, sshKey):
+    outgoingRevs = retry(out, kwargs=dict(src=reponame, remote=remote,
+                                          ssh_username=username,
+                                          ssh_key=sshKey))
+    for r in reversed(outgoingRevs):
+        run_cmd(['hg', '--config', 'extensions.mq=', 'strip', '-n',
+                 r[REVISION]], cwd=reponame)
+
+
+def path(src, name='default'):
+    """Returns the remote path associated with "name" """
+    try:
+        return get_output(['hg', 'path', name], cwd=src).strip()
+    except subprocess.CalledProcessError:
+        return None
+
+
+def init(dest):
+    """Initializes an empty repo in `dest`"""
+    run_cmd(['hg', 'init', dest])
+
+
+def unbundle(bundle, dest):
+    """Unbundles the bundle located at `bundle` into `dest`.
+
+    `bundle` can be a local file or remote url."""
+    try:
+        get_output(['hg', 'unbundle', bundle], cwd=dest, include_stderr=True)
+        return True
+    except subprocess.CalledProcessError:
+        return False
+
+
+def adjust_paths(dest, **paths):
+    """Adjusts paths in `dest`/.hg/hgrc so that names in `paths` are set to
+    paths[name].
+
+    Note that any comments in the hgrc will be lost if changes are made to the
+    file."""
+    hgrc = os.path.join(dest, '.hg', 'hgrc')
+    config = RawConfigParser()
+    config.read(hgrc)
+
+    if not config.has_section('paths'):
+        config.add_section('paths')
+
+    changed = False
+    for path_name, path_value in paths.items():
+        if (not config.has_option('paths', path_name) or
+                config.get('paths', path_name) != path_value):
+            changed = True
+            config.set('paths', path_name, path_value)
+
+    if changed:
+        config.write(open(hgrc, 'w'))
+
+
+def commit(dest, msg, user=None):
+    cmd = ['hg', 'commit', '-m', msg]
+    if user:
+        cmd.extend(['-u', user])
+    run_cmd(cmd, cwd=dest)
+    return get_revision(dest)
+
+
+def tag(dest, tags, user=None, msg=None, rev=None, force=None):
+    cmd = ['hg', 'tag']
+    if user:
+        cmd.extend(['-u', user])
+    if msg:
+        cmd.extend(['-m', msg])
+    if rev:
+        cmd.extend(['-r', rev])
+    if force:
+        cmd.append('-f')
+    cmd.extend(tags)
+    run_cmd(cmd, cwd=dest)
+    return get_revision(dest)
--- a/caps/include/nsScriptSecurityManager.h
+++ b/caps/include/nsScriptSecurityManager.h
@@ -425,30 +425,16 @@ private:
                             nsIPrincipal** result);
 
     // Returns null if a principal cannot be found.  Note that rv can be NS_OK
     // when this happens -- this means that there was no script for the
     // context.  Callers MUST pass in a non-null rv here.
     nsIPrincipal*
     GetSubjectPrincipal(JSContext* cx, nsresult* rv);
 
-    // Returns null if a principal cannot be found.  Note that rv can be NS_OK
-    // when this happens -- this means that there was no script.  Callers MUST
-    // pass in a non-null rv here.
-    static nsIPrincipal*
-    GetScriptPrincipal(JSScript* script, nsresult* rv);
-
-    // Returns null if a principal cannot be found.  Note that rv can be NS_OK
-    // when this happens -- this means that there was no script associated
-    // with the function object, and no global object associated with the scope
-    // of obj (the last object on its parent chain). Callers MUST pass in a
-    // non-null rv here.
-    static nsIPrincipal*
-    GetFunctionObjectPrincipal(JSContext* cx, JS::Handle<JSObject*> obj, nsresult* rv);
-
     /**
      * Check capability levels for an |aObj| that implements
      * nsISecurityCheckedComponent.
      *
      * NB: This function also checks to see if aObj is a plugin and the user
      * has set the "security.xpconnect.plugin.unrestricted" pref to allow
      * anybody to script plugin objects from anywhere.
      *
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -1579,35 +1579,17 @@ nsScriptSecurityManager::CheckLoadURIStr
 
 NS_IMETHODIMP
 nsScriptSecurityManager::CheckFunctionAccess(JSContext *aCx, void *aFunObj,
                                              void *aTargetObj)
 {
     // This check is called for event handlers
     nsresult rv;
     JS::Rooted<JSObject*> rootedFunObj(aCx, static_cast<JSObject*>(aFunObj));
-    nsIPrincipal* subject =
-        GetFunctionObjectPrincipal(aCx, rootedFunObj, &rv);
-
-    // If subject is null, get a principal from the function object's scope.
-    if (NS_SUCCEEDED(rv) && !subject)
-    {
-#ifdef DEBUG
-        {
-            JS_ASSERT(JS_ObjectIsFunction(aCx, rootedFunObj));
-            JS::Rooted<JSFunction*> fun(aCx, JS_GetObjectFunction(rootedFunObj));
-            JSScript *script = JS_GetFunctionScript(aCx, fun);
-
-            NS_ASSERTION(!script, "Null principal for non-native function!");
-        }
-#endif
-
-        subject = doGetObjectPrincipal(rootedFunObj);
-    }
-
+    nsIPrincipal* subject = doGetObjectPrincipal(rootedFunObj);
     if (!subject)
         return NS_ERROR_FAILURE;
 
     if (subject == mSystemPrincipal)
         // This is the system principal: just allow access
         return NS_OK;
 
     // Check if the principal the function was compiled under is
@@ -1924,87 +1906,16 @@ nsScriptSecurityManager::GetCodebasePrin
     rv = CreateCodebasePrincipal(aURI, aAppId, aInMozBrowser,
                                  getter_AddRefs(principal));
     NS_ENSURE_SUCCESS(rv, rv);
     NS_IF_ADDREF(*result = principal);
 
     return NS_OK;
 }
 
-// static
-nsIPrincipal*
-nsScriptSecurityManager::GetScriptPrincipal(JSScript *script,
-                                            nsresult* rv)
-{
-    NS_PRECONDITION(rv, "Null out param");
-    *rv = NS_OK;
-    if (!script)
-    {
-        return nullptr;
-    }
-    JSPrincipals *jsp = JS_GetScriptPrincipals(script);
-    if (!jsp) {
-        *rv = NS_ERROR_FAILURE;
-        NS_ERROR("Script compiled without principals!");
-        return nullptr;
-    }
-    return nsJSPrincipals::get(jsp);
-}
-
-// static
-nsIPrincipal*
-nsScriptSecurityManager::GetFunctionObjectPrincipal(JSContext *cx,
-                                                    JS::Handle<JSObject*> obj,
-                                                    nsresult *rv)
-{
-    NS_PRECONDITION(rv, "Null out param");
-
-    *rv = NS_OK;
-
-    if (!JS_ObjectIsFunction(cx, obj))
-    {
-        // Protect against pseudo-functions (like SJOWs).
-        nsIPrincipal *result = doGetObjectPrincipal(obj);
-        if (!result)
-            *rv = NS_ERROR_FAILURE;
-        return result;
-    }
-
-    JS::Rooted<JSFunction*> fun(cx, JS_GetObjectFunction(obj));
-    JSScript *script = JS_GetFunctionScript(cx, fun);
-
-    if (!script)
-    {
-        // A native function: skip it in order to find its scripted caller.
-        return nullptr;
-    }
-
-    if (!js::IsOriginalScriptFunction(fun))
-    {
-        // Here, obj is a cloned function object.  In this case, the
-        // clone's prototype may have been precompiled from brutally
-        // shared chrome, or else it is a lambda or nested function.
-        // The general case here is a function compiled against a
-        // different scope than the one it is parented by at runtime,
-        // hence the creation of a clone to carry the correct scope
-        // chain linkage.
-        //
-        // Since principals follow scope, we must get the object
-        // principal from the clone's scope chain. There are no
-        // reliable principals compiled into the function itself.
-
-        nsIPrincipal *result = doGetObjectPrincipal(obj);
-        if (!result)
-            *rv = NS_ERROR_FAILURE;
-        return result;
-    }
-
-    return GetScriptPrincipal(script, rv);
-}
-
 nsIPrincipal*
 nsScriptSecurityManager::GetSubjectPrincipal(JSContext *cx,
                                              nsresult* rv)
 {
     *rv = NS_OK;
     JSCompartment *compartment = js::GetContextCompartment(cx);
 
     // The context should always be in a compartment, either one it has entered
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -2111,16 +2111,20 @@ public:
     mStyleSheetChangeEventsEnabled = aValue;
   }
 
   bool StyleSheetChangeEventsEnabled() const
   {
     return mStyleSheetChangeEventsEnabled;
   }
 
+  void ObsoleteSheet(nsIURI *aSheetURI, mozilla::ErrorResult& rv);
+
+  void ObsoleteSheet(const nsAString& aSheetURI, mozilla::ErrorResult& rv);
+
   virtual nsHTMLDocument* AsHTMLDocument() { return nullptr; }
 
   virtual JSObject* WrapObject(JSContext *aCx,
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
 private:
   uint64_t mWarnedAbout;
 
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -9444,16 +9444,40 @@ nsIDocument::CaretPositionFromPoint(floa
 NS_IMETHODIMP
 nsDocument::CaretPositionFromPoint(float aX, float aY, nsISupports** aCaretPos)
 {
   NS_ENSURE_ARG_POINTER(aCaretPos);
   *aCaretPos = nsIDocument::CaretPositionFromPoint(aX, aY).get();
   return NS_OK;
 }
 
+void
+nsIDocument::ObsoleteSheet(nsIURI *aSheetURI, ErrorResult& rv)
+{
+  nsresult res = CSSLoader()->ObsoleteSheet(aSheetURI);
+  if (NS_FAILED(res)) {
+    rv.Throw(res);
+  }
+}
+
+void
+nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv)
+{
+  nsCOMPtr<nsIURI> uri;
+  nsresult res = NS_NewURI(getter_AddRefs(uri), aSheetURI);
+  if (NS_FAILED(res)) {
+    rv.Throw(res);
+    return;
+  }
+  res = CSSLoader()->ObsoleteSheet(uri);
+  if (NS_FAILED(res)) {
+    rv.Throw(res);
+  }
+}
+
 namespace mozilla {
 
 // Singleton class to manage the list of fullscreen documents which are the
 // root of a branch which contains fullscreen documents. We maintain this list
 // so that we can easily exit all windows from fullscreen when the user
 // presses the escape key.
 class FullscreenRoots {
 public:
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -631,16 +631,18 @@ MOCHITEST_FILES_C= \
 		test_bug869002.html \
 		test_bug876282.html \
 		test_CSP_bug885433.html \
 		file_CSP_bug885433_allows.html \
 		file_CSP_bug885433_allows.html^headers^ \
 		file_CSP_bug885433_blocks.html \
 		file_CSP_bug885433_blocks.html^headers^ \
 		test_bug890580.html \
+		test_declare_stylesheet_obsolete.html \
+		variable_style_sheet.sjs \
 		$(NULL)
 
 # OOP tests don't work on Windows (bug 763081) or native-fennec
 # (see Bug 774939)
 ifneq ($(OS_ARCH),WINNT)
 ifndef MOZ_ANDROID_OMTC
 MOCHITEST_FILES_B += \
 		test_messagemanager_assertpermission.html \
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_declare_stylesheet_obsolete.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=713564
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 713564</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <!-- Load the variable stylesheet, which changes the color of #content. -->
+  <link rel="stylesheet" type="text/css" href="variable_style_sheet.sjs"/>
+
+  <script type="application/javascript">
+
+  function insertLinkToVarSSAndRun(callback) {
+    var ss = document.createElement("link");
+    ss.rel = "stylesheet";
+    ss.type = "text/css";
+    ss.href = "variable_style_sheet.sjs";
+    document.getElementsByTagName("head")[0].appendChild(ss);
+    ss.addEventListener("load", callback);
+  }
+
+  /** Test for Bug 713564 **/
+
+  // Then you link to that sheet, remove the link from the DOM, insert a new link to
+  // the same url and check that there was no new access, then call our new method,
+  // insert _another_ <link> to the same url, and check that this time we hit the
+  // server.
+  SimpleTest.waitForExplicitFinish();
+
+  function do_test() {
+    var var_sheet = document.getElementsByTagName("link")[1];
+    var head = document.getElementsByTagName("head")[0];
+    var content = document.getElementById("content");
+    var var_sheet_url = var_sheet.href;
+
+    var previousBgColor = window.getComputedStyle(content).
+                                 getPropertyValue("background-color");
+    var_sheet.parentNode.removeChild(var_sheet);
+    insertLinkToVarSSAndRun(function() {
+      is(window.getComputedStyle(content).getPropertyValue("background-color"),
+         previousBgColor,
+         "Sheet should still be the same.");
+
+      // Obsolete sheet
+      try {
+        SpecialPowers.wrap(document).obsoleteSheet(var_sheet_url);
+      } catch (e) {
+        ok(false, "obsoleteSheet should not raise an error on valid URL.");
+      }
+      insertLinkToVarSSAndRun(function() {
+        isnot(window.getComputedStyle(content).getPropertyValue("background-color"),
+              previousBgColor,
+              "Sheet should change after obsoleted and reinserted.");
+        SimpleTest.finish();
+      });
+    });
+    // obsoleteSheet should throw with invalid input:
+    try {
+      SpecialPowers.wrap(document).obsoleteSheet("");
+      ok(false, "obsoleteSheet should throw with empty string.");
+    } catch (e) {
+      ok(true, "obsoleteSheet throws with empty string.");
+    }
+    try {
+      SpecialPowers.wrap(document).obsoleteSheet("foo");
+      ok(false, "obsoleteSheet should throw with invalid URL.");
+    } catch (e) {
+      ok(true, "obsoleteSheet throws with invalid URL.");
+    }
+    try {
+      SpecialPowers.wrap(document).obsoleteSheet("http://www.mozilla.org");
+      ok(true, "obsoleteSheet should not throw with valid URL.");
+    } catch (e) {
+      ok(false, "obsoleteSheet throws with valid URL.");
+    }
+  }
+
+  </script>
+</head>
+<body onload="do_test();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=713564">Mozilla Bug 713564</a>
+<p id="display"></p>
+<div id="content">
+  <br>
+  <br>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/variable_style_sheet.sjs
@@ -0,0 +1,19 @@
+function handleRequest(request, response)
+{
+  response.setHeader("Cache-Control", "no-cache", false);
+  response.setHeader("Content-Type", "text/css", false);
+
+  var accessCount;
+  var state = getState("varSSAccessCount");
+  if (!state) {
+    setState("varSSAccessCount", "0");
+    accessCount = 0;
+  } else {
+    setState("varSSAccessCount", (parseInt(state) + 1).toString());
+    accessCount = parseInt(state) + 1;
+  }
+
+  response.write("#content { background-color: rgb(" +
+                 (accessCount % 256) +
+                 ", 0, 0); }");
+}
--- a/content/html/content/src/HTMLPropertiesCollection.cpp
+++ b/content/html/content/src/HTMLPropertiesCollection.cpp
@@ -9,19 +9,16 @@
 #include "nsContentUtils.h"
 #include "nsGenericHTMLElement.h"
 #include "nsVariant.h"
 #include "nsDOMSettableTokenList.h"
 #include "nsAttrValue.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/dom/HTMLPropertiesCollectionBinding.h"
 
-DOMCI_DATA(HTMLPropertiesCollection, mozilla::dom::HTMLPropertiesCollection)
-DOMCI_DATA(PropertyNodeList, mozilla::dom::PropertyNodeList)
-
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLPropertiesCollection)
   // SetDocument(nullptr) ensures that we remove ourselves as a mutation observer
   tmp->SetDocument(nullptr);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNames)
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -352,22 +352,45 @@ nsXBLBinding::GenerateAnonymousContent()
                             mPrototypeBinding->ChromeOnlyContent());
 
     // Insert explicit children into insertion points
     if (mDefaultInsertionPoint && mInsertionPoints.IsEmpty()) {
       ExplicitChildIterator iter(mBoundElement);
       for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
         mDefaultInsertionPoint->AppendInsertedChild(child, bindingManager);
       }
-    } else if (!mInsertionPoints.IsEmpty()) {
+    } else {
+      // It is odd to come into this code if mInsertionPoints is not empty, but
+      // we need to make sure to do the compatibility hack below if the bound
+      // node has any non <xul:template> or <xul:observer> children.
       ExplicitChildIterator iter(mBoundElement);
       for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
         XBLChildrenElement* point = FindInsertionPointForInternal(child);
         if (point) {
           point->AppendInsertedChild(child, bindingManager);
+        } else {
+          nsINodeInfo *ni = child->NodeInfo();
+          if (ni->NamespaceID() != kNameSpaceID_XUL ||
+              (!ni->Equals(nsGkAtoms::_template) &&
+               !ni->Equals(nsGkAtoms::observer))) {
+            // Compatibility hack. For some reason the original XBL
+            // implementation dropped the content of a binding if any child of
+            // the bound element didn't match any of the <children> in the
+            // binding. This became a pseudo-API that we have to maintain.
+
+            // Undo InstallAnonymousContent
+            UninstallAnonymousContent(doc, mContent);
+
+            // Clear out our children elements to avoid dangling references.
+            ClearInsertionPoints();
+
+            // Pretend as though there was no content in the binding.
+            mContent = nullptr;
+            return;
+          }
         }
       }
     }
 
     // Set binding parent on default content if need
     if (mDefaultInsertionPoint) {
       mDefaultInsertionPoint->MaybeSetupDefaultContent(bindingManager);
     }
--- a/dom/bindings/Makefile.in
+++ b/dom/bindings/Makefile.in
@@ -142,20 +142,20 @@ CSS2Properties.webidl: $(topsrcdir)/layo
 $(test_webidl_files): %: $(srcdir)/test/%
 	$(INSTALL) $(IFLAGS1) $(srcdir)/test/$* .
 
 $(binding_header_files): .BindingGen
 
 $(binding_cpp_files): .BindingGen
 
 # $(binding_dependency_trackers) pick up additional dependencies via .pp files
+# The rule: just brings the tracker up to date, if it's out of date, so that
+# we'll know that we have to redo binding generation and flag this prerequisite
+# there as being newer than the bindinggen target.
 $(binding_dependency_trackers):
-	# Just bring it up to date, if it's out of date, so that we'll know that
-	# we have to redo binding generation and flag this prerequisite there as
-	# being newer than the bindinggen target.
 	@$(TOUCH) $@
 
 $(globalgen_targets): ParserResults.pkl
 
 %-example: .BindingGen
 	PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
 	  $(PLY_INCLUDE) -I$(srcdir)/parser \
 	  $(srcdir)/ExampleGen.py \
@@ -174,44 +174,44 @@ globalgen_dependencies := \
   $(CACHE_DIR)/.done \
   $(GLOBAL_DEPS) \
   $(NULL)
 
 $(CACHE_DIR)/.done:
 	$(MKDIR) -p $(CACHE_DIR)
 	@$(TOUCH) $@
 
+# Running GlobalGen.py updates ParserResults.pkl as a side-effect
 ParserResults.pkl: $(globalgen_dependencies)
-	# Running GlobalGen.py updates ParserResults.pkl as a side-effect
 	PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
-    $(PLY_INCLUDE) -I$(srcdir)/parser \
-    $(srcdir)/GlobalGen.py $(srcdir)/Bindings.conf . \
-    --cachedir=$(CACHE_DIR) \
-    $(all_webidl_files)
+	  $(PLY_INCLUDE) -I$(srcdir)/parser \
+	  $(srcdir)/GlobalGen.py $(srcdir)/Bindings.conf . \
+	  --cachedir=$(CACHE_DIR) \
+	  $(all_webidl_files)
 
+# Make sure .deps actually exists, since we'll try to write to it from 
+# BindingGen.py but we're typically running in the export phase, which is
+# before anyone has bothered creating .deps.
+# Then, pass our long lists through files to try to avoid blowing out the
+# command line.
+# Next, BindingGen.py will examine the changed dependency list to figure out
+# what it really needs to regenerate.
+# Finally, touch the .BindingGen file so that we don't have to keep redoing
+# all that until something else actually changes.
 .BindingGen: $(bindinggen_dependencies) $(binding_dependency_trackers)
-	# Make sure .deps actually exists, since we'll try to write to it from
-	# BindingGen.py but we're typically running in the export phase, which
-	# is before anyone has bothered creating .deps.
 	$(MKDIR) -p .deps
-	# Pass our long lists through files to try to avoid blowing
-	# out the command line
 	echo $(all_webidl_files) > .all-webidl-file-list
 	echo $? > .changed-dependency-list
-	# BindingGen.py will examine the changed dependency list to figure out
-	# what it really needs to regenerate.
 	PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
-    $(PLY_INCLUDE) -I$(srcdir)/parser \
-    $(srcdir)/BindingGen.py \
-    $(srcdir)/Bindings.conf \
-    $(CURDIR) \
-    .all-webidl-file-list \
-    .changed-dependency-list
-	# Now touch the .BindingGen file so that we don't have to keep redoing
-	# all that until something else actually changes.
+	  $(PLY_INCLUDE) -I$(srcdir)/parser \
+	  $(srcdir)/BindingGen.py \
+	  $(srcdir)/Bindings.conf \
+	  $(CURDIR) \
+	  .all-webidl-file-list \
+	  .changed-dependency-list
 	@$(TOUCH) $@
 
 GARBAGE += \
   webidlyacc.py \
   parser.out \
   $(wildcard *-example.h) \
   $(wildcard *-example.cpp) \
   .BindingGen \
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -13,16 +13,17 @@
  * http://dev.w3.org/csswg/cssom-view/#extensions-to-the-document-interface
  *
  * http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/core/nsIDOMDocument.idl
  */
 
 interface StyleSheetList;
 interface WindowProxy;
 interface nsISupports;
+interface URI;
 
 enum VisibilityState { "hidden", "visible" };
 
 /* http://dom.spec.whatwg.org/#interface-document */
 [Constructor]
 interface Document : Node {
   [Throws]
   readonly attribute DOMImplementation implementation;
@@ -311,16 +312,21 @@ partial interface Document {
   // createTouchList().
   [Creator, Func="nsGenericHTMLElement::TouchEventsEnabled"]
   TouchList createTouchList();
   [Creator, Func="nsGenericHTMLElement::TouchEventsEnabled"]
   TouchList createTouchList(sequence<Touch> touches);
 
   [ChromeOnly]
   attribute boolean styleSheetChangeEventsEnabled;
+
+  [ChromeOnly, Throws]
+  void obsoleteSheet(URI sheetURI);
+  [ChromeOnly, Throws]
+  void obsoleteSheet(DOMString sheetURI);
 };
 
 // Extension to give chrome JS the ability to determine when a document was
 // created to satisfy an iframe with srcdoc attribute.
 partial interface Document {
   [ChromeOnly] readonly attribute boolean isSrcdocDocument;
 };
 
--- a/gfx/skia/src/core/SkStroke.cpp
+++ b/gfx/skia/src/core/SkStroke.cpp
@@ -5,17 +5,17 @@
  * found in the LICENSE file.
  */
 
 #include "SkStrokerPriv.h"
 #include "SkGeometry.h"
 #include "SkPath.h"
 
 #define kMaxQuadSubdivide   5
-#define kMaxCubicSubdivide  4
+#define kMaxCubicSubdivide  7
 
 static inline bool degenerate_vector(const SkVector& v) {
     return !SkPoint::CanNormalize(v.fX, v.fY);
 }
 
 static inline bool normals_too_curvy(const SkVector& norm0, SkVector& norm1) {
     /*  root2/2 is a 45-degree angle
         make this constant bigger for more subdivisions (but not >= 1)
@@ -299,22 +299,18 @@ DRAW_LINE:
     }
     if (degenerateAB || degenerateCD) {
         goto DRAW_LINE;
     }
     SkAssertResult(set_normal_unitnormal(cd, fRadius, normalCD, unitNormalCD));
     bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius,
                                                &normalBC, &unitNormalBC);
 #ifndef SK_IGNORE_CUBIC_STROKE_FIX
-    if (subDivide <= 0) {
-        if (degenerateBC) {
-            goto DRAW_LINE;
-        } else {
-            goto DRAW_CUBIC;
-        }
+    if (--subDivide < 0) {
+        goto DRAW_LINE;
     }
 #endif
     if (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) ||
              normals_too_curvy(unitNormalBC, *unitNormalCD)) {
 #ifdef SK_IGNORE_CUBIC_STROKE_FIX
         // subdivide if we can
         if (--subDivide < 0) {
             goto DRAW_LINE;
@@ -325,19 +321,16 @@ DRAW_LINE:
 
         SkChopCubicAtHalf(pts, tmp);
         this->cubic_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit,
                        subDivide);
         // we use dummys since we already have a valid (and more accurate)
         // normals for CD
         this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide);
     } else {
-#ifndef SK_IGNORE_CUBIC_STROKE_FIX
-    DRAW_CUBIC:
-#endif
         SkVector    normalB, normalC;
 
         // need normals to inset/outset the off-curve pts B and C
 
         SkVector    unitBC = pts[2] - pts[1];
         unitBC.normalize();
         unitBC.rotateCCW();
 
--- a/gfx/src/nsColor.cpp
+++ b/gfx/src/nsColor.cpp
@@ -212,16 +212,24 @@ NS_GFX_(bool) NS_ColorNameToRGB(const ns
     if (aResult) {
       *aResult = kColors[id];
     }
     return true;
   }
   return false;
 }
 
+// Returns kColorNames, an array of all possible color names, and sets
+// *aSizeArray to the size of that array. Do NOT call free() on this array.
+NS_GFX_(const char * const *) NS_AllColorNames(size_t *aSizeArray)
+{
+  *aSizeArray = ArrayLength(kColorNames);
+  return kColorNames;
+}
+
 // Macro to blend two colors
 //
 // equivalent to target = (bg*(255-fgalpha) + fg*fgalpha)/255
 #define MOZ_BLEND(target, bg, fg, fgalpha)       \
   FAST_DIVIDE_BY_255(target, (bg)*(255-fgalpha) + (fg)*(fgalpha))
 
 NS_GFX_(nscolor)
 NS_ComposeColors(nscolor aBG, nscolor aFG)
--- a/gfx/src/nsColor.h
+++ b/gfx/src/nsColor.h
@@ -61,16 +61,20 @@ NS_GFX_(bool) NS_LooseHexToRGB(const nsS
 
 // There is no function to translate a color to a hex string, because
 // the hex-string syntax does not support transparency.
 
 // Translate a color name to a color. Return true if it parses ok,
 // otherwise return false.
 NS_GFX_(bool) NS_ColorNameToRGB(const nsAString& aBuf, nscolor* aResult);
 
+// Returns an array of all possible color names, and sets
+// *aSizeArray to the size of that array. Do NOT call |free()| on this array.
+NS_GFX_(const char * const *) NS_AllColorNames(size_t *aSizeArray);
+
 // function to convert from HSL color space to RGB color space
 // the float parameters are all expected to be in the range 0-1
 NS_GFX_(nscolor) NS_HSL2RGB(float h, float s, float l);
 
 // Return a color name for the given nscolor.  If there is no color
 // name for it, returns null.  If there are multiple possible color
 // names for the given color, the first one in nsColorNameList.h
 // (which is generally the first one in alphabetical order) will be
new file mode 100644
--- /dev/null
+++ b/image/src/FrameAnimator.cpp
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FrameAnimator.h"
+
+#include "imgIContainer.h"
+
+using namespace mozilla::image;
+using namespace mozilla;
+
+FrameAnimator::FrameAnimator(FrameBlender& aFrameBlender)
+  : mCurrentAnimationFrameIndex(0)
+  , mLoopCount(-1)
+  , mFrameBlender(aFrameBlender)
+  , mAnimationMode(imgIContainer::kNormalAnimMode)
+  , mDoneDecoding(false)
+{
+}
+
+uint32_t
+FrameAnimator::GetSingleLoopTime() const
+{
+  // If we aren't done decoding, we don't know the image's full play time.
+  if (!mDoneDecoding) {
+    return 0;
+  }
+
+  // If we're not looping, a single loop time has no meaning
+  if (mAnimationMode != imgIContainer::kNormalAnimMode) {
+    return 0;
+  }
+
+  uint32_t looptime = 0;
+  for (uint32_t i = 0; i < mFrameBlender.GetNumFrames(); ++i) {
+    int32_t timeout = mFrameBlender.RawGetFrame(i)->GetTimeout();
+    if (timeout > 0) {
+      looptime += static_cast<uint32_t>(timeout);
+    } else {
+      // If we have a frame that never times out, we're probably in an error
+      // case, but let's handle it more gracefully.
+      NS_WARNING("Negative frame timeout - how did this happen?");
+      return 0;
+    }
+  }
+
+  return looptime;
+}
+
+TimeStamp
+FrameAnimator::GetCurrentImgFrameEndTime() const
+{
+  imgFrame* currentFrame = mFrameBlender.RawGetFrame(mCurrentAnimationFrameIndex);
+  TimeStamp currentFrameTime = mCurrentAnimationFrameTime;
+  int64_t timeout = currentFrame->GetTimeout();
+
+  if (timeout < 0) {
+    // We need to return a sentinel value in this case, because our logic
+    // doesn't work correctly if we have a negative timeout value. The reason
+    // this positive infinity was chosen was because it works with the loop in
+    // RequestRefresh() below.
+    return TimeStamp() +
+           TimeDuration::FromMilliseconds(static_cast<double>(UINT64_MAX));
+  }
+
+  TimeDuration durationOfTimeout =
+    TimeDuration::FromMilliseconds(static_cast<double>(timeout));
+  TimeStamp currentFrameEndTime = currentFrameTime + durationOfTimeout;
+
+  return currentFrameEndTime;
+}
+
+FrameAnimator::RefreshResult
+FrameAnimator::AdvanceFrame(TimeStamp aTime)
+{
+  NS_ASSERTION(aTime <= TimeStamp::Now(),
+               "Given time appears to be in the future");
+
+  uint32_t currentFrameIndex = mCurrentAnimationFrameIndex;
+  uint32_t nextFrameIndex = currentFrameIndex + 1;
+  uint32_t timeout = 0;
+
+  RefreshResult ret;
+
+  // If we're done decoding, we know we've got everything we're going to get.
+  // If we aren't, we only display fully-downloaded frames; everything else
+  // gets delayed.
+  bool needToWait = !mDoneDecoding &&
+                    mFrameBlender.RawGetFrame(nextFrameIndex) &&
+                    !mFrameBlender.RawGetFrame(nextFrameIndex)->ImageComplete();
+
+  if (needToWait) {
+    // Uh oh, the frame we want to show is currently being decoded (partial)
+    // Wait until the next refresh driver tick and try again
+    return ret;
+  } else {
+    // If we're done decoding the next frame, go ahead and display it now and
+    // reinit with the next frame's delay time.
+    if (mFrameBlender.GetNumFrames() == nextFrameIndex) {
+      // End of Animation, unless we are looping forever
+
+      // If animation mode is "loop once", it's time to stop animating
+      if (mAnimationMode == imgIContainer::kLoopOnceAnimMode || mLoopCount == 0) {
+        ret.animationFinished = true;
+      }
+
+      nextFrameIndex = 0;
+
+      if (mLoopCount > 0) {
+        mLoopCount--;
+      }
+
+      // If we're done, exit early.
+      if (ret.animationFinished) {
+        return ret;
+      }
+    }
+
+    timeout = mFrameBlender.GetFrame(nextFrameIndex)->GetTimeout();
+  }
+
+  // Bad data
+  if (!(timeout > 0)) {
+    ret.animationFinished = true;
+    ret.error = true;
+  }
+
+  if (nextFrameIndex == 0) {
+    ret.dirtyRect = mFirstFrameRefreshArea;
+  } else {
+    // Change frame
+    if (!mFrameBlender.DoBlend(&ret.dirtyRect, currentFrameIndex, nextFrameIndex)) {
+      // something went wrong, move on to next
+      NS_WARNING("FrameAnimator::AdvanceFrame(): Compositing of frame failed");
+      mFrameBlender.RawGetFrame(nextFrameIndex)->SetCompositingFailed(true);
+      mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime();
+      mCurrentAnimationFrameIndex = nextFrameIndex;
+
+      ret.error = true;
+      return ret;
+    }
+
+    mFrameBlender.RawGetFrame(nextFrameIndex)->SetCompositingFailed(false);
+  }
+
+  mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime();
+
+  // If we can get closer to the current time by a multiple of the image's loop
+  // time, we should.
+  uint32_t loopTime = GetSingleLoopTime();
+  if (loopTime > 0) {
+    TimeDuration delay = aTime - mCurrentAnimationFrameTime;
+    if (delay.ToMilliseconds() > loopTime) {
+      // Explicitly use integer division to get the floor of the number of
+      // loops.
+      uint32_t loops = static_cast<uint32_t>(delay.ToMilliseconds()) / loopTime;
+      mCurrentAnimationFrameTime += TimeDuration::FromMilliseconds(loops * loopTime);
+    }
+  }
+
+  // Set currentAnimationFrameIndex at the last possible moment
+  mCurrentAnimationFrameIndex = nextFrameIndex;
+
+  // If we're here, we successfully advanced the frame.
+  ret.frameAdvanced = true;
+
+  return ret;
+}
+
+FrameAnimator::RefreshResult
+FrameAnimator::RequestRefresh(const mozilla::TimeStamp& aTime)
+{
+  // only advance the frame if the current time is greater than or
+  // equal to the current frame's end time.
+  TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime();
+
+  // By default, an empty RefreshResult.
+  RefreshResult ret;
+
+  while (currentFrameEndTime <= aTime) {
+    TimeStamp oldFrameEndTime = currentFrameEndTime;
+
+    RefreshResult frameRes = AdvanceFrame(aTime);
+
+    // Accumulate our result for returning to callers.
+    ret.Accumulate(frameRes);
+
+    currentFrameEndTime = GetCurrentImgFrameEndTime();
+
+    // if we didn't advance a frame, and our frame end time didn't change,
+    // then we need to break out of this loop & wait for the frame(s)
+    // to finish downloading
+    if (!frameRes.frameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
+      break;
+    }
+  }
+
+  return ret;
+}
+
+void
+FrameAnimator::ResetAnimation()
+{
+  mCurrentAnimationFrameIndex = 0;
+}
+
+void
+FrameAnimator::SetDoneDecoding(bool aDone)
+{
+  mDoneDecoding = aDone;
+}
+
+void
+FrameAnimator::SetAnimationMode(uint16_t aAnimationMode)
+{
+  mAnimationMode = aAnimationMode;
+}
+
+void
+FrameAnimator::InitAnimationFrameTimeIfNecessary()
+{
+  if (mCurrentAnimationFrameTime.IsNull()) {
+    mCurrentAnimationFrameTime = TimeStamp::Now();
+  }
+}
+
+void
+FrameAnimator::SetAnimationFrameTime(const TimeStamp& aTime)
+{
+  mCurrentAnimationFrameTime = aTime;
+}
+
+void
+FrameAnimator::SetFirstFrameRefreshArea(const nsIntRect& aRect)
+{
+  mFirstFrameRefreshArea = aRect;
+}
+
+void
+FrameAnimator::UnionFirstFrameRefreshArea(const nsIntRect& aRect)
+{
+  mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea, aRect);
+}
+
+void
+FrameAnimator::SetLoopCount(int loopcount)
+{
+  mLoopCount = loopcount;
+}
+
+uint32_t
+FrameAnimator::GetCurrentAnimationFrameIndex() const
+{
+  return mCurrentAnimationFrameIndex;
+}
+
+nsIntRect
+FrameAnimator::GetFirstFrameRefreshArea() const
+{
+  return mFirstFrameRefreshArea;
+}
+
+
new file mode 100644
--- /dev/null
+++ b/image/src/FrameAnimator.h
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_imagelib_FrameAnimator_h_
+#define mozilla_imagelib_FrameAnimator_h_
+
+#include "mozilla/TimeStamp.h"
+#include "FrameBlender.h"
+#include "nsRect.h"
+
+namespace mozilla {
+namespace image {
+
+class FrameAnimator
+{
+public:
+  FrameAnimator(FrameBlender& aBlender);
+
+  /**
+   * Return value from RequestRefresh. Tells callers what happened in that call
+   * to RequestRefresh.
+   */
+  struct RefreshResult
+  {
+    // The dirty rectangle to be re-drawn after this RequestRefresh().
+    nsIntRect dirtyRect;
+
+    // Whether any frame changed, and hence the dirty rect was set.
+    bool frameAdvanced : 1;
+
+    // Whether the animation has finished playing.
+    bool animationFinished : 1;
+
+    // Whether an error has occurred when trying to advance a frame. Note that
+    // errors do not, on their own, end the animation.
+    bool error : 1;
+
+    RefreshResult()
+      : frameAdvanced(false)
+      , animationFinished(false)
+      , error(false)
+    {}
+
+    void Accumulate(const RefreshResult& other)
+    {
+      frameAdvanced = frameAdvanced || other.frameAdvanced;
+      animationFinished = animationFinished || other.animationFinished;
+      error = error || other.error;
+      dirtyRect = dirtyRect.Union(other.dirtyRect);
+    }
+  };
+
+  /**
+   * Re-evaluate what frame we're supposed to be on, and do whatever blending
+   * is necessary to get us to that frame.
+   *
+   * Returns the result of that blending, including whether the current frame
+   * changed and what the resulting dirty rectangle is.
+   */
+  RefreshResult RequestRefresh(const mozilla::TimeStamp& aTime);
+
+  /**
+   * Call when this image is finished decoding so we know that there aren't any
+   * more frames coming.
+   */
+  void SetDoneDecoding(bool aDone);
+
+  /**
+   * Call when you need to re-start animating. Ensures we start from the first
+   * frame.
+   */
+  void ResetAnimation();
+
+  /**
+   * Number of times to loop the image.
+   * @note -1 means forever.
+   */
+  void SetLoopCount(int32_t aLoopCount);
+
+  /**
+   * The animation mode of the image.
+   *
+   * Constants defined in imgIContainer.idl.
+   */
+  void SetAnimationMode(uint16_t aAnimationMode);
+
+  /**
+   * Set the area to refresh when we loop around to the first frame.
+   */
+  void SetFirstFrameRefreshArea(const nsIntRect& aRect);
+
+  /**
+   * Union the area to refresh when we loop around to the first frame with this
+   * rect.
+   */
+  void UnionFirstFrameRefreshArea(const nsIntRect& aRect);
+
+  /**
+   * If the animation frame time has not yet been set, set it to
+   * TimeStamp::Now().
+   */
+  void InitAnimationFrameTimeIfNecessary();
+
+  /**
+   * Set the animation frame time to @aTime.
+   */
+  void SetAnimationFrameTime(const TimeStamp& aTime);
+
+  /**
+   * The current frame we're on, from 0 to (numFrames - 1).
+   */
+  uint32_t GetCurrentAnimationFrameIndex() const;
+
+  /**
+   * Get the area we refresh when we loop around to the first frame.
+   */
+  nsIntRect GetFirstFrameRefreshArea() const;
+
+private: // methods
+  /**
+   * Gets the length of a single loop of this image, in milliseconds.
+   *
+   * If this image is not finished decoding, is not animated, or it is animated
+   * but does not loop, returns 0.
+   */
+  uint32_t GetSingleLoopTime() const;
+
+  /**
+   * Advances the animation. Typically, this will advance a single frame, but it
+   * may advance multiple frames. This may happen if we have infrequently
+   * "ticking" refresh drivers (e.g. in background tabs), or extremely short-
+   * lived animation frames.
+   *
+   * @param aTime the time that the animation should advance to. This will
+   *              typically be <= TimeStamp::Now().
+   *
+   * @returns a RefreshResult that shows whether the frame was successfully
+   *          advanced, and its resulting dirty rect.
+   */
+  RefreshResult AdvanceFrame(mozilla::TimeStamp aTime);
+
+  /**
+   * Get the time the frame we're currently displaying is supposed to end.
+   *
+   * In the error case, returns an "infinity" timestamp.
+   */
+  mozilla::TimeStamp GetCurrentImgFrameEndTime() const;
+
+private: // data
+  //! Area of the first frame that needs to be redrawn on subsequent loops.
+  nsIntRect mFirstFrameRefreshArea;
+
+  //! the time that the animation advanced to the current frame
+  TimeStamp mCurrentAnimationFrameTime;
+
+  //! The current frame index we're on. 0 to (numFrames - 1).
+  uint32_t mCurrentAnimationFrameIndex;
+
+  //! number of loops remaining before animation stops (-1 no stop)
+  int32_t mLoopCount;
+
+  //! All the frames of the image, shared with our owner
+  FrameBlender& mFrameBlender;
+
+  //! The animation mode of this image. Constants defined in imgIContainer.
+  uint16_t mAnimationMode;
+
+  //! Whether this image is done being decoded.
+  bool mDoneDecoding;
+};
+
+} // namespace image
+} // namespace mozilla
+
+#endif /* mozilla_imagelib_FrameAnimator_h_ */
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -19,16 +19,17 @@
 #include "prsystem.h"
 #include "ImageContainer.h"
 #include "Layers.h"
 #include "nsPresContext.h"
 #include "nsThread.h"
 #include "nsIThreadPool.h"
 #include "nsXPCOMCIDInternal.h"
 #include "nsIObserverService.h"
+#include "FrameAnimator.h"
 
 #include "nsPNGDecoder.h"
 #include "nsGIFDecoder2.h"
 #include "nsJPEGDecoder.h"
 #include "nsBMPDecoder.h"
 #include "nsICODecoder.h"
 #include "nsIconDecoder.h"
 
@@ -385,17 +386,16 @@ NS_IMPL_THREADSAFE_ISUPPORTS3(RasterImag
 //******************************************************************************
 RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
                          nsIURI* aURI /* = nullptr */) :
   ImageResource(aStatusTracker, aURI), // invoke superclass's constructor
   mSize(0,0),
   mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
   mMultipartDecodedFrame(nullptr),
   mAnim(nullptr),
-  mLoopCount(-1),
   mLockCount(0),
   mDecodeCount(0),
 #ifdef DEBUG
   mFramesNotified(0),
 #endif
   mDecodingMutex("RasterImage"),
   mDecoder(nullptr),
   mBytesDecoded(0),
@@ -523,197 +523,51 @@ RasterImage::Init(const char* aMimeType,
   }
 
   // Mark us as initialized
   mInitialized = true;
 
   return NS_OK;
 }
 
-uint32_t
-RasterImage::GetSingleLoopTime() const
-{
-  if (!mAnim) {
-    return 0;
-  }
-
-  // If we aren't done decoding, we don't know the image's full play time.
-  if (!mHasBeenDecoded) {
-    return 0;
-  }
-
-  // If we're not looping, a single loop time has no meaning
-  if (mLoopCount == 0) {
-    return 0;
-  }
-
-  uint32_t looptime = 0;
-  for (uint32_t i = 0; i < GetNumFrames(); ++i) {
-    int32_t timeout = mFrameBlender.RawGetFrame(i)->GetTimeout();
-    if (timeout > 0) {
-      looptime += static_cast<uint32_t>(timeout);
-    } else {
-      // If we have a frame that never times out, we're probably in an error
-      // case, but let's handle it more gracefully.
-      NS_WARNING("Negative frame timeout - how did this happen?");
-      return 0;
-    }
-  }
-
-  return looptime;
-}
-
-bool
-RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect)
-{
-  NS_ASSERTION(aTime <= TimeStamp::Now(),
-               "Given time appears to be in the future");
-
-  uint32_t currentFrameIndex = mAnim->currentAnimationFrameIndex;
-  uint32_t nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
-  uint32_t timeout = 0;
-
-  // Figure out if we have the next full frame. This is more complicated than
-  // just checking GetNumFrames() because decoders append their frames
-  // before they're filled in.
-  NS_ABORT_IF_FALSE(mDecoder || nextFrameIndex <= GetNumFrames(),
-                    "How did we get 2 indices too far by incrementing?");
-
-  // If we don't have a decoder, we know we've got everything we're going to
-  // get. If we do, we only display fully-downloaded frames; everything else
-  // gets delayed.
-  bool haveFullNextFrame = (mMultipart && mBytesDecoded == 0) || !mDecoder ||
-                            nextFrameIndex < mDecoder->GetCompleteFrameCount();
-
-  // If we're done decoding the next frame, go ahead and display it now and
-  // reinit with the next frame's delay time.
-  if (haveFullNextFrame) {
-    if (GetNumFrames() == nextFrameIndex) {
-      // End of Animation, unless we are looping forever
-
-      // If animation mode is "loop once", it's time to stop animating
-      if (mAnimationMode == kLoopOnceAnimMode || mLoopCount == 0) {
-        mAnimationFinished = true;
-        EvaluateAnimation();
-      }
-
-      nextFrameIndex = 0;
-
-      if (mLoopCount > 0) {
-        mLoopCount--;
-      }
-
-      if (!mAnimating) {
-        // break out early if we are actually done animating
-        return false;
-      }
-    }
-
-    timeout = mFrameBlender.GetFrame(nextFrameIndex)->GetTimeout();
-
-  } else {
-    // Uh oh, the frame we want to show is currently being decoded (partial)
-    // Wait until the next refresh driver tick and try again
-    return false;
-  }
-
-  if (!(timeout > 0)) {
-    mAnimationFinished = true;
-    EvaluateAnimation();
-  }
-
-  if (nextFrameIndex == 0) {
-    *aDirtyRect = mAnim->firstFrameRefreshArea;
-  } else {
-    // Change frame
-    if (!mFrameBlender.DoBlend(aDirtyRect, currentFrameIndex, nextFrameIndex)) {
-      // something went wrong, move on to next
-      NS_WARNING("RasterImage::AdvanceFrame(): Compositing of frame failed");
-      mFrameBlender.RawGetFrame(nextFrameIndex)->SetCompositingFailed(true);
-      mAnim->currentAnimationFrameTime = GetCurrentImgFrameEndTime();
-      mAnim->currentAnimationFrameIndex = nextFrameIndex;
-      return false;
-    }
-
-    mFrameBlender.RawGetFrame(nextFrameIndex)->SetCompositingFailed(false);
-  }
-
-  mAnim->currentAnimationFrameTime = GetCurrentImgFrameEndTime();
-
-  // If we can get closer to the current time by a multiple of the image's loop
-  // time, we should.
-  uint32_t loopTime = GetSingleLoopTime();
-  if (loopTime > 0) {
-    TimeDuration delay = aTime - mAnim->currentAnimationFrameTime;
-    if (delay.ToMilliseconds() > loopTime) {
-      // Explicitly use integer division to get the floor of the number of
-      // loops.
-      uint32_t loops = static_cast<uint32_t>(delay.ToMilliseconds()) / loopTime;
-      mAnim->currentAnimationFrameTime += TimeDuration::FromMilliseconds(loops * loopTime);
-    }
-  }
-
-  // Set currentAnimationFrameIndex at the last possible moment
-  mAnim->currentAnimationFrameIndex = nextFrameIndex;
-
-  return true;
-}
-
 //******************************************************************************
 // [notxpcom] void requestRefresh ([const] in TimeStamp aTime);
 NS_IMETHODIMP_(void)
 RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
 {
   if (!ShouldAnimate()) {
     return;
   }
 
   EvaluateAnimation();
 
-  // only advance the frame if the current time is greater than or
-  // equal to the current frame's end time.
-  TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime();
-  bool frameAdvanced = false;
-
-  // The dirtyRect variable will contain an accumulation of the sub-rectangles
-  // that are dirty for each frame we advance in AdvanceFrame().
-  nsIntRect dirtyRect;
-
-  while (currentFrameEndTime <= aTime) {
-    TimeStamp oldFrameEndTime = currentFrameEndTime;
-    nsIntRect frameDirtyRect;
-    bool didAdvance = AdvanceFrame(aTime, &frameDirtyRect);
-    frameAdvanced = frameAdvanced || didAdvance;
-    currentFrameEndTime = GetCurrentImgFrameEndTime();
-
-    // Accumulate the dirty area.
-    dirtyRect = dirtyRect.Union(frameDirtyRect);
-
-    // if we didn't advance a frame, and our frame end time didn't change,
-    // then we need to break out of this loop & wait for the frame(s)
-    // to finish downloading
-    if (!didAdvance && (currentFrameEndTime == oldFrameEndTime)) {
-      break;
-    }
+  FrameAnimator::RefreshResult res;
+  if (mAnim) {
+    res = mAnim->RequestRefresh(aTime);
   }
 
-  if (frameAdvanced) {
+  if (res.frameAdvanced) {
     // Notify listeners that our frame has actually changed, but do this only
     // once for all frames that we've now passed (if AdvanceFrame() was called
     // more than once).
     #ifdef DEBUG
       mFramesNotified++;
     #endif
 
     UpdateImageContainer();
 
     // Explicitly call this on mStatusTracker so we're sure to not interfere
     // with the decoding process
     if (mStatusTracker)
-      mStatusTracker->FrameChanged(&dirtyRect);
+      mStatusTracker->FrameChanged(&res.dirtyRect);
+  }
+
+  if (res.animationFinished) {
+    mAnimationFinished = true;
+    EvaluateAnimation();
   }
 }
 
 //******************************************************************************
 /* readonly attribute int32_t width; */
 NS_IMETHODIMP
 RasterImage::GetWidth(int32_t *aWidth)
 {
@@ -828,44 +682,21 @@ RasterImage::GetDrawableImgFrame(uint32_
     return nullptr;
   return frame;
 }
 
 uint32_t
 RasterImage::GetCurrentImgFrameIndex() const
 {
   if (mAnim)
-    return mAnim->currentAnimationFrameIndex;
+    return mAnim->GetCurrentAnimationFrameIndex();
 
   return 0;
 }
 
-TimeStamp
-RasterImage::GetCurrentImgFrameEndTime() const
-{
-  imgFrame* currentFrame = mFrameBlender.RawGetFrame(mAnim->currentAnimationFrameIndex);
-  TimeStamp currentFrameTime = mAnim->currentAnimationFrameTime;
-  int64_t timeout = currentFrame->GetTimeout();
-
-  if (timeout < 0) {
-    // We need to return a sentinel value in this case, because our logic
-    // doesn't work correctly if we have a negative timeout value. The reason
-    // this positive infinity was chosen was because it works with the loop in
-    // RequestRefresh() above.
-    return TimeStamp() +
-           TimeDuration::FromMilliseconds(static_cast<double>(UINT64_MAX));
-  }
-
-  TimeDuration durationOfTimeout =
-    TimeDuration::FromMilliseconds(static_cast<double>(timeout));
-  TimeStamp currentFrameEndTime = currentFrameTime + durationOfTimeout;
-
-  return currentFrameEndTime;
-}
-
 imgFrame*
 RasterImage::GetCurrentImgFrame()
 {
   return GetImgFrame(GetCurrentImgFrameIndex());
 }
 
 //******************************************************************************
 /* [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame); */
@@ -1225,16 +1056,40 @@ RasterImage::NonHeapSizeOfDecoded() cons
 
 size_t
 RasterImage::OutOfProcessSizeOfDecoded() const
 {
   return SizeOfDecodedWithComputedFallbackIfHeap(gfxASurface::MEMORY_OUT_OF_PROCESS,
                                                  NULL);
 }
 
+void
+RasterImage::EnsureAnimExists()
+{
+  if (!mAnim) {
+
+    // Create the animation context
+    mAnim = new FrameAnimator(mFrameBlender);
+
+    // We don't support discarding animated images (See bug 414259).
+    // Lock the image and throw away the key.
+    //
+    // Note that this is inefficient, since we could get rid of the source
+    // data too. However, doing this is actually hard, because we're probably
+    // calling ensureAnimExists mid-decode, and thus we're decoding out of
+    // the source buffer. Since we're going to fix this anyway later, and
+    // since we didn't kill the source data in the old world either, locking
+    // is acceptable for the moment.
+    LockImage();
+
+    // Notify our observers that we are starting animation.
+    CurrentStatusTracker().RecordImageIsAnimated();
+  }
+}
+
 nsresult
 RasterImage::InternalAddFrameHelper(uint32_t framenum, imgFrame *aFrame,
                                     uint8_t **imageData, uint32_t *imageLength,
                                     uint32_t **paletteData, uint32_t *paletteLength,
                                     imgFrame** aRetFrame)
 {
   NS_ABORT_IF_FALSE(framenum <= GetNumFrames(), "Invalid frame index!");
   if (framenum > GetNumFrames())
@@ -1305,25 +1160,23 @@ RasterImage::InternalAddFrame(uint32_t f
     EnsureAnimExists();
 
     // If we dispose of the first frame by clearing it, then the
     // First Frame's refresh area is all of itself.
     // RESTORE_PREVIOUS is invalid (assumed to be DISPOSE_CLEAR)
     int32_t frameDisposalMethod = mFrameBlender.RawGetFrame(0)->GetFrameDisposalMethod();
     if (frameDisposalMethod == FrameBlender::kDisposeClear ||
         frameDisposalMethod == FrameBlender::kDisposeRestorePrevious)
-      mAnim->firstFrameRefreshArea = mFrameBlender.RawGetFrame(0)->GetRect();
+      mAnim->SetFirstFrameRefreshArea(mFrameBlender.RawGetFrame(0)->GetRect());
   }
 
   // Calculate firstFrameRefreshArea
   // Some gifs are huge but only have a small area that they animate
   // We only need to refresh that small area when Frame 0 comes around again
-  nsIntRect frameRect = frame->GetRect();
-  mAnim->firstFrameRefreshArea.UnionRect(mAnim->firstFrameRefreshArea,
-                                         frameRect);
+  mAnim->UnionFirstFrameRefreshArea(frame->GetRect());
 
   rv = InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
                               paletteData, paletteLength, aRetFrame);
 
   return rv;
 }
 
 bool
@@ -1536,19 +1389,32 @@ RasterImage::DecodingComplete()
       // complexity and it's not really needed since we already are smart about
       // not displaying the still-decoding frame of an animated image. We may
       // have already stored an extra frame, though, so we'll release it here.
       delete mMultipartDecodedFrame;
       mMultipartDecodedFrame = nullptr;
     }
   }
 
+  if (mAnim) {
+    mAnim->SetDoneDecoding(true);
+  }
+
   return NS_OK;
 }
 
+NS_IMETHODIMP
+RasterImage::SetAnimationMode(uint16_t aAnimationMode)
+{
+  if (mAnim) {
+    mAnim->SetAnimationMode(aAnimationMode);
+  }
+  return SetAnimationModeInternal(aAnimationMode);
+}
+
 //******************************************************************************
 /* void StartAnimation () */
 nsresult
 RasterImage::StartAnimation()
 {
   if (mError)
     return NS_ERROR_FAILURE;
 
@@ -1560,19 +1426,17 @@ RasterImage::StartAnimation()
   if (currentFrame) {
     if (currentFrame->GetTimeout() < 0) { // -1 means display this frame forever
       mAnimationFinished = true;
       return NS_ERROR_ABORT;
     }
 
     // We need to set the time that this initial frame was first displayed, as
     // this is used in AdvanceFrame().
-    if (mAnim->currentAnimationFrameTime.IsNull()) {
-      mAnim->currentAnimationFrameTime = TimeStamp::Now();
-    }
+    mAnim->InitAnimationFrameTimeIfNecessary();
   }
 
   return NS_OK;
 }
 
 //******************************************************************************
 /* void stopAnimation (); */
 nsresult
@@ -1590,35 +1454,38 @@ RasterImage::StopAnimation()
 /* void resetAnimation (); */
 NS_IMETHODIMP
 RasterImage::ResetAnimation()
 {
   if (mError)
     return NS_ERROR_FAILURE;
 
   if (mAnimationMode == kDontAnimMode ||
-      !mAnim || mAnim->currentAnimationFrameIndex == 0)
+      !mAnim || mAnim->GetCurrentAnimationFrameIndex() == 0)
     return NS_OK;
 
   mAnimationFinished = false;
 
   if (mAnimating)
     StopAnimation();
 
   mFrameBlender.ResetAnimation();
-
-  mAnim->currentAnimationFrameIndex = 0;
+  if (mAnim) {
+    mAnim->ResetAnimation();
+  }
+
   UpdateImageContainer();
 
   // Note - We probably want to kick off a redecode somewhere around here when
   // we fix bug 500402.
 
   // Update display if we were animating before
   if (mAnimating && mStatusTracker) {
-    mStatusTracker->FrameChanged(&(mAnim->firstFrameRefreshArea));
+    nsIntRect rect = mAnim->GetFirstFrameRefreshArea();
+    mStatusTracker->FrameChanged(&rect);
   }
 
   if (ShouldAnimate()) {
     StartAnimation();
     // The animation may not have been running before, if mAnimationFinished
     // was false (before we changed it to true in this function). So, mark the
     // animation as running.
     mAnimating = true;
@@ -1630,39 +1497,37 @@ RasterImage::ResetAnimation()
 //******************************************************************************
 // [notxpcom] void requestRefresh ([const] in TimeStamp aTime);
 NS_IMETHODIMP_(void)
 RasterImage::SetAnimationStartTime(const mozilla::TimeStamp& aTime)
 {
   if (mError || mAnimating || !mAnim)
     return;
 
-  mAnim->currentAnimationFrameTime = aTime;
+  mAnim->SetAnimationFrameTime(aTime);
 }
 
 NS_IMETHODIMP_(float)
 RasterImage::GetFrameIndex(uint32_t aWhichFrame)
 {
   MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
   return (aWhichFrame == FRAME_FIRST || !mAnim)
          ? 0.0f
-         : mAnim->currentAnimationFrameIndex;
+         : mAnim->GetCurrentAnimationFrameIndex();
 }
 
 void
 RasterImage::SetLoopCount(int32_t aLoopCount)
 {
   if (mError)
     return;
 
-  // -1  infinite
-  //  0  no looping, one iteration
-  //  1  one loop, two iterations
-  //  ...
-  mLoopCount = aLoopCount;
+  if (mAnim) {
+    mAnim->SetLoopCount(aLoopCount);
+  }
 }
 
 nsresult
 RasterImage::AddSourceData(const char *aBuffer, uint32_t aCount)
 {
   MutexAutoLock lock(mDecodingMutex);
 
   if (mError)
@@ -1924,16 +1789,20 @@ RasterImage::OnNewSourceData()
 
   // Reset some flags
   mDecoded = false;
   mHasSourceData = false;
   mHasSize = false;
   mWantFullDecode = true;
   mDecodeRequest = nullptr;
 
+  if (mAnim) {
+    mAnim->SetDoneDecoding(false);
+  }
+
   // We always need the size first.
   rv = InitDecoder(/* aDoSizeDecode = */ true);
   CONTAINER_ENSURE_SUCCESS(rv);
 
   return NS_OK;
 }
 
 nsresult
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -115,33 +115,30 @@ class nsIThreadPool;
  * disposal.  So, in the middle of DoComposite when composing Frame 3, right
  * after destroying Frame 2's area, we copy compositingFrame to
  * prevCompositingFrame.  When DoComposite gets called to do Frame 4, we
  * copy prevCompositingFrame back, and then draw Frame 4 on top.
  *
  * @par
  * The mAnim structure has members only needed for animated images, so
  * it's not allocated until the second frame is added.
- *
- * @note
- * mAnimationMode and mLoopCount are not in the mAnim structure because
- * they have public setters.
  */
 
 class ScaleRequest;
 
 namespace mozilla {
 namespace layers {
 class LayerManager;
 class ImageContainer;
 class Image;
 }
 namespace image {
 
 class Decoder;
+class FrameAnimator;
 
 class RasterImage : public ImageResource
                   , public nsIProperties
                   , public SupportsWeakPtr<RasterImage>
 #ifdef DEBUG
                   , public imgIContainerDebug
 #endif
 {
@@ -319,30 +316,16 @@ private:
       return *mDecodeRequest->mStatusTracker;
     } else {
       return *mStatusTracker;
     }
   }
 
   nsresult OnImageDataCompleteCore(nsIRequest* aRequest, nsISupports*, nsresult aStatus);
 
-  struct Anim
-  {
-    //! Area of the first frame that needs to be redrawn on subsequent loops.
-    nsIntRect                  firstFrameRefreshArea;
-    uint32_t                   currentAnimationFrameIndex; // 0 to numFrames-1
-
-    // the time that the animation advanced to the current frame
-    TimeStamp                  currentAnimationFrameTime;
-
-    Anim() :
-      currentAnimationFrameIndex(0)
-    {}
-  };
-
   /**
    * Each RasterImage has a pointer to one or zero heap-allocated
    * DecodeRequests.
    */
   struct DecodeRequest
   {
     DecodeRequest(RasterImage* aImage)
       : mImage(aImage)
@@ -541,84 +524,35 @@ private:
                                     const nsIntRect &aSubimage,
                                     uint32_t aFlags);
 
   nsresult CopyFrame(uint32_t aWhichFrame,
                      uint32_t aFlags,
                      gfxImageSurface **_retval);
 
   /**
-   * Advances the animation. Typically, this will advance a single frame, but it
-   * may advance multiple frames. This may happen if we have infrequently
-   * "ticking" refresh drivers (e.g. in background tabs), or extremely short-
-   * lived animation frames.
-   *
-   * @param aTime the time that the animation should advance to. This will
-   *              typically be <= TimeStamp::Now().
-   *
-   * @param [out] aDirtyRect a pointer to an nsIntRect which encapsulates the
-   *        area to be repainted after the frame is advanced.
-   *
-   * @returns true, if the frame was successfully advanced, false if it was not
-   *          able to be advanced (e.g. the frame to which we want to advance is
-   *          still decoding). Note: If false is returned, then aDirtyRect will
-   *          remain unmodified.
-   */
-  bool AdvanceFrame(mozilla::TimeStamp aTime, nsIntRect* aDirtyRect);
-
-  /**
-   * Gets the length of a single loop of this image, in milliseconds.
-   *
-   * If this image is not finished decoding, is not animated, or it is animated
-   * but does not loop, returns 0.
-   */
-  uint32_t GetSingleLoopTime() const;
-
-  /**
    * Deletes and nulls out the frame in mFrames[framenum].
    *
    * Does not change the size of mFrames.
    *
    * @param framenum The index of the frame to be deleted.
    *                 Must lie in [0, mFrames.Length() )
    */
   void DeleteImgFrame(uint32_t framenum);
 
   imgFrame* GetImgFrameNoDecode(uint32_t framenum);
   imgFrame* GetImgFrame(uint32_t framenum);
   imgFrame* GetDrawableImgFrame(uint32_t framenum);
   imgFrame* GetCurrentImgFrame();
   uint32_t GetCurrentImgFrameIndex() const;
-  mozilla::TimeStamp GetCurrentImgFrameEndTime() const;
 
   size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxASurface::MemoryLocation aLocation,
                                                  mozilla::MallocSizeOf aMallocSizeOf) const;
 
-  inline void EnsureAnimExists()
-  {
-    if (!mAnim) {
-
-      // Create the animation context
-      mAnim = new Anim();
-
-      // We don't support discarding animated images (See bug 414259).
-      // Lock the image and throw away the key.
-      //
-      // Note that this is inefficient, since we could get rid of the source
-      // data too. However, doing this is actually hard, because we're probably
-      // calling ensureAnimExists mid-decode, and thus we're decoding out of
-      // the source buffer. Since we're going to fix this anyway later, and
-      // since we didn't kill the source data in the old world either, locking
-      // is acceptable for the moment.
-      LockImage();
-
-      // Notify our observers that we are starting animation.
-      CurrentStatusTracker().RecordImageIsAnimated();
-    }
-  }
+  void EnsureAnimExists();
 
   nsresult InternalAddFrameHelper(uint32_t framenum, imgFrame *frame,
                                   uint8_t **imageData, uint32_t *imageLength,
                                   uint32_t **paletteData, uint32_t *paletteLength,
                                   imgFrame** aRetFrame);
   nsresult InternalAddFrame(uint32_t framenum, int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight,
                             gfxASurface::gfxImageFormat aFormat, uint8_t aPaletteDepth,
                             uint8_t **imageData, uint32_t *imageLength,
@@ -666,20 +600,17 @@ private: // data
   // The last frame we decoded for multipart images.
   imgFrame*                  mMultipartDecodedFrame;
 
   nsCOMPtr<nsIProperties>    mProperties;
 
   // IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure
   // that the frames actually exist (they may have been discarded to save memory, or
   // we maybe decoding on draw).
-  RasterImage::Anim*        mAnim;
-
-  //! # loops remaining before animation stops (-1 no stop)
-  int32_t                    mLoopCount;
+  FrameAnimator* mAnim;
 
   // Discard members
   uint32_t                   mLockCount;
   DiscardTracker::Node       mDiscardTrackerNode;
 
   // Source data members
   nsCString                  mSourceDataMimeType;
 
@@ -786,20 +717,16 @@ protected:
 
   friend class ImageFactory;
 };
 
 inline NS_IMETHODIMP RasterImage::GetAnimationMode(uint16_t *aAnimationMode) {
   return GetAnimationModeInternal(aAnimationMode);
 }
 
-inline NS_IMETHODIMP RasterImage::SetAnimationMode(uint16_t aAnimationMode) {
-  return SetAnimationModeInternal(aAnimationMode);
-}
-
 // Asynchronous Decode Requestor
 //
 // We use this class when someone calls requestDecode() from within a decode
 // notification. Since requestDecode() involves modifying the decoder's state
 // (for example, possibly shutting down a header-only decode and starting a
 // full decode), we don't want to do this from inside a decoder.
 class imgDecodeRequestor : public nsRunnable
 {
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -504,17 +504,17 @@ nsresult imgFrame::ImageUpdated(const ns
   nsIntRect boundsRect(mOffset, mSize);
   mDecoded.IntersectRect(mDecoded, boundsRect);
 
   mDirty = true;
 
   return NS_OK;
 }
 
-bool imgFrame::GetIsDirty()
+bool imgFrame::GetIsDirty() const
 {
   MutexAutoLock lock(mDirtyMutex);
   return mDirty;
 }
 
 nsIntRect imgFrame::GetRect() const
 {
   return nsIntRect(mOffset, mSize);
@@ -791,18 +791,21 @@ int32_t imgFrame::GetBlendMethod() const
   return mBlendMethod;
 }
 
 void imgFrame::SetBlendMethod(int32_t aBlendMethod)
 {
   mBlendMethod = (int8_t)aBlendMethod;
 }
 
+// This can be called from any thread.
 bool imgFrame::ImageComplete() const
 {
+  MutexAutoLock lock(mDirtyMutex);
+
   return mDecoded.IsEqualInterior(nsIntRect(mOffset, mSize));
 }
 
 // A hint from the image decoders that this image has no alpha, even
 // though we created is ARGB32.  This changes our format to RGB24,
 // which in turn will cause us to Optimize() to RGB24.  Has no effect
 // after Optimize() is called, though in all cases it will be just a
 // performance win -- the pixels are still correct and have the A byte
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -36,17 +36,17 @@ public:
   nsresult Optimize();
 
   void Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter,
             const gfxMatrix &aUserSpaceToImageSpace, const gfxRect& aFill,
             const nsIntMargin &aPadding, const nsIntRect &aSubimage,
             uint32_t aImageFlags = imgIContainer::FLAG_NONE);
 
   nsresult ImageUpdated(const nsIntRect &aUpdateRect);
-  bool GetIsDirty();
+  bool GetIsDirty() const;
 
   nsIntRect GetRect() const;
   gfxASurface::gfxImageFormat GetFormat() const;
   bool GetNeedsBackground() const;
   uint32_t GetImageBytesPerRow() const;
   uint32_t GetImageDataLength() const;
   bool GetIsPaletted() const;
   bool GetHasAlpha() const;
@@ -147,17 +147,17 @@ private: // data
   nsRefPtr<gfxQuartzImageSurface> mQuartzSurface;
 #endif
 
   nsIntSize    mSize;
   nsIntPoint   mOffset;
 
   nsIntRect    mDecoded;
 
-  mozilla::Mutex mDirtyMutex;
+  mutable mozilla::Mutex mDirtyMutex;
 
   // The palette and image data for images that are paletted, since Cairo
   // doesn't support these images.
   // The paletted data comes first, then the image data itself.
   // Total length is PaletteDataLength() + GetImageDataLength().
   uint8_t*     mPalettedImageData;
 
   // Note that the data stored in gfxRGBA is *non-alpha-premultiplied*.
--- a/image/src/moz.build
+++ b/image/src/moz.build
@@ -12,16 +12,17 @@ EXPORTS += [
     'imgRequest.h',
     'imgRequestProxy.h',
 ]
 
 CPP_SOURCES += [
     'ClippedImage.cpp',
     'Decoder.cpp',
     'DiscardTracker.cpp',
+    'FrameAnimator.cpp',
     'FrameBlender.cpp',
     'FrameSequence.cpp',
     'FrozenImage.cpp',
     'Image.cpp',
     'ImageFactory.cpp',
     'ImageMetadata.cpp',
     'ImageOps.cpp',
     'ImageWrapper.cpp',
--- a/js/public/Vector.h
+++ b/js/public/Vector.h
@@ -205,18 +205,16 @@ class Vector : private AllocPolicy
 
     static const bool sElemIsPod = mozilla::IsPod<T>::value;
     typedef VectorImpl<T, N, AllocPolicy, sElemIsPod> Impl;
     friend struct VectorImpl<T, N, AllocPolicy, sElemIsPod>;
 
     bool growStorageBy(size_t incr);
     bool convertToHeapStorage(size_t newCap);
 
-    template <bool InitNewElems> inline bool growByImpl(size_t inc);
-
     /* magic constants */
 
     static const int sMaxInlineBytes = 1024;
 
     /* compute constants */
 
     /*
      * Consider element size to be 1 for buffer sizing if there are
@@ -751,48 +749,49 @@ Vector<T,N,AP>::shrinkBy(size_t incr)
 {
     REENTRANCY_GUARD_ET_AL;
     MOZ_ASSERT(incr <= mLength);
     Impl::destroy(endNoCheck() - incr, endNoCheck());
     mLength -= incr;
 }
 
 template <class T, size_t N, class AP>
-template <bool InitNewElems>
 MOZ_ALWAYS_INLINE bool
-Vector<T,N,AP>::growByImpl(size_t incr)
+Vector<T,N,AP>::growBy(size_t incr)
 {
     REENTRANCY_GUARD_ET_AL;
     if (incr > mCapacity - mLength && !growStorageBy(incr))
         return false;
 
     MOZ_ASSERT(mLength + incr <= mCapacity);
     T *newend = endNoCheck() + incr;
-    if (InitNewElems)
-        Impl::initialize(endNoCheck(), newend);
+    Impl::initialize(endNoCheck(), newend);
     mLength += incr;
 #ifdef DEBUG
     if (mLength > mReserved)
         mReserved = mLength;
 #endif
     return true;
 }
 
 template <class T, size_t N, class AP>
 MOZ_ALWAYS_INLINE bool
-Vector<T,N,AP>::growBy(size_t incr)
-{
-    return growByImpl<true>(incr);
-}
-
-template <class T, size_t N, class AP>
-MOZ_ALWAYS_INLINE bool
 Vector<T,N,AP>::growByUninitialized(size_t incr)
 {
-    return growByImpl<false>(incr);
+    REENTRANCY_GUARD_ET_AL;
+    if (incr > mCapacity - mLength && !growStorageBy(incr))
+        return false;
+
+    MOZ_ASSERT(mLength + incr <= mCapacity);
+    mLength += incr;
+#ifdef DEBUG
+    if (mLength > mReserved)
+        mReserved = mLength;
+#endif
+    return true;
 }
 
 template <class T, size_t N, class AP>
 STATIC_POSTCONDITION(!return || ubound(this->begin()) >= newLength)
 inline bool
 Vector<T,N,AP>::resize(size_t newLength)
 {
     size_t curLength = mLength;
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -599,16 +599,17 @@ if test -n "$_WIN32_MSVC"; then
     SKIP_LIBRARY_CHECKS=1
 
     # Since we're skipping compiler and library checks, hard-code
     # some facts here.
 
     # Common to all MSVC environments:
 
     AC_DEFINE(HAVE_LOCALECONV)
+    AC_DEFINE(HAVE_HYPOT)
 fi
 
 fi # COMPILE_ENVIRONMENT
 
 if test "$cross_compiling"  = "yes"; then
     CROSS_COMPILE=1
 else
     CROSS_COMPILE=
@@ -2747,16 +2748,22 @@ fi
 dnl Checks for library functions.
 dnl ========================================================
 AC_PROG_GCC_TRADITIONAL
 AC_FUNC_MEMCMP
 AC_CHECK_FUNCS([fchmod flockfile getc_unlocked _getc_nolock getpagesize \
                 lchown localtime_r lstat64 memmove random rint sbrk snprintf \
                 stat64 statvfs statvfs64 strerror strtok_r truncate64])
 
+
+dnl Checks for math functions.
+dnl ========================================================
+AC_CHECK_FUNCS(log2 log1p expm1 sqrt1pm1 acosh asinh atanh hypot trunc cbrt)
+
+
 dnl check for wcrtomb/mbrtowc
 dnl =======================================================================
 if test -z "$MACOS_DEPLOYMENT_TARGET" || test "$MACOS_DEPLOYMENT_TARGET" -ge "100300"; then
 AC_LANG_SAVE
 AC_LANG_CPLUSPLUS
 AC_CACHE_CHECK(for wcrtomb,
     ac_cv_have_wcrtomb,
     [AC_TRY_LINK([#include <wchar.h>],
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -353,35 +353,35 @@ frontend::CompileScript(JSContext *cx, H
 
     if (sct == &mysct && !sct->complete())
         return NULL;
 
     return script;
 }
 
 bool
-frontend::CompileLazyFunction(JSContext *cx, HandleFunction fun, LazyScript *lazy,
-                              const jschar *chars, size_t length)
+frontend::CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *chars, size_t length)
 {
-    JS_ASSERT(cx->compartment() == fun->compartment());
+    JS_ASSERT(cx->compartment() == lazy->function()->compartment());
 
     CompileOptions options(cx, lazy->version());
     options.setPrincipals(cx->compartment()->principals)
            .setOriginPrincipals(lazy->originPrincipals())
            .setFileAndLine(lazy->source()->filename(), lazy->lineno())
            .setColumn(lazy->column())
            .setCompileAndGo(true)
            .setNoScriptRval(false)
            .setSelfHostingMode(false);
 
     Parser<FullParseHandler> parser(cx, &cx->tempLifoAlloc(), options, chars, length,
                                     /* foldConstants = */ true, NULL, lazy);
 
     uint32_t staticLevel = lazy->staticLevel(cx);
 
+    Rooted<JSFunction*> fun(cx, lazy->function());
     ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict());
     if (!pn)
         return false;
 
     if (!NameFunctions(cx, pn))
         return false;
 
     RootedObject enclosingScope(cx, lazy->enclosingScope());
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -21,18 +21,17 @@ namespace frontend {
 
 JSScript *
 CompileScript(JSContext *cx, HandleObject scopeChain, HandleScript evalCaller,
               const CompileOptions &options, const jschar *chars, size_t length,
               JSString *source_ = NULL, unsigned staticLevel = 0,
               SourceCompressionToken *extraSct = NULL);
 
 bool
-CompileLazyFunction(JSContext *cx, HandleFunction fun, LazyScript *lazy,
-                    const jschar *chars, size_t length);
+CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *chars, size_t length);
 
 bool
 CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options,
                     const AutoNameVector &formals, const jschar *chars, size_t length,
                     bool isAsmJSRecompile = false);
 
 /*
  * True if str consists of an IdentifierStart character, followed by one or
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2041,21 +2041,16 @@ EmitElemOperands(ExclusiveContext *cx, P
         if (!right)
             return false;
     } else {
         JS_ASSERT(pn->isArity(PN_BINARY));
         left = pn->pn_left;
         right = pn->pn_right;
     }
 
-    if (op == JSOP_GETELEM && left->isKind(PNK_NAME) && right->isKind(PNK_NUMBER)) {
-        if (!BindNameToSlot(cx, bce, left))
-            return false;
-    }
-
     if (!EmitTree(cx, bce, left))
         return false;
 
     if (op == JSOP_CALLELEM && Emit1(cx, bce, JSOP_DUP) < 0)
         return false;
 
     if (!EmitTree(cx, bce, right))
         return false;
@@ -2726,92 +2721,108 @@ EmitDestructuringLHS(ExclusiveContext *c
         if (emitOption == InitializeVars) {
             /*
              * Per its post-condition, EmitDestructuringOpsHelper has left the
              * to-be-destructured value on top of the stack.
              */
             if (Emit1(cx, bce, JSOP_POP) < 0)
                 return false;
         }
+    } else if (emitOption == PushInitialValues) {
+        // The lhs is a simple name so the to-be-destructured value is
+        // its initial value and there is nothing to do.
+        JS_ASSERT(pn->getOp() == JSOP_GETLOCAL);
+        JS_ASSERT(pn->pn_dflags & PND_BOUND);
     } else {
-        if (emitOption == PushInitialValues) {
-            /*
-             * The lhs is a simple name so the to-be-destructured value is
-             * its initial value and there is nothing to do.
-             */
-            JS_ASSERT(pn->getOp() == JSOP_GETLOCAL);
-            JS_ASSERT(pn->pn_dflags & PND_BOUND);
-            return true;
-        }
-
-        /* All paths below must pop after assigning to the lhs. */
-
-        if (pn->isKind(PNK_NAME)) {
+        // All paths below must pop after assigning to the lhs.
+
+        switch (pn->getKind()) {
+          case PNK_NAME:
             if (!BindNameToSlot(cx, bce, pn))
                 return false;
 
-            /* Allow 'const [x,y] = o', make 'const x,y; [x,y] = o' a nop. */
+            // Allow 'const [x,y] = o', make 'const x,y; [x,y] = o' a nop.
             if (pn->isConst() && !pn->isDefn())
                 return Emit1(cx, bce, JSOP_POP) >= 0;
-        }
-
-        switch (pn->getOp()) {
-          case JSOP_SETNAME:
-          case JSOP_SETGNAME:
-            /*
-             * NB: pn is a PN_NAME node, not a PN_BINARY.  Nevertheless,
-             * we want to emit JSOP_ENUMELEM, which has format JOF_ELEM.
-             * So here and for JSOP_ENUMCONSTELEM, we use EmitElemOp.
-             */
+
+            switch (pn->getOp()) {
+              case JSOP_SETNAME:
+              case JSOP_SETGNAME:
+                // This is like ordinary assignment, but with one difference.
+                //
+                // In `a = b`, we first determine a binding for `a` (using
+                // JSOP_BINDNAME or JSOP_BINDGNAME), then we evaluate `b`, then
+                // a JSOP_SETNAME instruction.
+                //
+                // In `[a] = [b]`, per spec, `b` is evaluated first, then we
+                // determine a binding for `a`. Then we need to do assignment--
+                // but the operands are on the stack in the wrong order for
+                // JSOP_SETPROP, so we use JSOP_ENUMELEM instead.
+                //
+                // EmitElemOp ordinarily works with PNK_ELEM nodes, naturally,
+                // but it has special code to handle PNK_NAME nodes in this one
+                // case.
+                if (!EmitElemOp(cx, pn, JSOP_ENUMELEM, bce))
+                    return false;
+                break;
+
+              case JSOP_SETCONST:
+                // As above.
+                if (!EmitElemOp(cx, pn, JSOP_ENUMCONSTELEM, bce))
+                    return false;
+                break;
+
+              case JSOP_SETLOCAL:
+              case JSOP_SETARG:
+                if (!EmitVarOp(cx, pn, pn->getOp(), bce))
+                    return false;
+                if (Emit1(cx, bce, JSOP_POP) < 0)
+                    return false;
+                break;
+
+              default:
+                MOZ_ASSUME_UNREACHABLE("EmitDestructuringLHS: bad name op");
+            }
+            break;
+
+          case PNK_DOT:
+          case PNK_ELEM:
+            // See the (PNK_NAME, JSOP_SETNAME) case above.
+            //
+            // In `a.x = b`, `a` is evaluated first, then `b`, then a
+            // JSOP_SETPROP instruction.
+            //
+            // In `[a.x] = [b]`, per spec, `b` is evaluated before `a`. Then we
+            // need a property set -- but the operands are on the stack in the
+            // wrong order for JSOP_SETPROP, so we use JSOP_ENUMELEM instead.
+            //
+            // EmitElemOp has special code to handle PNK_DOT nodes in this one
+            // case.
             if (!EmitElemOp(cx, pn, JSOP_ENUMELEM, bce))
                 return false;
             break;
 
-          case JSOP_SETCONST:
-            if (!EmitElemOp(cx, pn, JSOP_ENUMCONSTELEM, bce))
-                return false;
-            break;
-
-          case JSOP_SETLOCAL:
-          case JSOP_SETARG:
-            if (!EmitVarOp(cx, pn, pn->getOp(), bce))
-                return false;
-            if (Emit1(cx, bce, JSOP_POP) < 0)
-                return false;
-            break;
-
-          case JSOP_CALL:
-          case JSOP_EVAL:
-          case JSOP_FUNCALL:
-          case JSOP_FUNAPPLY:
+          case PNK_CALL:
             JS_ASSERT(pn->pn_xflags & PNX_SETCALL);
             if (!EmitTree(cx, bce, pn))
                 return false;
 
-            /*
-             * We just emitted JSOP_SETCALL which will always throw.
-             * Pop the call return value and the RHS.
-             */
+            // Pop the call return value and the RHS, presumably for the
+            // benefit of bytecode analysis. (The interpreter will never reach
+            // these instructions since we just emitted JSOP_SETCALL, which
+            // always throws. It's possible no analyses actually depend on this
+            // either.)
             if (Emit1(cx, bce, JSOP_POP) < 0)
                 return false;
             if (Emit1(cx, bce, JSOP_POP) < 0)
                 return false;
             break;
 
           default:
-          {
-            if (!EmitTree(cx, bce, pn))
-                return false;
-            if (!EmitElemOpBase(cx, bce, JSOP_ENUMELEM))
-                return false;
-            break;
-          }
-
-          case JSOP_ENUMELEM:
-            JS_ASSERT(0);
+            MOZ_ASSUME_UNREACHABLE("EmitDestructuringLHS: bad lhs kind");
         }
     }
 
     return true;
 }
 
 /*
  * Recursive helper for EmitDestructuringOps.
@@ -5676,29 +5687,31 @@ frontend::EmitTree(ExclusiveContext *cx,
                 }
             }
         }
         if (fun->hasDefaults()) {
             ParseNode *rest = NULL;
             bool restIsDefn = false;
             if (fun->hasRest()) {
                 JS_ASSERT(!bce->sc->asFunctionBox()->argumentsHasLocalBinding());
+
                 // Defaults with a rest parameter need special handling. The
                 // rest parameter needs to be undefined while defaults are being
                 // processed. To do this, we create the rest argument and let it
                 // sit on the stack while processing defaults. The rest
                 // parameter's slot is set to undefined for the course of
                 // default processing.
                 rest = pn->pn_head;
                 while (rest->pn_next != pnlast)
                     rest = rest->pn_next;
                 restIsDefn = rest->isDefn();
                 if (Emit1(cx, bce, JSOP_REST) < 0)
                     return false;
                 CheckTypeSet(cx, bce, JSOP_REST);
+
                 // Only set the rest parameter if it's not aliased by a nested
                 // function in the body.
                 if (restIsDefn) {
                     if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
                         return false;
                     if (!BindNameToSlot(cx, bce, rest))
                         return false;
                     if (!EmitVarOp(cx, rest, JSOP_SETARG, bce))
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -81,27 +81,23 @@ class FullParseHandler
     {}
 
     static ParseNode *null() { return NULL; }
 
     ParseNode *freeTree(ParseNode *pn) { return allocator.freeTree(pn); }
     void prepareNodeForMutation(ParseNode *pn) { return allocator.prepareNodeForMutation(pn); }
     const Token &currentToken() { return tokenStream.currentToken(); }
 
-    ParseNode *newName(PropertyName *name, InBlockBool inBlock, uint32_t blockid,
-                       const TokenPos &pos)
-    {
-        return new_<NameNode>(PNK_NAME, JSOP_NAME, name, inBlock, blockid, pos);
+    ParseNode *newName(PropertyName *name, uint32_t blockid, const TokenPos &pos) {
+        return new_<NameNode>(PNK_NAME, JSOP_NAME, name, blockid, pos);
     }
 
-    Definition *newPlaceholder(JSAtom *atom, InBlockBool inBlock, uint32_t blockid,
-                               const TokenPos &pos)
-    {
+    Definition *newPlaceholder(JSAtom *atom, uint32_t blockid, const TokenPos &pos) {
         Definition *dn =
-            (Definition *) new_<NameNode>(PNK_NAME, JSOP_NOP, atom, inBlock, blockid, pos);
+            (Definition *) new_<NameNode>(PNK_NAME, JSOP_NOP, atom, blockid, pos);
         if (!dn)
             return NULL;
         dn->setDefn(true);
         dn->pn_dflags |= PND_PLACEHOLDER;
         return dn;
     }
 
     ParseNode *newIdentifier(JSAtom *atom, const TokenPos &pos) {
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -625,17 +625,16 @@ class ParseNode
     Definition *maybeLexDef() { return pn_used ? lexdef() : NULL; }
 
     Definition *resolve();
 
 /* PN_CODE and PN_NAME pn_dflags bits. */
 #define PND_LET                 0x01    /* let (block-scoped) binding */
 #define PND_CONST               0x02    /* const binding (orthogonal to let) */
 #define PND_ASSIGNED            0x04    /* set if ever LHS of assignment */
-#define PND_BLOCKCHILD          0x08    /* use or def is direct block child */
 #define PND_PLACEHOLDER         0x10    /* placeholder definition for lexdep */
 #define PND_BOUND               0x20    /* bound to a stack or global slot */
 #define PND_DEOPTIMIZED         0x40    /* former pn_used name node, pn_lexdef
                                            still valid, but this use no longer
                                            optimizable via an upvar opcode */
 #define PND_CLOSED              0x80    /* variable is closed over */
 #define PND_DEFAULT            0x100    /* definition is an arg with a default */
 #define PND_IMPLICITARGUMENTS  0x200    /* the definition is a placeholder for
@@ -708,17 +707,16 @@ class ParseNode
         }
         return NULL;
     }
 
     inline bool test(unsigned flag) const;
 
     bool isLet() const          { return test(PND_LET); }
     bool isConst() const        { return test(PND_CONST); }
-    bool isBlockChild() const   { return test(PND_BLOCKCHILD); }
     bool isPlaceholder() const  { return test(PND_PLACEHOLDER); }
     bool isDeoptimized() const  { return test(PND_DEOPTIMIZED); }
     bool isAssigned() const     { return test(PND_ASSIGNED); }
     bool isClosed() const       { return test(PND_CLOSED); }
     bool isBound() const        { return test(PND_BOUND); }
     bool isImplicitArguments() const { return test(PND_IMPLICITARGUMENTS); }
 
     /* True if pn is a parsenode representing a literal constant. */
@@ -971,31 +969,26 @@ struct CodeNode : public ParseNode
         return node.isArity(PN_CODE);
     }
 
 #ifdef DEBUG
     void dump(int indent);
 #endif
 };
 
-enum InBlockBool {
-    NotInBlock = false,
-    InBlock = true
-};
-
 struct NameNode : public ParseNode
 {
-    NameNode(ParseNodeKind kind, JSOp op, JSAtom *atom, InBlockBool inBlock, uint32_t blockid,
+    NameNode(ParseNodeKind kind, JSOp op, JSAtom *atom, uint32_t blockid,
              const TokenPos &pos)
       : ParseNode(kind, op, PN_NAME, pos)
     {
         pn_atom = atom;
         pn_expr = NULL;
         pn_cookie.makeFree();
-        pn_dflags = inBlock ? PND_BLOCKCHILD : 0;
+        pn_dflags = 0;
         pn_blockid = blockid;
         JS_ASSERT(pn_blockid == blockid);  // check for bitfield overflow
     }
 
     static bool test(const ParseNode &node) {
         return node.isArity(PN_NAME);
     }
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -777,51 +777,59 @@ Parser<ParseHandler>::checkFinalReturn(N
 {
     JS_ASSERT(pc->sc->isFunctionBox());
     return HasFinalReturn(pn) == ENDS_IN_RETURN ||
            reportBadReturn(pn, ParseExtraWarning,
                            JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
 }
 
 /*
- * Check that it is permitted to assign to lhs.  Strict mode code may not
- * assign to 'eval' or 'arguments'.
+ * Check that assigning to lhs is permitted.  Assigning to 'eval' or
+ * 'arguments' is banned in strict mode and in destructuring assignment.
  */
 template <typename ParseHandler>
 bool
-Parser<ParseHandler>::checkStrictAssignment(Node lhs)
+Parser<ParseHandler>::checkStrictAssignment(Node lhs, AssignmentFlavor flavor)
 {
-    if (!pc->sc->needStrictChecks())
+    if (!pc->sc->needStrictChecks() && flavor != KeyedDestructuringAssignment)
         return true;
 
     JSAtom *atom = handler.isName(lhs);
     if (!atom)
         return true;
 
     if (atom == context->names().eval || atom == context->names().arguments) {
         JSAutoByteString name;
-        if (!AtomToPrintableString(context, atom, &name) ||
-            !report(ParseStrictError, pc->sc->strict, lhs,
-                    JSMSG_DEPRECATED_ASSIGN, name.ptr()))
-        {
+        if (!AtomToPrintableString(context, atom, &name))
             return false;
+
+        ParseReportKind kind;
+        unsigned errnum;
+        if (pc->sc->strict || flavor != KeyedDestructuringAssignment) {
+            kind = ParseStrictError;
+            errnum = JSMSG_BAD_STRICT_ASSIGN;
+        } else {
+            kind = ParseError;
+            errnum = JSMSG_BAD_DESTRUCT_ASSIGN;
         }
+        if (!report(kind, pc->sc->strict, lhs, errnum, name.ptr()))
+            return false;
     }
     return true;
 }
 
 /*
  * Check that it is permitted to introduce a binding for atom.  Strict mode
  * forbids introducing new definitions for 'eval', 'arguments', or for any
  * strict mode reserved keyword.  Use pn for reporting error locations, or use
  * pc's token stream if pn is NULL.
  */
 template <typename ParseHandler>
 bool
-Parser<ParseHandler>::checkStrictBinding(HandlePropertyName name, Node pn)
+Parser<ParseHandler>::checkStrictBinding(PropertyName *name, Node pn)
 {
     if (!pc->sc->needStrictChecks())
         return true;
 
     if (name == context->names().eval || name == context->names().arguments || IsKeyword(name)) {
         JSAutoByteString bytes;
         if (!AtomToPrintableString(context, name, &bytes))
             return false;
@@ -1249,17 +1257,17 @@ MatchOrInsertSemicolon(TokenStream &ts)
 template <typename ParseHandler>
 typename ParseHandler::DefinitionNode
 Parser<ParseHandler>::getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom)
 {
     AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(atom);
     if (p)
         return p.value().get<ParseHandler>();
 
-    DefinitionNode dn = handler.newPlaceholder(atom, pc->inBlock(), pc->blockid(), pos());
+    DefinitionNode dn = handler.newPlaceholder(atom, pc->blockid(), pos());
     if (!dn)
         return ParseHandler::nullDefinition();
     DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn);
     if (!pc->lexdeps->add(p, atom, def))
         return ParseHandler::nullDefinition();
     return dn;
 }
 
@@ -1292,36 +1300,32 @@ ConvertDefinitionToNamedLambdaUse(TokenS
 /*
  * Beware: this function is called for functions nested in other functions or
  * global scripts but not for functions compiled through the Function
  * constructor or JSAPI. To always execute code when a function has finished
  * parsing, use Parser::functionBody.
  */
 template <>
 bool
-Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funName,
-                                        ParseContext<FullParseHandler> *outerpc,
+Parser<FullParseHandler>::leaveFunction(ParseNode *fn, ParseContext<FullParseHandler> *outerpc,
                                         FunctionSyntaxKind kind)
 {
     outerpc->blockidGen = pc->blockidGen;
 
     FunctionBox *funbox = fn->pn_funbox;
     JS_ASSERT(funbox == pc->sc->asFunctionBox());
 
-    if (!outerpc->topStmt || outerpc->topStmt->type == STMT_BLOCK)
-        fn->pn_dflags |= PND_BLOCKCHILD;
-
     /* Propagate unresolved lexical names up to outerpc->lexdeps. */
     if (pc->lexdeps->count()) {
         for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
             JSAtom *atom = r.front().key();
             Definition *dn = r.front().value().get<FullParseHandler>();
             JS_ASSERT(dn->isPlaceholder());
 
-            if (atom == funName && kind == Expression) {
+            if (atom == funbox->function()->name() && kind == Expression) {
                 if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
                     return false;
                 continue;
             }
 
             Definition *outer_dn = outerpc->decls().lookupFirst(atom);
 
             /*
@@ -1395,18 +1399,17 @@ Parser<FullParseHandler>::leaveFunction(
 
     InternalHandle<Bindings*> bindings =
         InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
     return pc->generateFunctionBindings(context, alloc, bindings);
 }
 
 template <>
 bool
-Parser<SyntaxParseHandler>::leaveFunction(Node fn, HandlePropertyName funName,
-                                          ParseContext<SyntaxParseHandler> *outerpc,
+Parser<SyntaxParseHandler>::leaveFunction(Node fn, ParseContext<SyntaxParseHandler> *outerpc,
                                           FunctionSyntaxKind kind)
 {
     outerpc->blockidGen = pc->blockidGen;
 
     FunctionBox *funbox = pc->sc->asFunctionBox();
     return addFreeVariablesFromLazyFunction(funbox->function(), outerpc);
 }
 
@@ -1905,17 +1908,17 @@ Parser<SyntaxParseHandler>::checkFunctio
     }
 
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream::Position &start,
-                                  size_t startOffset, FunctionType type, FunctionSyntaxKind kind)
+                                  FunctionType type, FunctionSyntaxKind kind)
 {
     JS_ASSERT_IF(kind == Statement, funName);
 
     /* Make a TOK_FUNCTION node. */
     Node pn = handler.newFunctionDefinition();
     if (!pn)
         return null();
 
@@ -1930,28 +1933,31 @@ Parser<ParseHandler>::functionDef(Handle
     if (!fun)
         return null();
 
     // If the outer scope is strict, immediately parse the function in strict
     // mode. Otherwise, we parse it normally. If we see a "use strict"
     // directive, we backup and reparse it as strict.
     bool initiallyStrict = pc->sc->strict;
     bool becameStrict;
-    if (!functionArgsAndBody(pn, fun, funName, startOffset, type, kind, initiallyStrict,
+    if (!functionArgsAndBody(pn, fun, type, kind, initiallyStrict,
                              &becameStrict))
     {
         if (initiallyStrict || !becameStrict || tokenStream.hadError())
             return null();
 
         // Reparse the function in strict mode.
         tokenStream.seek(start);
         if (funName && tokenStream.getToken() == TOK_ERROR)
             return null();
+
+        // functionArgsAndBody may have already set pn->pn_body before failing.
         handler.setFunctionBody(pn, null());
-        if (!functionArgsAndBody(pn, fun, funName, startOffset, type, kind, true))
+
+        if (!functionArgsAndBody(pn, fun, type, kind, true))
             return null();
     }
 
     return pn;
 }
 
 template <>
 bool
@@ -2012,17 +2018,18 @@ Parser<SyntaxParseHandler>::finishFuncti
     // still available.
 
     if (funbox->inWith)
         return abortIfSyntaxParser();
 
     size_t numFreeVariables = pc->lexdeps->count();
     size_t numInnerFunctions = pc->innerFunctions.length();
 
-    LazyScript *lazy = LazyScript::Create(context, numFreeVariables, numInnerFunctions, versionNumber(),
+    RootedFunction fun(context, funbox->function());
+    LazyScript *lazy = LazyScript::Create(context, fun, numFreeVariables, numInnerFunctions, versionNumber(),
                                           funbox->bufStart, funbox->bufEnd,
                                           funbox->startLine, funbox->startColumn);
     if (!lazy)
         return false;
 
     HeapPtrAtom *freeVariables = lazy->freeVariables();
     size_t i = 0;
     for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront())
@@ -2034,26 +2041,24 @@ Parser<SyntaxParseHandler>::finishFuncti
         innerFunctions[i].init(pc->innerFunctions[i]);
 
     if (pc->sc->strict)
         lazy->setStrict();
     if (funbox->usesArguments && funbox->usesApply)
         lazy->setUsesArgumentsAndApply();
     PropagateTransitiveParseFlags(funbox, lazy);
 
-    funbox->object->as<JSFunction>().initLazyScript(lazy);
+    fun->initLazyScript(lazy);
     return true;
 }
 
 template <>
 bool
 Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
-                                              HandlePropertyName funName,
-                                              size_t startOffset, FunctionType type,
-                                              FunctionSyntaxKind kind,
+                                              FunctionType type, FunctionSyntaxKind kind,
                                               bool strict, bool *becameStrict)
 {
     if (becameStrict)
         *becameStrict = false;
     ParseContext<FullParseHandler> *outerpc = pc;
 
     // Create box for fun->object early to protect against last-ditch GC.
     FunctionBox *funbox = newFunctionBox(fun, pc, strict);
@@ -2073,17 +2078,17 @@ Parser<FullParseHandler>::functionArgsAn
             parser->tokenStream.seek(position, tokenStream);
 
             ParseContext<SyntaxParseHandler> funpc(parser, outerpc, funbox,
                                                    outerpc->staticLevel + 1, outerpc->blockidGen);
             if (!funpc.init())
                 return false;
 
             if (!parser->functionArgsAndBodyGeneric(SyntaxParseHandler::NodeGeneric,
-                                                    fun, funName, type, kind, strict, becameStrict))
+                                                    fun, type, kind, becameStrict))
             {
                 if (parser->hadAbortedSyntaxParse()) {
                     // Try again with a full parse.
                     parser->clearAbortedSyntaxParse();
                     break;
                 }
                 return false;
             }
@@ -2106,20 +2111,20 @@ Parser<FullParseHandler>::functionArgsAn
     } while (false);
 
     // Continue doing a full parse for this inner function.
     ParseContext<FullParseHandler> funpc(this, pc, funbox,
                                          outerpc->staticLevel + 1, outerpc->blockidGen);
     if (!funpc.init())
         return false;
 
-    if (!functionArgsAndBodyGeneric(pn, fun, funName, type, kind, strict, becameStrict))
+    if (!functionArgsAndBodyGeneric(pn, fun, type, kind, becameStrict))
         return false;
 
-    if (!leaveFunction(pn, funName, outerpc, kind))
+    if (!leaveFunction(pn, outerpc, kind))
         return false;
 
     pn->pn_blockid = outerpc->blockid();
 
     /*
      * Fruit of the poisonous tree: if a closure contains a dynamic name access
      * (eval, with, etc), we consider the parent to do the same. The reason is
      * that the deoptimizing effects of dynamic name access apply equally to
@@ -2127,19 +2132,17 @@ Parser<FullParseHandler>::functionArgsAn
      */
     PropagateTransitiveParseFlags(funbox, outerpc->sc);
     return true;
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
-                                                HandlePropertyName funName,
-                                                size_t startOffset, FunctionType type,
-                                                FunctionSyntaxKind kind,
+                                                FunctionType type, FunctionSyntaxKind kind,
                                                 bool strict, bool *becameStrict)
 {
     if (becameStrict)
         *becameStrict = false;
     ParseContext<SyntaxParseHandler> *outerpc = pc;
 
     // Create box for fun->object early to protect against last-ditch GC.
     FunctionBox *funbox = newFunctionBox(fun, pc, strict);
@@ -2147,20 +2150,20 @@ Parser<SyntaxParseHandler>::functionArgs
         return false;
 
     // Initialize early for possible flags mutation via destructuringExpr.
     ParseContext<SyntaxParseHandler> funpc(this, pc, funbox,
                                            outerpc->staticLevel + 1, outerpc->blockidGen);
     if (!funpc.init())
         return false;
 
-    if (!functionArgsAndBodyGeneric(pn, fun, funName, type, kind, strict, becameStrict))
+    if (!functionArgsAndBodyGeneric(pn, fun, type, kind, becameStrict))
         return false;
 
-    if (!leaveFunction(pn, funName, outerpc, kind))
+    if (!leaveFunction(pn, outerpc, kind))
         return false;
 
     // This is a lazy function inner to another lazy function. Remember the
     // inner function so that if the outer function is eventually parsed we do
     // not need any further parsing or processing of the inner function.
     JS_ASSERT(fun->lazyScript());
     return outerpc->innerFunctions.append(fun);
 }
@@ -2178,43 +2181,39 @@ Parser<FullParseHandler>::standaloneLazy
     if (!funbox)
         return null();
     handler.setFunctionBox(pn, funbox);
 
     ParseContext<FullParseHandler> funpc(this, NULL, funbox, staticLevel, 0);
     if (!funpc.init())
         return null();
 
-    RootedPropertyName funName(context, fun->atom() ? fun->atom()->asPropertyName() : NULL);
-
-    if (!functionArgsAndBodyGeneric(pn, fun, funName, Normal, Statement, strict, NULL))
+    if (!functionArgsAndBodyGeneric(pn, fun, Normal, Statement, NULL))
         return null();
 
     if (fun->isNamedLambda()) {
-        if (AtomDefnPtr p = pc->lexdeps->lookup(funName)) {
+        if (AtomDefnPtr p = pc->lexdeps->lookup(fun->name())) {
             Definition *dn = p.value().get<FullParseHandler>();
             if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
                 return NULL;
         }
     }
 
     InternalHandle<Bindings*> bindings =
         InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
     if (!pc->generateFunctionBindings(context, alloc, bindings))
         return null();
 
     return pn;
 }
 
 template <typename ParseHandler>
 bool
-Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun,
-                                                 HandlePropertyName funName, FunctionType type,
-                                                 FunctionSyntaxKind kind,
-                                                 bool strict, bool *becameStrict)
+Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, FunctionType type,
+                                                 FunctionSyntaxKind kind, bool *becameStrict)
 {
     // Given a properly initialized parse context, try to parse an actual
     // function without concern for conversion to strict mode, use of lazy
     // parsing and such.
 
     Node prelude = null();
     bool hasRest;
     if (!functionArguments(kind, &prelude, pn, hasRest))
@@ -2260,17 +2259,17 @@ Parser<ParseHandler>::functionArgsAndBod
         if (becameStrict && pc->funBecameStrict)
             *becameStrict = true;
         return false;
     }
 
     if (!yieldGuard.empty() && !yieldGuard.ref().checkValidBody(body, JSMSG_YIELD_IN_ARROW))
         return false;
 
-    if (funName && !checkStrictBinding(funName, pn))
+    if (fun->name() && !checkStrictBinding(fun->name(), pn))
         return false;
 
 #if JS_HAS_EXPR_CLOSURES
     if (bodyType == StatementListBody) {
 #endif
         if (!tokenStream.matchToken(TOK_RC)) {
             report(ParseError, false, null(), JSMSG_CURLY_AFTER_BODY);
             return false;
@@ -2351,32 +2350,32 @@ Parser<ParseHandler>::functionStmt()
         return null();
     }
 
     /* We forbid function statements in strict mode code. */
     if (!pc->atBodyLevel() && pc->sc->needStrictChecks() &&
         !report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT))
         return null();
 
-    return functionDef(name, start, tokenStream.positionToOffset(start), Normal, Statement);
+    return functionDef(name, start, Normal, Statement);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionExpr()
 {
     RootedPropertyName name(context);
     JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION);
     TokenStream::Position start(keepAtoms);
     tokenStream.tell(&start);
     if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME)
         name = tokenStream.currentToken().name();
     else
         tokenStream.ungetToken();
-    return functionDef(name, start, tokenStream.positionToOffset(start), Normal, Expression);
+    return functionDef(name, start, Normal, Expression);
 }
 
 /*
  * Return true if this node, known to be an unparenthesized string literal,
  * could be the string of a directive in a Directive Prologue. Directive
  * strings never contain escape sequences or line continuations.
  * isEscapeFreeStringLiteral, below, checks whether the node itself could be
  * a directive.
@@ -2915,67 +2914,16 @@ Parser<FullParseHandler>::bindDestructur
     if (data->op == JSOP_DEFCONST)
         pn->pn_dflags |= PND_CONST;
 
     pn->markAsAssigned();
     return true;
 }
 
 /*
- * Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
- * LHS expression except a destructuring initialiser, and R is on the stack.
- * Because R is already evaluated, the usual LHS-specialized bytecodes won't
- * work.  After pushing R[P] we need to evaluate Q's "reference base" QB and
- * then push its property name QN.  At this point the stack looks like
- *
- *   [... R, R[P], QB, QN]
- *
- * We need to set QB[QN] = R[P].  This is a job for JSOP_ENUMELEM, which takes
- * its operands with left-hand side above right-hand side:
- *
- *   [rval, lval, xval]
- *
- * and pops all three values, setting lval[xval] = rval.  But we cannot select
- * JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
- * which can be optimized further.  So we select JSOP_SETNAME.
- */
-template <>
-bool
-Parser<FullParseHandler>::bindDestructuringLHS(ParseNode *pn)
-{
-    switch (pn->getKind()) {
-      case PNK_NAME:
-        pn->markAsAssigned();
-        /* FALL THROUGH */
-
-      case PNK_DOT:
-      case PNK_ELEM:
-        /*
-         * We may be called on a name node that has already been specialized,
-         * in the very weird and ECMA-262-required "for (var [x] = i in o) ..."
-         * case. See bug 558633.
-         */
-        if (!(js_CodeSpec[pn->getOp()].format & JOF_SET))
-            pn->setOp(JSOP_SETNAME);
-        break;
-
-      case PNK_CALL:
-        if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS))
-            return false;
-        break;
-
-      default:
-        report(ParseError, false, pn, JSMSG_BAD_LEFTSIDE_OF_ASS);
-        return false;
-    }
-
-    return true;
-}
-
-/*
  * Destructuring patterns can appear in two kinds of contexts:
  *
  * - assignment-like: assignment expressions and |for| loop heads.  In
  *   these cases, the patterns' property value positions can be
  *   arbitrary lvalue expressions; the destructuring is just a fancy
  *   assignment.
  *
  * - declaration-like: |var| and |let| declarations, functions' formal
@@ -3033,17 +2981,17 @@ Parser<FullParseHandler>::checkDestructu
                 } else {
                     if (data) {
                         if (!pn->isKind(PNK_NAME)) {
                             report(ParseError, false, pn, JSMSG_NO_VARIABLE_NAME);
                             return false;
                         }
                         ok = bindDestructuringVar(data, pn);
                     } else {
-                        ok = bindDestructuringLHS(pn);
+                        ok = checkAndMarkAsAssignmentLhs(pn, KeyedDestructuringAssignment);
                     }
                 }
                 if (!ok)
                     return false;
             }
         }
     } else {
         JS_ASSERT(left->isKind(PNK_OBJECT));
@@ -3067,17 +3015,17 @@ Parser<FullParseHandler>::checkDestructu
                  * officially linked to its def or registered in lexdeps. Do
                  * that now.
                  */
                 if (pair->pn_right == pair->pn_left) {
                     RootedPropertyName name(context, pn->pn_atom->asPropertyName());
                     if (!noteNameUse(name, pn))
                         return false;
                 }
-                ok = bindDestructuringLHS(pn);
+                ok = checkAndMarkAsAssignmentLhs(pn, KeyedDestructuringAssignment);
             }
             if (!ok)
                 return false;
         }
     }
 
     /*
      * The catch/finally handler implementation in the interpreter assumes
@@ -3949,17 +3897,17 @@ Parser<FullParseHandler>::forStatement()
 #endif
             }
         } else {
             /* Not a declaration. */
             JS_ASSERT(!blockObj);
             pn2 = pn1;
             pn1 = NULL;
 
-            if (!setAssignmentLhsOps(pn2, true))
+            if (!checkAndMarkAsAssignmentLhs(pn2, PlainAssignment))
                 return null();
         }
 
         pn3 = expr();
         if (!pn3)
             return null();
 
         if (blockObj) {
@@ -4174,17 +4122,17 @@ Parser<SyntaxParseHandler>::forStatement
             return null();
         }
 
         if (!simpleForDecl) {
             JS_ALWAYS_FALSE(abortIfSyntaxParser());
             return null();
         }
 
-        if (!isForDecl && !setAssignmentLhsOps(lhsNode, true))
+        if (!isForDecl && !checkAndMarkAsAssignmentLhs(lhsNode, PlainAssignment))
             return null();
 
         if (!expr())
             return null();
     } else {
         /* Parse the loop condition or null. */
         MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
         if (tokenStream.peekToken(TSF_OPERAND) != TOK_SEMI) {
@@ -5081,65 +5029,76 @@ Parser<ParseHandler>::condExpr1()
         return null();
 
     tokenStream.getToken(); /* read one token past the end */
     return handler.newConditional(condition, thenExpr, elseExpr);
 }
 
 template <>
 bool
-Parser<FullParseHandler>::setAssignmentLhsOps(ParseNode *pn, bool isPlainAssignment)
+Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode *pn, AssignmentFlavor flavor)
 {
     switch (pn->getKind()) {
       case PNK_NAME:
-        if (!checkStrictAssignment(pn))
+        if (!checkStrictAssignment(pn, flavor))
             return false;
-        pn->setOp(pn->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
+        if (flavor == KeyedDestructuringAssignment) {
+            /*
+             * We may be called on a name node that has already been
+             * specialized, in the very weird "for (var [x] = i in o) ..."
+             * case. See bug 558633.
+             */
+            if (!(js_CodeSpec[pn->getOp()].format & JOF_SET))
+                pn->setOp(JSOP_SETNAME);
+        } else {
+            pn->setOp(pn->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
+        }
         pn->markAsAssigned();
         break;
+
       case PNK_DOT:
-        pn->setOp(JSOP_SETPROP);
+      case PNK_ELEM:
         break;
-      case PNK_ELEM:
-        pn->setOp(JSOP_SETELEM);
-        break;
+
 #if JS_HAS_DESTRUCTURING
       case PNK_ARRAY:
       case PNK_OBJECT:
-        if (!isPlainAssignment) {
+        if (flavor == CompoundAssignment) {
             report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS);
             return false;
         }
         if (!checkDestructuring(NULL, pn))
             return false;
         break;
 #endif
+
       case PNK_CALL:
         if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS))
             return false;
         break;
+
       default:
-        report(ParseError, false, null(), JSMSG_BAD_LEFTSIDE_OF_ASS);
+        report(ParseError, false, pn, JSMSG_BAD_LEFTSIDE_OF_ASS);
         return false;
     }
     return true;
 }
 
 template <>
 bool
-Parser<SyntaxParseHandler>::setAssignmentLhsOps(Node pn, bool isPlainAssignment)
+Parser<SyntaxParseHandler>::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor)
 {
     /* Full syntax checking of valid assignment LHS terms requires a parse tree. */
     if (pn != SyntaxParseHandler::NodeName &&
         pn != SyntaxParseHandler::NodeGetProp &&
         pn != SyntaxParseHandler::NodeLValue)
     {
         return abortIfSyntaxParser();
     }
-    return checkStrictAssignment(pn);
+    return checkStrictAssignment(pn, flavor);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::assignExpr()
 {
     JS_CHECK_RECURSION(context, return null());
 
@@ -5197,91 +5156,80 @@ Parser<ParseHandler>::assignExpr()
 
       case TOK_ARROW: {
         tokenStream.seek(start);
         if (!abortIfSyntaxParser())
             return null();
 
         if (tokenStream.getToken() == TOK_ERROR)
             return null();
-        size_t offset = pos().begin;
         tokenStream.ungetToken();
 
-        return functionDef(NullPtr(), start, offset, Normal, Arrow);
+        return functionDef(NullPtr(), start, Normal, Arrow);
       }
 
       default:
         JS_ASSERT(!tokenStream.isCurrentTokenAssignment());
         tokenStream.ungetToken();
         return lhs;
     }
 
-    if (!setAssignmentLhsOps(lhs, kind == PNK_ASSIGN))
+    AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment;
+    if (!checkAndMarkAsAssignmentLhs(lhs, flavor))
         return null();
 
     Node rhs = assignExpr();
     if (!rhs)
         return null();
 
     return handler.newBinaryOrAppend(kind, lhs, rhs, pc, op);
 }
 
-template <> bool
-Parser<FullParseHandler>::setLvalKid(ParseNode *pn, ParseNode *kid, const char *name)
-{
-    if (!kid->isKind(PNK_NAME) &&
-        !kid->isKind(PNK_DOT) &&
-        (!kid->isKind(PNK_CALL) ||
-         (!kid->isOp(JSOP_CALL) && !kid->isOp(JSOP_EVAL) &&
-          !kid->isOp(JSOP_FUNCALL) && !kid->isOp(JSOP_FUNAPPLY))) &&
-        !kid->isKind(PNK_ELEM))
-    {
-        report(ParseError, false, null(), JSMSG_BAD_OPERAND, name);
-        return false;
-    }
-    if (!checkStrictAssignment(kid))
-        return false;
-    pn->pn_kid = kid;
-    return true;
-}
-
 static const char incop_name_str[][10] = {"increment", "decrement"};
 
 template <>
 bool
-Parser<FullParseHandler>::setIncOpKid(ParseNode *pn, ParseNode *kid, TokenKind tt, bool preorder)
+Parser<FullParseHandler>::checkAndMarkAsIncOperand(ParseNode *kid, TokenKind tt, bool preorder)
 {
-    if (!setLvalKid(pn, kid, incop_name_str[tt == TOK_DEC]))
+    // Check.
+    if (!kid->isKind(PNK_NAME) &&
+        !kid->isKind(PNK_DOT) &&
+        !kid->isKind(PNK_ELEM) &&
+        !(kid->isKind(PNK_CALL) &&
+          (kid->isOp(JSOP_CALL) ||
+           kid->isOp(JSOP_EVAL) ||
+           kid->isOp(JSOP_FUNCALL) ||
+           kid->isOp(JSOP_FUNAPPLY))))
+    {
+        report(ParseError, false, null(), JSMSG_BAD_OPERAND, incop_name_str[tt == TOK_DEC]);
         return false;
-
-    switch (kid->getKind()) {
-      case PNK_NAME:
+    }
+
+    if (!checkStrictAssignment(kid, IncDecAssignment))
+        return false;
+
+    // Mark.
+    if (kid->isKind(PNK_NAME)) {
         kid->markAsAssigned();
-        break;
-
-      case PNK_CALL:
+    } else if (kid->isKind(PNK_CALL)) {
         if (!makeSetCall(kid, JSMSG_BAD_INCOP_OPERAND))
             return false;
-        break;
-
-      case PNK_DOT:
-      case PNK_ELEM:
-        break;
-
-      default:
-        JS_ASSERT(0);
     }
     return true;
 }
 
 template <>
 bool
-Parser<SyntaxParseHandler>::setIncOpKid(Node pn, Node kid, TokenKind tt, bool preorder)
+Parser<SyntaxParseHandler>::checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder)
 {
-    return setAssignmentLhsOps(kid, false);
+    // To the extent of what we support in syntax-parse mode, the rules for
+    // inc/dec operands are the same as for assignment. There are differences,
+    // such as destructuring; but if we hit any of those cases, we'll abort and
+    // reparse in full mode.
+    return checkAndMarkAsAssignmentLhs(kid, IncDecAssignment);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin)
 {
     Node kid = unaryExpr();
     if (!kid)
@@ -5315,25 +5263,22 @@ Parser<ParseHandler>::unaryExpr()
 
       case TOK_INC:
       case TOK_DEC:
       {
         TokenKind tt2 = tokenStream.getToken(TSF_OPERAND);
         pn2 = memberExpr(tt2, true);
         if (!pn2)
             return null();
-        pn = handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
-                              JSOP_NOP,
-                              begin,
-                              pn2);
-        if (!pn)
+        if (!checkAndMarkAsIncOperand(pn2, tt, true))
             return null();
-        if (!setIncOpKid(pn, pn2, tt, true))
-            return null();
-        break;
+        return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
+                                JSOP_NOP,
+                                begin,
+                                pn2);
       }
 
       case TOK_DELETE: {
         Node expr = unaryExpr();
         if (!expr)
             return null();
 
         // Per spec, deleting any unary expression is valid -- it simply
@@ -5354,29 +5299,26 @@ Parser<ParseHandler>::unaryExpr()
         pn = memberExpr(tt, true);
         if (!pn)
             return null();
 
         /* Don't look across a newline boundary for a postfix incop. */
         tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
         if (tt == TOK_INC || tt == TOK_DEC) {
             tokenStream.consumeKnownToken(tt);
-            pn2 = handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT,
-                                   JSOP_NOP,
-                                   begin,
-                                   pn);
-            if (!pn2)
+            if (!checkAndMarkAsIncOperand(pn, tt, false))
                 return null();
-            if (!setIncOpKid(pn2, pn, tt, false))
-                return null();
-            pn = pn2;
+            return handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT,
+                                    JSOP_NOP,
+                                    begin,
+                                    pn);
         }
-        break;
+        return pn;
     }
-    return pn;
+    MOZ_ASSUME_UNREACHABLE("unaryExpr");
 }
 
 /*
  * A dedicated helper for transplanting the comprehension expression E in
  *
  *   [E for (V in I)]   // array comprehension
  *   (E for (V in I))   // generator expression
  *
@@ -5640,18 +5582,18 @@ CompExprTransplanter::transplant(ParseNo
                     /*
                      * The variable originally appeared to be a use of a
                      * definition or placeholder outside the generator, but now
                      * we know it is scoped within the comprehension tail's
                      * clauses. Make it (along with any other uses within the
                      * generator) a use of a new placeholder in the generator's
                      * lexdeps.
                      */
-                    Definition *dn2 = parser->handler.newPlaceholder(
-                        atom, parser->pc->inBlock(), parser->pc->blockid(), parser->pos());
+                    Definition *dn2 = parser->handler.newPlaceholder(atom, parser->pc->blockid(),
+                                                                     parser->pos());
                     if (!dn2)
                         return false;
                     dn2->pn_pos = root->pn_pos;
 
                     /*
                      * Change all uses of |dn| that lie within the generator's
                      * |yield| expression into uses of dn2.
                      */
@@ -6069,18 +6011,17 @@ Parser<FullParseHandler>::generatorExpr(
         ParseNode *body = comprehensionTail(pn, outerpc->blockid(), true, outerpc);
         if (!body)
             return null();
         JS_ASSERT(!genfn->pn_body);
         genfn->pn_body = body;
         genfn->pn_pos.begin = body->pn_pos.begin = kid->pn_pos.begin;
         genfn->pn_pos.end = body->pn_pos.end = pos().end;
 
-        RootedPropertyName funName(context);
-        if (!leaveFunction(genfn, funName, outerpc))
+        if (!leaveFunction(genfn, outerpc))
             return null();
     }
 
     /*
      * Our result is a call expression that invokes the anonymous generator
      * function object.
      */
     ParseNode *result = ListNode::create(PNK_GENEXP, &handler);
@@ -6267,35 +6208,19 @@ Parser<ParseHandler>::memberExpr(TokenKi
     }
     if (tt == TOK_ERROR)
         return null();
     return lhs;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::bracketedExpr()
-{
-    /*
-     * Always accept the 'in' operator in a parenthesized expression,
-     * where it's unambiguous, even if we might be parsing the init of a
-     * for statement.
-     */
-    bool oldParsingForInit = pc->parsingForInit;
-    pc->parsingForInit = false;
-    Node pn = expr();
-    pc->parsingForInit = oldParsingForInit;
-    return pn;
-}
-
-template <typename ParseHandler>
-typename ParseHandler::Node
 Parser<ParseHandler>::newName(PropertyName *name)
 {
-    return handler.newName(name, pc->inBlock(), pc->blockid(), pos());
+    return handler.newName(name, pc->blockid(), pos());
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::identifierName()
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
 
@@ -6568,18 +6493,17 @@ Parser<ParseHandler>::primaryExpr(TokenK
                     JS_ASSERT(op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER);
 
                     handler.setListFlag(pn, PNX_NONCONST);
 
                     /* NB: Getter function in { get x(){} } is unnamed. */
                     Rooted<PropertyName*> funName(context, NULL);
                     TokenStream::Position start(keepAtoms);
                     tokenStream.tell(&start);
-                    pn2 = functionDef(funName, start, tokenStream.positionToOffset(start),
-                                      op == JSOP_INITPROP_GETTER ? Getter : Setter,
+                    pn2 = functionDef(funName, start, op == JSOP_INITPROP_GETTER ? Getter : Setter,
                                       Expression);
                     if (!pn2)
                         return null();
                     pn2 = handler.newBinary(PNK_COLON, pn3, pn2, op);
                     goto skip;
                 }
               case TOK_STRING: {
                 atom = tokenStream.currentToken().atom();
@@ -6795,17 +6719,26 @@ Parser<ParseHandler>::parenExpr(bool *ge
     JS_ASSERT(tokenStream.currentToken().type == TOK_LP);
     uint32_t begin = pos().begin;
 
     if (genexp)
         *genexp = false;
 
     GenexpGuard<ParseHandler> guard(this);
 
-    Node pn = bracketedExpr();
+    /*
+     * Always accept the 'in' operator in a parenthesized expression,
+     * where it's unambiguous, even if we might be parsing the init of a
+     * for statement.
+     */
+    bool oldParsingForInit = pc->parsingForInit;
+    pc->parsingForInit = false;
+    Node pn = expr();
+    pc->parsingForInit = oldParsingForInit;
+
     if (!pn)
         return null();
     guard.endBody();
 
 #if JS_HAS_GENERATOR_EXPRS
     if (tokenStream.matchToken(TOK_FOR)) {
         if (!guard.checkValidBody(pn))
             return null();
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -267,17 +267,16 @@ struct ParseContext : public GenericPars
     inline bool init()
 {
     if (!frontend::GenerateBlockId(this, this->bodyid))
         return false;
 
     return decls_.init() && lexdeps.ensureMap(sc->context);
 }
 
-    InBlockBool inBlock() const { return InBlockBool(!topStmt || topStmt->type == STMT_BLOCK); }
     unsigned blockid() { return topStmt ? topStmt->blockid : bodyid; }
 
     // True if we are at the topmost level of a entire script or function body.
     // For example, while parsing this code we would encounter f1 and f2 at
     // body level, but we would not encounter f3 or f4 at body level:
     //
     //   function f1() { function f2() { } }
     //   if (cond) { function f3() { if (cond) { function f4() { } } } }
@@ -411,19 +410,18 @@ class Parser : private AutoGCRooter, pub
 
     /*
      * Parse a function body.  Pass StatementListBody if the body is a list of
      * statements; pass ExpressionBody if the body is a single expression.
      */
     enum FunctionBodyType { StatementListBody, ExpressionBody };
     Node functionBody(FunctionSyntaxKind kind, FunctionBodyType type);
 
-    bool functionArgsAndBodyGeneric(Node pn, HandleFunction fun,
-                                    HandlePropertyName funName, FunctionType type,
-                                    FunctionSyntaxKind kind, bool strict, bool *becameStrict);
+    bool functionArgsAndBodyGeneric(Node pn, HandleFunction fun, FunctionType type,
+                                    FunctionSyntaxKind kind, bool *becameStrict);
 
     virtual bool strictMode() { return pc->sc->strict; }
 
     const CompileOptions &options() const {
         return tokenStream.options();
     }
 
   private:
@@ -481,60 +479,65 @@ class Parser : private AutoGCRooter, pub
     Node parenExpr(bool *genexp = NULL);
 
     /*
      * Additional JS parsers.
      */
     bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest);
 
     Node functionDef(HandlePropertyName name, const TokenStream::Position &start,
-                     size_t startOffset, FunctionType type, FunctionSyntaxKind kind);
-    bool functionArgsAndBody(Node pn, HandleFunction fun, HandlePropertyName funName,
-                             size_t startOffset, FunctionType type, FunctionSyntaxKind kind,
+                     FunctionType type, FunctionSyntaxKind kind);
+    bool functionArgsAndBody(Node pn, HandleFunction fun,
+                             FunctionType type, FunctionSyntaxKind kind,
                              bool strict, bool *becameStrict = NULL);
 
     Node unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin);
 
     Node condition();
     Node comprehensionTail(Node kid, unsigned blockid, bool isGenexp,
                            ParseContext<ParseHandler> *outerpc,
                            ParseNodeKind kind = PNK_SEMI, JSOp op = JSOP_NOP);
     bool arrayInitializerComprehensionTail(Node pn);
     Node generatorExpr(Node kid);
     bool argumentList(Node listNode);
-    Node bracketedExpr();
     Node letBlock(LetContext letContext);
     Node destructuringExpr(BindData<ParseHandler> *data, TokenKind tt);
 
     Node identifierName();
 
     bool allowsForEachIn() {
 #if !JS_HAS_FOR_EACH_IN
         return false;
 #else
         return versionNumber() >= JSVERSION_1_6;
 #endif
     }
 
-    bool setAssignmentLhsOps(Node pn, bool isPlainAssignment);
+    enum AssignmentFlavor {
+        PlainAssignment,
+        CompoundAssignment,
+        KeyedDestructuringAssignment,
+        IncDecAssignment
+    };
+
+    bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor);
     bool matchInOrOf(bool *isForOfp);
 
     bool checkFunctionArguments();
     bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom);
     bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind,
                                  bool *pbodyProcessed);
     bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body);
     bool addFreeVariablesFromLazyFunction(JSFunction *fun, ParseContext<ParseHandler> *pc);
 
     bool isValidForStatementLHS(Node pn1, JSVersion version,
                                 bool forDecl, bool forEach, bool forOf);
-    bool setLvalKid(Node pn, Node kid, const char *name);
-    bool setIncOpKid(Node pn, Node kid, TokenKind tt, bool preorder);
-    bool checkStrictAssignment(Node lhs);
-    bool checkStrictBinding(HandlePropertyName name, Node pn);
+    bool checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder);
+    bool checkStrictAssignment(Node lhs, AssignmentFlavor flavor);
+    bool checkStrictBinding(PropertyName *name, Node pn);
     bool defineArg(Node funcpn, HandlePropertyName name,
                    bool disallowDuplicateArgs = false, Node *duplicatedArg = NULL);
     Node pushLexicalScope(StmtInfoPC *stmt);
     Node pushLexicalScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC *stmt);
     Node pushLetScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC *stmt);
     bool noteNameUse(HandlePropertyName name, Node pn);
     Node newRegExp();
     Node newBindingNode(PropertyName *name, bool functionScope, VarContext varContext = HoistVars);
@@ -563,36 +566,35 @@ class Parser : private AutoGCRooter, pub
 
     static Node null() { return ParseHandler::null(); }
 
     bool reportRedeclaration(Node pn, bool isConst, JSAtom *atom);
     bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum);
     bool checkFinalReturn(Node pn);
     DefinitionNode getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom);
 
-    bool leaveFunction(Node fn, HandlePropertyName funName,
-                       ParseContext<ParseHandler> *outerpc,
+    bool leaveFunction(Node fn, ParseContext<ParseHandler> *outerpc,
                        FunctionSyntaxKind kind = Expression);
 
     TokenPos pos() const { return tokenStream.currentToken().pos; }
 
     friend class CompExprTransplanter;
     friend class GenexpGuard<ParseHandler>;
     friend struct BindData<ParseHandler>;
 };
 
 /* Declare some required template specializations. */
 
 template <>
 bool
-Parser<FullParseHandler>::setAssignmentLhsOps(ParseNode *pn, bool isPlainAssignment);
+Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode *pn, AssignmentFlavor flavor);
 
 template <>
 bool
-Parser<SyntaxParseHandler>::setAssignmentLhsOps(Node pn, bool isPlainAssignment);
+Parser<SyntaxParseHandler>::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor);
 
 } /* namespace frontend */
 } /* namespace js */
 
 /*
  * Convenience macro to access Parser.tokenStream as a pointer.
  */
 #define TS(p) (&(p)->tokenStream)
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -48,24 +48,22 @@ class SyntaxParseHandler
       : lastAtom(NULL),
         tokenStream(tokenStream)
     {}
 
     static Node null() { return NodeFailure; }
 
     void trace(JSTracer *trc) {}
 
-    Node newName(PropertyName *name, InBlockBool inBlock, uint32_t blockid, const TokenPos &pos) {
+    Node newName(PropertyName *name, uint32_t blockid, const TokenPos &pos) {
         lastAtom = name;
         return NodeName;
     }
 
-    DefinitionNode newPlaceholder(JSAtom *atom, InBlockBool inBlock, uint32_t blockid,
-                                  const TokenPos &pos)
-    {
+    DefinitionNode newPlaceholder(JSAtom *atom, uint32_t blockid, const TokenPos &pos) {
         return Definition::PLACEHOLDER;
     }
 
     Node newIdentifier(JSAtom *atom, const TokenPos &pos) { return NodeString; }
     Node newNumber(double value, DecimalPoint decimalPoint, const TokenPos &pos) { return NodeGeneric; }
     Node newBooleanLiteral(bool cond, const TokenPos &pos) { return NodeGeneric; }
 
     Node newStringLiteral(JSAtom *atom, const TokenPos &pos) {
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -127,16 +127,21 @@ class EncapsulatedPtr
 
   public:
     EncapsulatedPtr() : value(NULL) {}
     EncapsulatedPtr(T *v) : value(v) {}
     explicit EncapsulatedPtr(const EncapsulatedPtr<T> &v) : value(v.value) {}
 
     ~EncapsulatedPtr() { pre(); }
 
+    void init(T *v) {
+        JS_ASSERT(!IsPoisonedPtr<T>(v));
+        this->value = v;
+    }
+
     /* Use to set the pointer to NULL. */
     void clear() {
         pre();
         value = NULL;
     }
 
     EncapsulatedPtr<T, Unioned> &operator=(T *v) {
         pre();
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/Assertions.h"
 
 #include "vm/ForkJoin.h"
 
 #include "vm/ObjectImpl-inl.h"
 
 using namespace js;
 using namespace js::gc;
+using mozilla::ReentrancyGuard;
 
 /*** SlotEdge ***/
 
 JS_ALWAYS_INLINE HeapSlot *
 StoreBuffer::SlotEdge::slotLocation() const
 {
     if (kind == HeapSlot::Element) {
         if (offset >= object->getDenseInitializedLength())
@@ -142,16 +143,17 @@ StoreBuffer::MonoTypeBuffer<T>::compact(
     compactNotInSet(owner->runtime->gcNursery);
     compactRemoveDuplicates();
 }
 
 template <typename T>
 void
 StoreBuffer::MonoTypeBuffer<T>::mark(JSTracer *trc)
 {
+    ReentrancyGuard g(*this);
     compact();
     T *cursor = base;
     while (cursor != pos) {
         T edge = *cursor++;
 
         if (edge.isNullEdge())
             continue;
 
@@ -235,16 +237,18 @@ void
 StoreBuffer::GenericBuffer::clear()
 {
     pos = base;
 }
 
 void
 StoreBuffer::GenericBuffer::mark(JSTracer *trc)
 {
+    ReentrancyGuard g(*this);
+
     uint8_t *p = base;
     while (p < pos) {
         unsigned size = *((unsigned *)p);
         p += sizeof(unsigned);
 
         BufferableRef *edge = reinterpret_cast<BufferableRef *>(p);
         edge->mark(trc);
 
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -8,16 +8,18 @@
 #define gc_StoreBuffer_h
 
 #ifdef JSGC_GENERATIONAL
 
 #ifndef JSGC_USE_EXACT_ROOTING
 # error "Generational GC requires exact rooting."
 #endif
 
+#include "mozilla/ReentrancyGuard.h"
+
 #include "jsalloc.h"
 #include "jsgc.h"
 #include "jsobj.h"
 
 #include "gc/Nursery.h"
 
 namespace js {
 namespace gc {
@@ -72,16 +74,17 @@ class StoreBuffer
      * This buffer holds only a single type of edge. Using this buffer is more
      * efficient than the generic buffer when many writes will be to the same
      * type of edge: e.g. Value or Cell*.
      */
     template<typename T>
     class MonoTypeBuffer
     {
         friend class StoreBuffer;
+        friend class mozilla::ReentrancyGuard;
 
         StoreBuffer *owner;
 
         T *base;      /* Pointer to the start of the buffer. */
         T *pos;       /* Pointer to the current insertion position. */
         T *top;       /* Pointer to one element after the end. */
 
         /*
@@ -92,18 +95,20 @@ class StoreBuffer
 
         /*
          * This set stores duplicates found when compacting. We create the set
          * here, rather than local to the algorithm to avoid malloc overhead in
          * the common case.
          */
         EdgeSet duplicates;
 
+        bool entered;
+
         MonoTypeBuffer(StoreBuffer *owner)
-          : owner(owner), base(NULL), pos(NULL), top(NULL)
+          : owner(owner), base(NULL), pos(NULL), top(NULL), entered(false)
         {
             duplicates.init();
         }
 
         MonoTypeBuffer &operator=(const MonoTypeBuffer& other) MOZ_DELETE;
 
         bool enable(uint8_t *region, size_t len);
         void disable();
@@ -120,16 +125,17 @@ class StoreBuffer
         /*
          * Attempts to reduce the usage of the buffer by removing unnecessary
          * entries.
          */
         virtual void compact();
 
         /* Add one item to the buffer. */
         void put(const T &v) {
+            mozilla::ReentrancyGuard g(*this);
             JS_ASSERT(!owner->inParallelSection());
 
             /* Check if we have been enabled. */
             if (!pos)
                 return;
 
             /*
              * Note: it is sometimes valid for a put to happen in the middle of a GC,
@@ -175,38 +181,42 @@ class StoreBuffer
             JS_ASSERT(!this->owner->inParallelSection());
             MonoTypeBuffer<T>::put(v.tagged());
         }
     };
 
     class GenericBuffer
     {
         friend class StoreBuffer;
+        friend class mozilla::ReentrancyGuard;
 
         StoreBuffer *owner;
 
         uint8_t *base; /* Pointer to start of buffer. */
         uint8_t *pos;  /* Pointer to current buffer position. */
         uint8_t *top;  /* Pointer to one past the last entry. */
 
+        bool entered;
+
         GenericBuffer(StoreBuffer *owner)
-          : owner(owner)
+          : owner(owner), base(NULL), pos(NULL), top(NULL), entered(false)
         {}
 
         GenericBuffer &operator=(const GenericBuffer& other) MOZ_DELETE;
 
         bool enable(uint8_t *region, size_t len);
         void disable();
         void clear();
 
         /* Mark all generic edges. */
         void mark(JSTracer *trc);
 
         template <typename T>
         void put(const T &t) {
+            mozilla::ReentrancyGuard g(*this);
             JS_ASSERT(!owner->inParallelSection());
 
             /* Ensure T is derived from BufferableRef. */
             (void)static_cast<const BufferableRef*>(&t);
 
             /* Check if we have been enabled. */
             if (!pos)
                 return;
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -3566,16 +3566,55 @@ CodeGenerator::visitMathFunctionD(LMathF
         funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_atan_impl);
         break;
       case MMathFunction::ASin:
         funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_asin_impl);
         break;
       case MMathFunction::ACos:
         funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_acos_impl);
         break;
+      case MMathFunction::Log10:
+        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_log10_impl);
+        break;
+      case MMathFunction::Log2:
+        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_log2_impl);
+        break;
+      case MMathFunction::Log1P:
+        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_log1p_impl);
+        break;
+      case MMathFunction::ExpM1:
+        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_expm1_impl);
+        break;
+      case MMathFunction::CosH:
+        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_cosh_impl);
+        break;
+      case MMathFunction::SinH:
+        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_sinh_impl);
+        break;
+      case MMathFunction::TanH:
+        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_tanh_impl);
+        break;
+      case MMathFunction::ACosH:
+        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_acosh_impl);
+        break;
+      case MMathFunction::ASinH:
+        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_asinh_impl);
+        break;
+      case MMathFunction::ATanH:
+        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_atanh_impl);
+        break;
+      case MMathFunction::Sign:
+        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_sign_impl);
+        break;
+      case MMathFunction::Trunc:
+        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_trunc_impl);
+        break;
+      case MMathFunction::Cbrt:
+        funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_cbrt_impl);
+        break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unknown math function");
     }
 
     masm.callWithABI(funptr, MacroAssembler::DOUBLE);
     return true;
 }
 
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -7898,23 +7898,18 @@ IonBuilder::getPropTryCache(bool *emitte
     // The input value must either be an object, or we should have strong suspicions
     // that it can be safely unboxed to an object.
     if (obj->type() != MIRType_Object) {
         types::StackTypeSet *types = obj->resultTypeSet();
         if (!types || !types->objectOrSentinel())
             return true;
     }
 
-    MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag());
-    if (barrier || IsNullOrUndefined(rvalType) || accessGetter)
-        rvalType = MIRType_Value;
-
     current->pop();
     MGetPropertyCache *load = MGetPropertyCache::New(obj, name);
-    load->setResultType(rvalType);
 
     // Try to mark the cache as idempotent. We only do this if JM is enabled
     // (its ICs are used to mark property reads as likely non-idempotent) or
     // if we are compiling eagerly (to improve test coverage).
     //
     // In parallel execution, idempotency of caches is ignored, since we
     // repeat the entire ForkJoin workload if we bail out. Note that it's
     // overly restrictive to mark everything as idempotent, because we can
@@ -7939,16 +7934,31 @@ IonBuilder::getPropTryCache(bool *emitte
     current->push(load);
 
     if (load->isEffectful() && !resumeAfter(load))
         return false;
 
     if (accessGetter)
         barrier = true;
 
+    // ParallelGetPropertyIC cannot safely call TypeScript::Monitor to ensure
+    // that the observed type set contains undefined. To account for possible
+    // missing properties, which property types do not track, we must always
+    // insert a type barrier.
+    if (info().executionMode() == ParallelExecution &&
+        !types->hasType(types::Type::UndefinedType()))
+    {
+        barrier = true;
+    }
+
+    MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag());
+    if (barrier || IsNullOrUndefined(rvalType))
+        rvalType = MIRType_Value;
+    load->setResultType(rvalType);
+
     if (!pushTypeBarrier(load, types, barrier))
         return false;
 
     *emitted = true;
     return true;
 }
 
 bool
--- a/js/src/ion/MCallOptimize.cpp
+++ b/js/src/ion/MCallOptimize.cpp
@@ -67,16 +67,42 @@ IonBuilder::inlineNativeCall(CallInfo &c
     if (native == js::math_log)
         return inlineMathFunction(callInfo, MMathFunction::Log);
     if (native == js::math_atan)
         return inlineMathFunction(callInfo, MMathFunction::ATan);
     if (native == js::math_asin)
         return inlineMathFunction(callInfo, MMathFunction::ASin);
     if (native == js::math_acos)
         return inlineMathFunction(callInfo, MMathFunction::ACos);
+    if (native == js::math_log10)
+        return inlineMathFunction(callInfo, MMathFunction::Log10);
+    if (native == js::math_log2)
+        return inlineMathFunction(callInfo, MMathFunction::Log2);
+    if (native == js::math_log1p)
+        return inlineMathFunction(callInfo, MMathFunction::Log1P);
+    if (native == js::math_expm1)
+        return inlineMathFunction(callInfo, MMathFunction::ExpM1);
+    if (native == js::math_cosh)
+        return inlineMathFunction(callInfo, MMathFunction::CosH);
+    if (native == js::math_sin)
+        return inlineMathFunction(callInfo, MMathFunction::SinH);
+    if (native == js::math_tan)
+        return inlineMathFunction(callInfo, MMathFunction::TanH);
+    if (native == js::math_acosh)
+        return inlineMathFunction(callInfo, MMathFunction::ACosH);
+    if (native == js::math_asin)
+        return inlineMathFunction(callInfo, MMathFunction::ASinH);
+    if (native == js::math_atan)
+        return inlineMathFunction(callInfo, MMathFunction::ATanH);
+    if (native == js::math_sign)
+        return inlineMathFunction(callInfo, MMathFunction::Sign);
+    if (native == js::math_trunc)
+        return inlineMathFunction(callInfo, MMathFunction::Trunc);
+    if (native == js::math_cbrt)
+        return inlineMathFunction(callInfo, MMathFunction::Cbrt);
 
     // String natives.
     if (native == js_String)
         return inlineStringObject(callInfo);
     if (native == js_str_charCodeAt)
         return inlineStrCharCodeAt(callInfo);
     if (native == js::str_fromCharCode)
         return inlineStrFromCharCode(callInfo);
--- a/js/src/ion/MIR.cpp
+++ b/js/src/ion/MIR.cpp
@@ -1576,17 +1576,16 @@ MCompare::infer(JSContext *cx, BaselineI
     MIRType lhs = getOperand(0)->type();
     MIRType rhs = getOperand(1)->type();
 
     bool looseEq = jsop() == JSOP_EQ || jsop() == JSOP_NE;
     bool strictEq = jsop() == JSOP_STRICTEQ || jsop() == JSOP_STRICTNE;
     bool relationalEq = !(looseEq || strictEq);
 
     // Comparisons on unsigned integers may be treated as UInt32.
-    MDefinition *newlhs, *newrhs;
     if (tryUseUnsignedOperands()) {
         compareType_ = Compare_UInt32;
         return;
     }
 
     // Integer to integer or boolean to boolean comparisons may be treated as Int32.
     if ((lhs == MIRType_Int32 && rhs == MIRType_Int32) ||
         (lhs == MIRType_Boolean && rhs == MIRType_Boolean))
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -3201,17 +3201,30 @@ class MMathFunction
     enum Function {
         Log,
         Sin,
         Cos,
         Exp,
         Tan,
         ACos,
         ASin,
-        ATan
+        ATan,
+        Log10,
+        Log2,
+        Log1P,
+        ExpM1,
+        CosH,
+        SinH,
+        TanH,
+        ACosH,
+        ASinH,
+        ATanH,
+        Sign,
+        Trunc,
+        Cbrt
     };
 
   private:
     Function function_;
     MathCache *cache_;
 
     MMathFunction(MDefinition *input, Function function, MathCache *cache)
       : MUnaryInstruction(input), function_(function), cache_(cache)
--- a/js/src/ion/RangeAnalysis.cpp
+++ b/js/src/ion/RangeAnalysis.cpp
@@ -890,16 +890,19 @@ MUrsh::computeRange()
 void
 MAbs::computeRange()
 {
     if (specialization_ != MIRType_Int32 && specialization_ != MIRType_Double)
         return;
 
     Range other(getOperand(0));
     setRange(Range::abs(&other));
+
+    if (implicitTruncate_ && !range()->isInt32())
+        setRange(new Range(INT32_MIN, INT32_MAX));
 }
 
 void
 MMinMax::computeRange()
 {
     if (specialization_ != MIRType_Int32 && specialization_ != MIRType_Double)
         return;
 
@@ -912,27 +915,33 @@ void
 MAdd::computeRange()
 {
     if (specialization() != MIRType_Int32 && specialization() != MIRType_Double)
         return;
     Range left(getOperand(0));
     Range right(getOperand(1));
     Range *next = Range::add(&left, &right);
     setRange(next);
+
+    if (isTruncated() && !range()->isInt32())
+        setRange(new Range(INT32_MIN, INT32_MAX));
 }
 
 void
 MSub::computeRange()
 {
     if (specialization() != MIRType_Int32 && specialization() != MIRType_Double)
         return;
     Range left(getOperand(0));
     Range right(getOperand(1));
     Range *next = Range::sub(&left, &right);
     setRange(next);
+
+    if (isTruncated() && !range()->isInt32())
+        setRange(new Range(INT32_MIN, INT32_MAX));
 }
 
 void
 MMul::computeRange()
 {
     if (specialization() != MIRType_Int32 && specialization() != MIRType_Double)
         return;
     Range left(getOperand(0));
--- a/js/src/ion/arm/MacroAssembler-arm.cpp
+++ b/js/src/ion/arm/MacroAssembler-arm.cpp
@@ -336,16 +336,21 @@ NextInst(Instruction *i)
     return i->next();
 }
 
 void
 MacroAssemblerARM::ma_movPatchable(Imm32 imm_, Register dest,
                                    Assembler::Condition c, RelocStyle rs, Instruction *i)
 {
     int32_t imm = imm_.value;
+    if (i) {
+        // If the access goes through an iterator (which this doesn't) then
+        // all issues pertaining to reading guard instructions.
+        i = InstructionIterator(i).cur();
+    }
     switch(rs) {
       case L_MOVWT:
         as_movw(dest, Imm16(imm & 0xffff), c, i);
         // i can be NULL here.  that just means "insert in the next in sequence."
         // NextInst is special cased to not do anything when it is passed NULL, so two
         // consecutive instructions will be inserted.
         i = NextInst(i);
         as_movt(dest, Imm16(imm >> 16 & 0xffff), c, i);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/testBug893364.js
@@ -0,0 +1,14 @@
+function m()
+{
+    "use asm";
+    function f()
+    {
+        var x = 0;
+        var y = 0;
+        x = (((0x77777777 - 0xcccccccc) | 0) % -1) | 0;
+        y = (((0x7FFFFFFF + 0x7FFFFFFF) | 0) % -1) | 0;
+        return (x+y)|0;
+    }
+    return f;
+}
+assertEq(m()(), 0)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/testBug893368.js
@@ -0,0 +1,13 @@
+function m(stdlib)
+{
+  "use asm";
+  var abs = stdlib.Math.abs;
+  function f(p)
+  {
+    p = p|0;
+    return ((abs(p|0)|0) % 3)|0;
+  }
+  return f;
+}
+var f = m(this);
+assertEq(f(0x80000000), -2);
--- a/js/src/jit-test/tests/auto-regress/bug735936.js
+++ b/js/src/jit-test/tests/auto-regress/bug735936.js
@@ -1,7 +1,8 @@
+// |jit-test| error: SyntaxError
 // Binary: cache/js-dbg-32-7e713db43d8d-linux
 // Flags:
 //
 f = (function() {
     "use strict";
     [arguments] = eval()
 })
--- a/js/src/jit-test/tests/baseline/bug847678.js
+++ b/js/src/jit-test/tests/baseline/bug847678.js
@@ -1,7 +1,7 @@
-// |jit-test| error: TypeError
+// |jit-test| error: SyntaxError
 s = newGlobal();
 function g(c) {
     evalcx(c, s)
 }
 g("[eval]=(function(){})")
 g("while(eval());")
--- a/js/src/jit-test/tests/basic/testBug741497.js
+++ b/js/src/jit-test/tests/basic/testBug741497.js
@@ -1,2 +1,3 @@
+// |jit-test| error: SyntaxError
 "use strict";
 function inner() (([arguments, b] = this, c)());
--- a/js/src/jit-test/tests/jaeger/bug588363-1.js
+++ b/js/src/jit-test/tests/jaeger/bug588363-1.js
@@ -1,5 +1,6 @@
+// |jit-test| error: SyntaxError
 ({eval} = Object.defineProperty(evalcx("lazy"), "", {}))
 eval("eval(/x/)", [])
 
 /* Don't assert or crash. */
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/bug-889628.js
@@ -0,0 +1,38 @@
+// Destructuring assignment to eval or arguments in destructuring is a SyntaxError
+
+load(libdir + "asserts.js");
+
+var patterns = [
+    "[_]",
+    "[a, b, _]",
+    "[[_]]",
+    "[[], [{}, [_]]]",
+    "{x:_}",
+    "{x:y, z:_}",
+    "{0:_}",
+    "{_}",
+    //"[..._]"
+];
+
+// If the assertion below fails, congratulations! It means you have added
+// spread operator support to destructuring assignment. Simply uncomment the
+// "[..._]" case above. Then delete this comment and assertion.
+assertThrowsInstanceOf(() => Function("[...x] = [1]"), ReferenceError);
+
+for (var pattern of patterns) {
+    var stmt = pattern + " = obj";
+    if (stmt[0] == "{")
+        stmt = "(" + stmt + ")";
+    stmt += ";"
+
+    // stmt is a legal statement...
+    Function(stmt);
+
+    // ...but not if you replace _ with one of these two names.
+    for (var name of ["eval", "arguments"]) {
+        var s = stmt.replace("_", name);
+        assertThrowsInstanceOf(() => Function(s), SyntaxError);
+        assertThrowsInstanceOf(() => eval(s), SyntaxError);
+        assertThrowsInstanceOf(() => eval("'use strict'; " + s), SyntaxError);
+    }
+}
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -281,17 +281,17 @@ MSG_DEF(JSMSG_MISSING_FUN_ARG,        22
 MSG_DEF(JSMSG_JSON_BAD_PARSE,         228, 1, JSEXN_SYNTAXERR, "JSON.parse: {0}")
 MSG_DEF(JSMSG_JSON_BAD_STRINGIFY,     229, 0, JSEXN_ERR, "JSON.stringify")
 MSG_DEF(JSMSG_NOT_CALLABLE_OR_UNDEFINED, 230, 0, JSEXN_TYPEERR, "value is not a function or undefined")
 MSG_DEF(JSMSG_NOT_NONNULL_OBJECT,     231, 0, JSEXN_TYPEERR, "value is not a non-null object")
 MSG_DEF(JSMSG_DEPRECATED_OCTAL,       232, 0, JSEXN_SYNTAXERR, "octal literals and octal escape sequences are deprecated")
 MSG_DEF(JSMSG_STRICT_CODE_WITH,       233, 0, JSEXN_SYNTAXERR, "strict mode code may not contain 'with' statements")
 MSG_DEF(JSMSG_DUPLICATE_PROPERTY,     234, 1, JSEXN_SYNTAXERR, "property name {0} appears more than once in object literal")
 MSG_DEF(JSMSG_DEPRECATED_DELETE_OPERAND, 235, 0, JSEXN_SYNTAXERR, "applying the 'delete' operator to an unqualified name is deprecated")
-MSG_DEF(JSMSG_DEPRECATED_ASSIGN,      236, 1, JSEXN_SYNTAXERR, "assignment to {0} is deprecated")
+MSG_DEF(JSMSG_BAD_STRICT_ASSIGN,      236, 1, JSEXN_SYNTAXERR, "can't assign to {0} in strict mode")
 MSG_DEF(JSMSG_BAD_BINDING,            237, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated")
 MSG_DEF(JSMSG_INVALID_DESCRIPTOR,     238, 0, JSEXN_TYPEERR, "property descriptors must not specify a value or be writable when a getter or setter has been specified")
 MSG_DEF(JSMSG_OBJECT_NOT_EXTENSIBLE,  239, 1, JSEXN_TYPEERR, "{0} is not extensible")
 MSG_DEF(JSMSG_CANT_REDEFINE_PROP,     240, 1, JSEXN_TYPEERR, "can't redefine non-configurable property '{0}'")
 MSG_DEF(JSMSG_CANT_APPEND_TO_ARRAY,   241, 0, JSEXN_TYPEERR, "can't add elements past the end of an array if its length property is unwritable")
 MSG_DEF(JSMSG_CANT_REDEFINE_ARRAY_LENGTH,242, 0, JSEXN_TYPEERR, "can't redefine array length")
 MSG_DEF(JSMSG_CANT_DEFINE_PAST_ARRAY_LENGTH,243, 0, JSEXN_TYPEERR, "can't define array index property past the end of an array with non-writable length")
 MSG_DEF(JSMSG_TYPED_ARRAY_BAD_INDEX,  244, 0, JSEXN_ERR, "invalid or out-of-range index")
@@ -396,8 +396,9 @@ MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL,      34
 MSG_DEF(JSMSG_USE_ASM_LINK_FAIL,      343, 1, JSEXN_TYPEERR, "asm.js link error: {0}")
 MSG_DEF(JSMSG_USE_ASM_TYPE_OK,        344, 1, JSEXN_ERR,     "successfully compiled asm.js code ({0})")
 MSG_DEF(JSMSG_BAD_ARROW_ARGS,         345, 0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
 MSG_DEF(JSMSG_YIELD_IN_ARROW,         346, 0, JSEXN_SYNTAXERR, "arrow function may not contain yield")
 MSG_DEF(JSMSG_WRONG_VALUE,            347, 2, JSEXN_ERR, "expected {0} but found {1}")
 MSG_DEF(JSMSG_PAR_ARRAY_SCATTER_BAD_TARGET, 348, 1, JSEXN_ERR, "target for index {0} is not an integer")
 MSG_DEF(JSMSG_SELFHOSTED_UNBOUND_NAME,349, 0, JSEXN_TYPEERR, "self-hosted code may not contain unbound name lookups")
 MSG_DEF(JSMSG_DEPRECATED_SOURCE_MAP,  350, 0, JSEXN_SYNTAXERR, "Using //@ to indicate source map URL pragmas is deprecated. Use //# instead")
+MSG_DEF(JSMSG_BAD_DESTRUCT_ASSIGN,    351, 1, JSEXN_SYNTAXERR, "can't assign to {0} using destructuring assignment")
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1070,16 +1070,26 @@ JSFunction::createScriptForLazilyInterpr
 
         if (script) {
             fun->initScript(script);
             return true;
         }
 
         fun->initScript(NULL);
 
+        if (fun != lazy->function()) {
+            script = lazy->function()->getOrCreateScript(cx);
+            if (!script) {
+                fun->initLazyScript(lazy);
+                return false;
+            }
+            fun->initScript(script);
+            return true;
+        }
+
         // Lazy script caching is only supported for leaf functions. If a
         // script with inner functions was returned by the cache, those inner
         // functions would be delazified when deep cloning the script, even if
         // they have never executed.
         //
         // Additionally, the lazy script cache is not used during incremental
         // GCs, to avoid resurrecting dead scripts after incremental sweeping
         // has started.
@@ -1119,17 +1129,17 @@ JSFunction::createScriptForLazilyInterpr
         if (!chars) {
             fun->initLazyScript(lazy);
             return false;
         }
 
         const jschar *lazyStart = chars + lazy->begin();
         size_t lazyLength = lazy->end() - lazy->begin();
 
-        if (!frontend::CompileLazyFunction(cx, fun, lazy, lazyStart, lazyLength)) {
+        if (!frontend::CompileLazyFunction(cx, lazy, lazyStart, lazyLength)) {
             fun->initLazyScript(lazy);
             return false;
         }
 
         script = fun->nonLazyScript();
 
         // Try to insert the newly compiled script into the lazy script cache.
         if (!lazy->numInnerFunctions()) {
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -17,16 +17,17 @@
 
 #include "jslibmath.h"
 
 #include "mozilla/Constants.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 
+#include <algorithm>  // for std::max
 #include <fcntl.h>
 
 #ifdef XP_UNIX
 # include <unistd.h>
 #endif
 
 #include "jstypes.h"
 #include "prmjtime.h"
@@ -45,16 +46,17 @@ using mozilla::ExponentComponent;
 using mozilla::IsFinite;
 using mozilla::IsInfinite;
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::IsNegativeZero;
 using mozilla::PositiveInfinity;
 using mozilla::NegativeInfinity;
 using mozilla::SpecificNaN;
+using JS::ToNumber;
 
 #ifndef M_E
 #define M_E             2.7182818284590452354
 #endif
 #ifndef M_LOG2E
 #define M_LOG2E         1.4426950408889634074
 #endif
 #ifndef M_LOG10E
@@ -793,16 +795,448 @@ js::math_tan(JSContext *cx, unsigned arg
     if (!mathCache)
         return false;
 
     double z = math_tan_impl(mathCache, x);
     args.rval().setDouble(z);
     return true;
 }
 
+
+typedef double (*UnaryMathFunctionType)(MathCache *cache, double);
+
+template <UnaryMathFunctionType F>
+JSBool math_function(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() == 0) {
+        args.rval().setNumber(js_NaN);
+        return true;
+    }
+
+    double x;
+    if (!ToNumber(cx, args.handleAt(0), &x))
+        return false;
+
+    MathCache *mathCache = cx->runtime()->getMathCache(cx);
+    if (!mathCache)
+        return false;
+    double z = F(mathCache, x);
+    args.rval().setNumber(z);
+
+    return true;
+}
+
+
+
+double
+js::math_log10_impl(MathCache *cache, double x)
+{
+    return cache->lookup(log10, x);
+}
+
+JSBool
+js::math_log10(JSContext *cx, unsigned argc, Value *vp)
+{
+    return math_function<math_log10_impl>(cx, argc, vp);
+}
+
+#if !HAVE_LOG2
+double log2(double x)
+{
+    return log(x) / M_LN2;
+}
+#endif
+
+double
+js::math_log2_impl(MathCache *cache, double x)
+{
+    return cache->lookup(log2, x);
+}
+
+JSBool
+js::math_log2(JSContext *cx, unsigned argc, Value *vp)
+{
+    return math_function<math_log2_impl>(cx, argc, vp);
+}
+
+#if !HAVE_LOG1P
+double log1p(double x)
+{
+    if (fabs(x) < 1e-4) {
+        /*
+         * Use Taylor approx. log(1 + x) = x - x^2 / 2 + x^3 / 3 - x^4 / 4 with error x^5 / 5
+         * Since |x| < 10^-4, |x|^5 < 10^-20, relative error less than 10^-16
+         */
+        double z = -(x * x * x * x) / 4 + (x * x * x) / 3 - (x * x) / 2 + x;
+        return z;
+    } else {
+        /* For other large enough values of x use direct computation */
+        return log(1.0 + x);
+    }
+}
+#endif
+
+double
+js::math_log1p_impl(MathCache *cache, double x)
+{
+#ifdef __APPLE__
+    // Ensure that log1p(-0) is -0.
+    if (x == 0)
+        return x;
+#endif
+
+    return cache->lookup(log1p, x);
+}
+
+JSBool
+js::math_log1p(JSContext *cx, unsigned argc, Value *vp)
+{
+    return math_function<math_log1p_impl>(cx, argc, vp);
+}
+
+#if !HAVE_EXPM1
+double expm1(double x)
+{
+    /* Special handling for -0 */
+    if (x == 0.0)
+        return x;
+
+    if (fabs(x) < 1e-5) {
+        /*
+         * Use Taylor approx. exp(x) - 1 = x + x^2 / 2 + x^3 / 6 with error x^4 / 24
+         * Since |x| < 10^-5, |x|^4 < 10^-20, relative error less than 10^-15
+         */
+        double z = (x * x * x) / 6 + (x * x) / 2 + x;
+        return z;
+    } else {
+        /* For other large enough values of x use direct computation */
+        return exp(x) - 1.0;
+    }
+}
+#endif
+
+double
+js::math_expm1_impl(MathCache *cache, double x)
+{
+    return cache->lookup(expm1, x);
+}
+
+JSBool
+js::math_expm1(JSContext *cx, unsigned argc, Value *vp)
+{
+    return math_function<math_expm1_impl>(cx, argc, vp);
+}
+
+#if !HAVE_SQRT1PM1
+/* This algorithm computes sqrt(1+x)-1 for small x */
+double sqrt1pm1(double x)
+{
+    if (fabs(x) > 0.75)
+        return sqrt(1 + x) - 1;
+
+    return expm1(log1p(x) / 2);
+}
+#endif
+
+
+double
+js::math_cosh_impl(MathCache *cache, double x)
+{
+    return cache->lookup(cosh, x);
+}
+
+JSBool
+js::math_cosh(JSContext *cx, unsigned argc, Value *vp)
+{
+    return math_function<math_cosh_impl>(cx, argc, vp);
+}
+
+double
+js::math_sinh_impl(MathCache *cache, double x)
+{
+    return cache->lookup(sinh, x);
+}
+
+JSBool
+js::math_sinh(JSContext *cx, unsigned argc, Value *vp)
+{
+    return math_function<math_sinh_impl>(cx, argc, vp);
+}
+
+double
+js::math_tanh_impl(MathCache *cache, double x)
+{
+    return cache->lookup(tanh, x);
+}
+
+JSBool
+js::math_tanh(JSContext *cx, unsigned argc, Value *vp)
+{
+    return math_function<math_tanh_impl>(cx, argc, vp);
+}
+
+#if !HAVE_ACOSH
+double acosh(double x)
+{
+    const double SQUARE_ROOT_EPSILON = sqrt(std::numeric_limits<double>::epsilon());
+
+    if ((x - 1) >= SQUARE_ROOT_EPSILON) {
+        if (x > 1 / SQUARE_ROOT_EPSILON) {
+            /*
+             * http://functions.wolfram.com/ElementaryFunctions/ArcCosh/06/01/06/01/0001/
+             * approximation by laurent series in 1/x at 0+ order from -1 to 0
+             */
+            return log(x) + M_LN2;
+        } else if (x < 1.5) {
+            // This is just a rearrangement of the standard form below
+            // devised to minimize loss of precision when x ~ 1:
+            double y = x - 1;
+            return log1p(y + sqrt(y * y + 2 * y));
+        } else {
+            // http://functions.wolfram.com/ElementaryFunctions/ArcCosh/02/
+            return log(x + sqrt(x * x - 1));
+        }
+    } else {
+        // see http://functions.wolfram.com/ElementaryFunctions/ArcCosh/06/01/04/01/0001/
+        double y = x - 1;
+        // approximation by taylor series in y at 0 up to order 2.
+        // If x is less than 1, sqrt(2 * y) is NaN and the result is NaN.
+        return sqrt(2 * y) * (1 - y / 12 + 3 * y * y / 160);
+    }
+}
+#endif
+
+double
+js::math_acosh_impl(MathCache *cache, double x)
+{
+    return cache->lookup(acosh, x);
+}
+
+JSBool
+js::math_acosh(JSContext *cx, unsigned argc, Value *vp)
+{
+    return math_function<math_acosh_impl>(cx, argc, vp);
+}
+
+#if !HAVE_ASINH
+double asinh(double x)
+{
+    const double SQUARE_ROOT_EPSILON = sqrt(std::numeric_limits<double>::epsilon());
+    const double FOURTH_ROOT_EPSILON = sqrt(SQUARE_ROOT_EPSILON);
+
+    if (x >= FOURTH_ROOT_EPSILON) {
+        if (x > 1 / SQUARE_ROOT_EPSILON)
+            // http://functions.wolfram.com/ElementaryFunctions/ArcSinh/06/01/06/01/0001/
+            // approximation by laurent series in 1/x at 0+ order from -1 to 1
+            return M_LN2 + log(x) + 1 / (4 * x * x);
+        else if (x < 0.5)
+            return log1p(x + sqrt1pm1(x * x));
+        else
+            return log(x + sqrt(x * x + 1));
+    } else if (x <= -FOURTH_ROOT_EPSILON) {
+        return -asinh(-x);
+    } else {
+        // http://functions.wolfram.com/ElementaryFunctions/ArcSinh/06/01/03/01/0001/
+        // approximation by taylor series in x at 0 up to order 2
+        double result = x;
+
+        if (fabs(x) >= SQUARE_ROOT_EPSILON) {
+            double x3 = x * x * x;
+            // approximation by taylor series in x at 0 up to order 4
+            result -= x3 / 6;
+        }
+
+        return result;
+    }
+}
+#endif
+
+double
+js::math_asinh_impl(MathCache *cache, double x)
+{
+    return cache->lookup(asinh, x);
+}
+
+JSBool
+js::math_asinh(JSContext *cx, unsigned argc, Value *vp)
+{
+    return math_function<math_asinh_impl>(cx, argc, vp);
+}
+
+#if !HAVE_ATANH
+double atanh(double x)
+{
+    const double EPSILON = std::numeric_limits<double>::epsilon();
+    const double SQUARE_ROOT_EPSILON = sqrt(EPSILON);
+    const double FOURTH_ROOT_EPSILON = sqrt(SQUARE_ROOT_EPSILON);
+
+    if (fabs(x) >= FOURTH_ROOT_EPSILON) {
+        // http://functions.wolfram.com/ElementaryFunctions/ArcTanh/02/
+        if (fabs(x) < 0.5)
+            return (log1p(x) - log1p(-x)) / 2;
+
+        return log((1 + x) / (1 - x)) / 2;
+    } else {
+        // http://functions.wolfram.com/ElementaryFunctions/ArcTanh/06/01/03/01/
+        // approximation by taylor series in x at 0 up to order 2
+        double result = x;
+
+        if (fabs(x) >= SQUARE_ROOT_EPSILON) {
+            double x3 = x * x * x;
+            result += x3 / 3;
+        }
+
+        return result;
+    }
+}
+#endif
+
+double
+js::math_atanh_impl(MathCache *cache, double x)
+{
+    return cache->lookup(atanh, x);
+}
+
+JSBool
+js::math_atanh(JSContext *cx, unsigned argc, Value *vp)
+{
+    return math_function<math_atanh_impl>(cx, argc, vp);
+}
+
+#if !HAVE_HYPOT
+double hypot(double x, double y)
+{
+    if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y))
+        return js_PositiveInfinity;
+
+    if (mozilla::IsNaN(x) || mozilla::IsNaN(y))
+        return js_NaN;
+
+    double xabs = mozilla::Abs(x);
+    double yabs = mozilla::Abs(y);
+
+    double min = std::min(xabs, yabs);
+    double max = std::max(xabs, yabs);
+
+    if (min == 0) {
+        return max;
+    } else {
+        double u = min / max;
+        return max * sqrt(1 + u * u);
+    }
+}
+#endif
+
+double
+js::math_hypot_impl(double x, double y)
+{
+#ifdef XP_WIN
+    // On Windows, hypot(NaN, Infinity) is NaN. ES6 requires Infinity.
+    if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y))
+        return js_PositiveInfinity;
+#endif
+    return hypot(x, y);
+}
+
+JSBool
+js::math_hypot(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() < 2) {
+        args.rval().setNumber(js_NaN);
+        return true;
+    }
+
+    double x, y;
+    if (!ToNumber(cx, args.handleAt(0), &x))
+        return false;
+
+    if (!ToNumber(cx, args.handleAt(1), &y))
+        return false;
+
+    if (args.length() == 2) {
+        args.rval().setNumber(math_hypot_impl(x, y));
+        return true;
+    }
+
+    /* args.length() > 2 */
+    double z;
+    if (!ToNumber(cx, args.handleAt(2), &z)) {
+        return false;