Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 28 Oct 2014 16:10:05 -0400
changeset 212718 9fd712fd9a6cc9a407ebf9a1d36406218058e06b
parent 212717 17a2e10e38088d37b102e7242dc0072d9a313227 (current diff)
parent 212696 b1f8b6f4541b6b06617f6c2f7bedf44f6f7f6556 (diff)
child 212719 3694deb38dd491144971e1ce0a40cb32073c3ccf
push id51042
push userryanvm@gmail.com
push dateTue, 28 Oct 2014 20:25:03 +0000
treeherdermozilla-inbound@53d84829b2b8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone36.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 inbound to m-c. a=merge
dom/base/test/mochitest.ini
--- a/accessible/atk/AccessibleWrap.cpp
+++ b/accessible/atk/AccessibleWrap.cpp
@@ -595,23 +595,25 @@ finalizeCB(GObject *aObj)
     // finalize of GObjectClass will unref the accessible parent if has
     if (G_OBJECT_CLASS (parent_class)->finalize)
         G_OBJECT_CLASS (parent_class)->finalize(aObj);
 }
 
 const gchar*
 getNameCB(AtkObject* aAtkObj)
 {
+  nsAutoString name;
   AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
-  if (!accWrap)
+  if (accWrap)
+    accWrap->Name(name);
+  else if (ProxyAccessible* proxy = GetProxy(aAtkObj))
+    proxy->Name(name);
+  else
     return nullptr;
 
-  nsAutoString name;
-  accWrap->Name(name);
-
   // XXX Firing an event from here does not seem right
   MaybeFireNameChange(aAtkObj, name);
 
   return aAtkObj->name;
 }
 
 static void
 MaybeFireNameChange(AtkObject* aAtkObj, const nsString& aNewName)
@@ -637,23 +639,28 @@ MaybeFireNameChange(AtkObject* aAtkObj, 
 
   if (notify)
     g_object_notify(G_OBJECT(aAtkObj), "accessible-name");
 }
 
 const gchar *
 getDescriptionCB(AtkObject *aAtkObj)
 {
-    AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
-    if (!accWrap || accWrap->IsDefunct())
-        return nullptr;
+  nsAutoString uniDesc;
+  AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
+  if (accWrap) {
+    if (accWrap->IsDefunct())
+      return nullptr;
 
-    /* nsIAccessible is responsible for the nonnull description */
-    nsAutoString uniDesc;
     accWrap->Description(uniDesc);
+  } else if (ProxyAccessible* proxy = GetProxy(aAtkObj)) {
+    proxy->Description(uniDesc);
+  } else {
+    return nullptr;
+  }
 
     NS_ConvertUTF8toUTF16 objDesc(aAtkObj->description);
     if (!uniDesc.Equals(objDesc))
         atk_object_set_description(aAtkObj,
                                    NS_ConvertUTF16toUTF8(uniDesc).get());
 
     return aAtkObj->description;
 }
@@ -777,29 +784,31 @@ GetLocaleCB(AtkObject* aAtkObj)
   nsAutoString locale;
   accWrap->Language(locale);
   return AccessibleWrap::ReturnString(locale);
 }
 
 AtkObject *
 getParentCB(AtkObject *aAtkObj)
 {
-  if (!aAtkObj->accessible_parent) {
-    AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
-    if (!accWrap)
-      return nullptr;
+  if (aAtkObj->accessible_parent)
+    return aAtkObj->accessible_parent;
 
-    Accessible* accParent = accWrap->Parent();
-    if (!accParent)
-      return nullptr;
+  AtkObject* atkParent = nullptr;
+  if (AccessibleWrap* wrapper = GetAccessibleWrap(aAtkObj)) {
+    Accessible* parent = wrapper->Parent();
+    atkParent = parent ? AccessibleWrap::GetAtkObject(parent) : nullptr;
+  } else if (ProxyAccessible* proxy = GetProxy(aAtkObj)) {
+    ProxyAccessible* parent = proxy->Parent();
+    atkParent = parent ? GetWrapperFor(parent) : nullptr;
+  }
 
-    AtkObject* parent = AccessibleWrap::GetAtkObject(accParent);
-    if (parent)
-      atk_object_set_parent(aAtkObj, parent);
-  }
+  if (atkParent)
+    atk_object_set_parent(aAtkObj, atkParent);
+
   return aAtkObj->accessible_parent;
 }
 
 gint
 getChildCountCB(AtkObject *aAtkObj)
 {
     AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
     if (!accWrap || nsAccUtils::MustPrune(accWrap)) {
@@ -977,16 +986,22 @@ GetProxy(AtkObject* aObj)
 {
   if (!aObj || !(MAI_ATK_OBJECT(aObj)->accWrap & IS_PROXY))
     return nullptr;
 
   return reinterpret_cast<ProxyAccessible*>(MAI_ATK_OBJECT(aObj)->accWrap
       & ~IS_PROXY);
 }
 
+AtkObject*
+GetWrapperFor(ProxyAccessible* aProxy)
+{
+  return reinterpret_cast<AtkObject*>(aProxy->GetWrapper() & ~IS_PROXY);
+}
+
 static uint16_t
 GetInterfacesForProxy(ProxyAccessible* aProxy)
 {
   return MAI_INTERFACE_COMPONENT;
 }
 
 void
 a11y::ProxyCreated(ProxyAccessible* aProxy)
--- a/accessible/atk/nsMai.h
+++ b/accessible/atk/nsMai.h
@@ -31,16 +31,17 @@ class ProxyAccessible;
                                          MAI_TYPE_ATK_OBJECT))
 #define MAI_ATK_OBJECT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), \
                                          MAI_TYPE_ATK_OBJECT, \
                                          MaiAtkObjectClass))
 GType mai_atk_object_get_type(void);
 GType mai_util_get_type();
 mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
 mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj);
+AtkObject* GetWrapperFor(mozilla::a11y::ProxyAccessible* aProxy);
 
 extern int atkMajorVersion, atkMinorVersion;
 
 /**
  * Return true if the loaded version of libatk-1.0.so is at least
  * aMajor.aMinor.0.
  */
 static inline bool
--- a/accessible/ipc/DocAccessibleChild.cpp
+++ b/accessible/ipc/DocAccessibleChild.cpp
@@ -13,19 +13,17 @@ namespace a11y {
 
 void
 SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree)
 {
   uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID());
   uint32_t role = aRoot->Role();
   uint32_t childCount = aRoot->ChildCount();
 
-  nsString name;
-  aRoot->Name(name);
-  aTree.AppendElement(AccessibleData(id, role, childCount, name));
+  aTree.AppendElement(AccessibleData(id, role, childCount));
   for (uint32_t i = 0; i < childCount; i++)
     SerializeTree(aRoot->GetChildAt(i), aTree);
 }
 
 void
 DocAccessibleChild::ShowEvent(AccShowEvent* aShowEvent)
 {
   Accessible* parent = aShowEvent->Parent();
@@ -45,10 +43,32 @@ DocAccessibleChild::RecvState(const uint
     *aState = states::DEFUNCT;
     return true;
   }
 
   *aState = acc->State();
 
   return true;
 }
+
+bool
+DocAccessibleChild::RecvName(const uint64_t& aID, nsString* aName)
+{
+  Accessible* acc = mDoc->GetAccessibleByUniqueID((void*)aID);
+  if (!acc)
+    return true;
+
+  acc->Name(*aName);
+  return true;
+}
+
+bool
+DocAccessibleChild::RecvDescription(const uint64_t& aID, nsString* aDesc)
+{
+  Accessible* acc = mDoc->GetAccessibleByUniqueID((void*)aID);
+  if (!acc)
+    return true;
+
+  acc->Description(*aDesc);
+  return true;
 }
 }
+}
--- a/accessible/ipc/DocAccessibleChild.h
+++ b/accessible/ipc/DocAccessibleChild.h
@@ -33,16 +33,26 @@ public:
 
   void ShowEvent(AccShowEvent* aShowEvent);
 
   /*
    * Return the state for the accessible with given ID.
    */
   virtual bool RecvState(const uint64_t& aID, uint64_t* aState) MOZ_OVERRIDE;
 
+  /*
+   * Get the name for the accessible with given id.
+   */
+  virtual bool RecvName(const uint64_t& aID, nsString* aName) MOZ_OVERRIDE;
+
+  /*
+   * Get the description for the accessible with given id.
+   */
+  virtual bool RecvDescription(const uint64_t& aID, nsString* aDesc) MOZ_OVERRIDE;
+
 private:
   DocAccessible* mDoc;
 };
 
 }
 }
 
 #endif
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -38,20 +38,22 @@ DocAccessibleParent::RecvShowEvent(const
   uint32_t newChildIdx = aData.Idx();
   if (newChildIdx > parent->ChildrenCount()) {
     NS_ERROR("invalid index to add child at");
     return false;
   }
 
   uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
   MOZ_ASSERT(consumed == aData.NewTree().Length());
+#ifdef DEBUG
   for (uint32_t i = 0; i < consumed; i++) {
     uint64_t id = aData.NewTree()[i].ID();
     MOZ_ASSERT(mAccessibles.GetEntry(id));
   }
+#endif
 
   return consumed;
 }
 
 uint32_t
 DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
                                 const nsTArray<a11y::AccessibleData>& aNewTree,
                                 uint32_t aIdx, uint32_t aIdxInParent)
@@ -64,17 +66,17 @@ DocAccessibleParent::AddSubtree(ProxyAcc
   const AccessibleData& newChild = aNewTree[aIdx];
   if (newChild.Role() > roles::LAST_ROLE) {
     NS_ERROR("invalid role");
     return 0;
   }
 
   auto role = static_cast<a11y::role>(newChild.Role());
   ProxyAccessible* newProxy =
-    new ProxyAccessible(newChild.ID(), aParent, this, role, newChild.Name());
+    new ProxyAccessible(newChild.ID(), aParent, this, role);
   aParent->AddChildAt(aIdxInParent, newProxy);
   mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
   ProxyCreated(newProxy);
 
   uint32_t accessibles = 1;
   uint32_t kids = newChild.ChildrenCount();
   for (uint32_t i = 0; i < kids; i++) {
     uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i);
--- a/accessible/ipc/PDocAccessible.ipdl
+++ b/accessible/ipc/PDocAccessible.ipdl
@@ -9,17 +9,16 @@ include protocol PContent;
 namespace mozilla {
 namespace a11y {
 
 struct AccessibleData
 {
   uint64_t ID;
   uint32_t Role;
   uint32_t ChildrenCount;
-  nsString Name;
 };
 
 struct ShowEventData
 {
   uint64_t ID;
   uint32_t Idx;
   AccessibleData[] NewTree;
 };
@@ -36,12 +35,14 @@ parent:
    * event.
    */
   Event(uint32_t type);
   ShowEvent(ShowEventData data);
   HideEvent(uint64_t aRootID);
 
 child:
   prio(high) sync State(uint64_t aID) returns(uint64_t states);
+  prio(high) sync Name(uint64_t aID) returns(nsString name);
+  prio(high) sync Description(uint64_t aID) returns(nsString desc);
 };
 
 }
 }
--- a/accessible/ipc/ProxyAccessible.cpp
+++ b/accessible/ipc/ProxyAccessible.cpp
@@ -42,10 +42,22 @@ ProxyAccessible::SetChildDoc(DocAccessib
 
 uint64_t
 ProxyAccessible::State() const
 {
   uint64_t state = 0;
   unused << mDoc->SendState(mID, &state);
   return state;
 }
+
+void
+ProxyAccessible::Name(nsString& aName) const
+{
+  unused << mDoc->SendName(mID, &aName);
+}
+
+void
+ProxyAccessible::Description(nsString& aDesc) const
+{
+  unused << mDoc->SendDescription(mID, &aDesc);
 }
 }
+}
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -16,19 +16,18 @@ namespace a11y {
 
 class DocAccessibleParent;
 
 class ProxyAccessible
 {
 public:
 
   ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
-                  DocAccessibleParent* aDoc, role aRole,
-                  const nsString& aName) :
-     mParent(aParent), mDoc(aDoc), mID(aID), mRole(aRole), mOuterDoc(false), mName(aName)
+                  DocAccessibleParent* aDoc, role aRole) :
+     mParent(aParent), mDoc(aDoc), mID(aID), mRole(aRole), mOuterDoc(false)
   {
     MOZ_COUNT_CTOR(ProxyAccessible);
   }
   ~ProxyAccessible() { MOZ_COUNT_DTOR(ProxyAccessible); }
 
   void AddChildAt(uint32_t aIdx, ProxyAccessible* aChild)
   { mChildren.InsertElementAt(aIdx, aChild); }
 
@@ -54,16 +53,26 @@ public:
    */
   role Role() const { return mRole; }
 
   /*
    * Return the states for the proxied accessible.
    */
   uint64_t State() const;
 
+  /*
+   * Set aName to the name of the proxied accessible.
+   */
+  void Name(nsString& aName) const;
+
+  /**
+   * Set aDesc to the description of the proxied accessible.
+   */
+  void Description(nsString& aDesc) const;
+
   /**
    * Allow the platform to store a pointers worth of data on us.
    */
   uintptr_t GetWrapper() const { return mWrapper; }
   void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
 
   /*
    * Return the ID of the accessible being proxied.
@@ -80,15 +89,14 @@ protected:
 
 private:
   nsTArray<ProxyAccessible*> mChildren;
   DocAccessibleParent* mDoc;
   uintptr_t mWrapper;
   uint64_t mID;
   role mRole : 31;
   bool mOuterDoc : 1;
-  nsString mName;
 };
 
 }
 }
 
 #endif
--- a/b2g/app/ua-update.json.in
+++ b/b2g/app/ua-update.json.in
@@ -6,26 +6,22 @@
   // bug 826335, globo.com
   "globo.com": "\\(Mobile#(Android; Mobile",
   // bug 826347, msn.com
   "msn.com": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
   // bug 826348, linkedin.com
   "linkedin.com": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
   // bug 826353, itau.com.br
   "itau.com.br": "\\(Mobile#(Android; Mobile",
-  // bug 826504, orkut.com.br
-  "orkut.com.br": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
   // bug 826510, r7.com
   "r7.com": "\\(Mobile#(Android; Mobile",
   // bug 826514, estadao.com.br
   "estadao.com.br": "\\(Mobile#(Android; Mobile",
   // bug 826711, bb.com.br
   "bb.com.br": "\\(Mobile#(Android; Mobile",
-  // bug 826712, orkut.com
-  "orkut.com": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
   // bug 827622, bing.com
   "bing.com": "\\(Mobile#(Android; Mobile",
   // bug 827626, magazineluiza.com.br
   "magazineluiza.com.br": "\\(Mobile#(Android; Mobile",
   // bug 827628, groupon.com.br
   "groupon.com.br": "\\(Mobile#(Android; Mobile",
   // bug 827633, hao123.com
   "hao123.com": "\\(Mobile#(Android; Mobile",
@@ -34,28 +30,24 @@
   // bug 827670, elpais.com.co
   "elpais.com.co": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
   // bug 827674, avianca.com
   "avianca.com": "\\(Mobile#(Android; Mobile",
   // bug 827678, marca.com
   "marca.com": "\\(Mobile#(Android; Mobile",
   // bug 828371, ingbank.pl
   "ingbank.pl": "\\(Mobile#(Android; Mobile",
-  // bug 828399, antena3.com
-  "antena3.com": "\\(Mobile#(Android; Mobile",
   // bug 828416, loteriasyapuestas.es
   "loteriasyapuestas.es": "\\(Mobile#(Android; Mobile",
   // bug 828418, bbva.es
   "bbva.es": "\\(Mobile#(Android; Mobile",
   // bug 828422, publico.es
   "publico.es": "\\(Mobile#(Android; Mobile",
   // bug 828439, movistar.com.ve
   "movistar.com.ve": "\\(Mobile#(Android; Mobile",
-  // bug 843126, es.playstation.com
-  "es.playstation.com": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
   // bug 843129, 11870.com
   "iphonejuegosgratis.com": "\\(Mobile#(Android; Mobile",
   // bug 843132, comunio.es
   "comunio.es": "\\(Mobile#(Android; Mobile",
   // bug 843151, citibank.com
   "citibank.com": "\\(Mobile#(Android; Mobile",
   // bug 843153, games.com
   "games.com": "\\(Mobile#(Android; Mobile",
--- a/browser/devtools/framework/test/browser.ini
+++ b/browser/devtools/framework/test/browser.ini
@@ -3,16 +3,17 @@ subsuite = devtools
 support-files =
   browser_toolbox_options_disable_js.html
   browser_toolbox_options_disable_js_iframe.html
   browser_toolbox_options_disable_cache.sjs
   head.js
   doc_theme.css
 
 [browser_devtools_api.js]
+skip-if = e10s # Bug 1090340
 [browser_devtools_api_destroy.js]
 skip-if = e10s # Bug 1070837 - devtools/framework/toolbox.js |doc| getter not e10s friendly
 [browser_dynamic_tool_enabling.js]
 [browser_keybindings.js]
 [browser_new_activation_workflow.js]
 [browser_target_events.js]
 [browser_target_remote.js]
 [browser_target_support.js]
--- a/browser/devtools/styleinspector/test/browser.ini
+++ b/browser/devtools/styleinspector/test/browser.ini
@@ -90,16 +90,17 @@ skip-if = (os == "win" && debug) || e10s
 [browser_ruleview_multiple-properties-unfinished_01.js]
 [browser_ruleview_multiple-properties-unfinished_02.js]
 [browser_ruleview_multiple_properties_01.js]
 [browser_ruleview_multiple_properties_02.js]
 [browser_ruleview_original-source-link.js]
 [browser_ruleview_override.js]
 [browser_ruleview_pseudo-element_01.js]
 [browser_ruleview_pseudo-element_02.js]
+skip-if = e10s # Bug 1090340
 [browser_ruleview_refresh-on-attribute-change_01.js]
 [browser_ruleview_refresh-on-attribute-change_02.js]
 [browser_ruleview_refresh-on-style-change.js]
 [browser_ruleview_select-and-copy-styles.js]
 [browser_ruleview_selector-highlighter_01.js]
 [browser_ruleview_selector-highlighter_02.js]
 [browser_ruleview_style-editor-link.js]
 skip-if = e10s # bug 1040670 Cannot open inline styles in viewSourceUtils
--- a/config/config.mk
+++ b/config/config.mk
@@ -414,18 +414,22 @@ endif # WINNT && (MOS_PROFILE_GENERATE ^
 ifdef FAIL_ON_WARNINGS_DEBUG
 ifdef MOZ_DEBUG
 FAIL_ON_WARNINGS = 1
 endif # MOZ_DEBUG
 endif # FAIL_ON_WARNINGS_DEBUG
 
 # Check for normal version of flag, and add WARNINGS_AS_ERRORS if it's set to 1.
 ifdef FAIL_ON_WARNINGS
+# Never treat warnings as errors in clang-cl, because it warns about many more
+# things than MSVC does.
+ifndef CLANG_CL
 CXXFLAGS += $(WARNINGS_AS_ERRORS)
 CFLAGS   += $(WARNINGS_AS_ERRORS)
+endif # CLANG_CL
 endif # FAIL_ON_WARNINGS
 
 ifeq ($(OS_ARCH)_$(GNU_CC),WINNT_)
 #// Currently, unless USE_STATIC_LIBS is defined, the multithreaded
 #// DLL version of the RTL is used...
 #//
 #//------------------------------------------------------------------------
 ifdef USE_STATIC_LIBS
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -9351,31 +9351,40 @@ nsGlobalWindow::UpdateCommands(const nsA
                                                             anAction));
     }
   }
 
   if (gSelectionCaretPrefEnabled && mDoc && anAction.EqualsLiteral("selectionchange")) {
     SelectionChangeEventInit init;
     init.mBubbles = true;
     if (aSel) {
-      nsCOMPtr<nsIDOMRange> range;
-      nsresult rv = aSel->GetRangeAt(0, getter_AddRefs(range));
-      if (NS_SUCCEEDED(rv) && range) {
-        nsRefPtr<nsRange> nsrange = static_cast<nsRange*>(range.get());
-        init.mBoundingClientRect = nsrange->GetBoundingClientRect(true, false);
-        range->ToString(init.mSelectedText);
-
-        for (uint32_t reasonType = 0;
-             reasonType < static_cast<uint32_t>(SelectionChangeReason::EndGuard_);
-             ++reasonType) {
-          SelectionChangeReason strongReasonType =
-            static_cast<SelectionChangeReason>(reasonType);
-          if (CheckReason(aReason, strongReasonType)) {
-            init.mReasons.AppendElement(strongReasonType);
-          }
+      Selection* selection = static_cast<Selection*>(aSel);
+      int32_t rangeCount = selection->GetRangeCount();
+      nsLayoutUtils::RectAccumulator accumulator;
+      for (int32_t idx = 0; idx < rangeCount; ++idx) {
+        nsRange* range = selection->GetRangeAt(idx);
+        nsRange::CollectClientRects(&accumulator, range,
+                                    range->GetStartParent(), range->StartOffset(),
+                                    range->GetEndParent(), range->EndOffset(),
+                                    true, false);
+      }
+      nsRect rect = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect :
+        accumulator.mResultRect;
+      nsRefPtr<DOMRect> domRect = new DOMRect(ToSupports(this));
+      domRect->SetLayoutRect(rect);
+      init.mBoundingClientRect = domRect;
+
+      selection->Stringify(init.mSelectedText);
+      for (uint32_t reasonType = 0;
+           reasonType < static_cast<uint32_t>(SelectionChangeReason::EndGuard_);
+           ++reasonType) {
+        SelectionChangeReason strongReasonType =
+          static_cast<SelectionChangeReason>(reasonType);
+        if (CheckReason(aReason, strongReasonType)) {
+          init.mReasons.AppendElement(strongReasonType);
         }
       }
 
       nsRefPtr<SelectionChangeEvent> event =
         SelectionChangeEvent::Constructor(mDoc, NS_LITERAL_STRING("mozselectionchange"), init);
 
       event->SetTrusted(true);
       event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -410,16 +410,17 @@ skip-if = e10s # Bug 1081453 - shutdown 
 [test_bug372964-2.html]
 [test_bug372964.html]
 [test_bug373181.xhtml]
 [test_bug375314.html]
 [test_bug378969.html]
 [test_bug380418.html]
 [test_bug380418.html^headers^]
 [test_bug382113.html]
+skip-if == (os == 'mac' || os == 'win') && debug # bug 453969
 [test_bug382871.html]
 [test_bug384003.xhtml]
 [test_bug390219.html]
 [test_bug390735.html]
 [test_bug392318.html]
 [test_bug392511.html]
 [test_bug393968.html]
 [test_bug395915.html]
--- a/dom/canvas/test/mochitest.ini
+++ b/dom/canvas/test/mochitest.ini
@@ -211,17 +211,17 @@ disabled = bug 407107
 [test_bug764125.html]
 [test_bug856472.html]
 [test_bug866575.html]
 skip-if = (toolkit == 'gonk' && debug) #bug 1045153
 [test_bug902651.html]
 [test_canvas.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only crash; bug 933541
 [test_canvas_focusring.html]
-skip-if = (toolkit == 'gonk' && !debug) #specialpowers.wrap
+skip-if = (toolkit == 'gonk' && !debug) || (os == 'win' && debug) #specialpowers.wrap
 [test_canvas_font_setter.html]
 [test_canvas_path.html]
 [test_hitregion_canvas.html]
 [test_hitregion_event.html]
 skip-if = os == "android" || appname == "b2g"
 [test_canvas_strokeStyle_getter.html]
 [test_drawImageIncomplete.html]
 [test_drawImage_document_domain.html]
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -674,18 +674,21 @@ HTMLInputElement::nsFilePickerShownCallb
     }
 
     nsCOMPtr<nsISupports> tmp;
     bool hasMore = true;
 
     while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
       iter->GetNext(getter_AddRefs(tmp));
       nsCOMPtr<nsIDOMFile> domFile = do_QueryInterface(tmp);
-      MOZ_ASSERT(domFile);
-      newFiles.AppendElement(static_cast<File*>(domFile.get()));
+      NS_WARN_IF_FALSE(domFile,
+                       "Null file object from FilePicker's file enumerator?");
+      if (domFile) {
+        newFiles.AppendElement(static_cast<File*>(domFile.get()));
+      }
     }
   } else {
     MOZ_ASSERT(mode == static_cast<int16_t>(nsIFilePicker::modeOpen));
     nsCOMPtr<nsIDOMFile> domFile;
     nsresult rv = mFilePicker->GetDomfile(getter_AddRefs(domFile));
     NS_ENSURE_SUCCESS(rv, rv);
     if (domFile) {
       newFiles.AppendElement(static_cast<File*>(domFile.get()));
--- a/dom/html/test/forms/test_input_color_picker_popup.html
+++ b/dom/html/test/forms/test_input_color_picker_popup.html
@@ -4,17 +4,16 @@
 https://bugzilla.mozilla.org/show_bug.cgi?id=885996
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 1234567</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <style> label { display: block } </style>
   <script type="application/javascript;version=1.8">
 
   /** Test the behaviour of the <input type='color'> when clicking on it from
       different ways. **/
 
   SimpleTest.waitForExplicitFinish();
 
   var MockColorPicker = SpecialPowers.MockColorPicker;
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -299,18 +299,18 @@ TabChildBase::HandlePossibleViewportChan
 
   TABC_LOG("HandlePossibleViewportChange aOldScreenSize=%s mInnerSize=%s\n",
     Stringify(aOldScreenSize).c_str(), Stringify(mInnerSize).c_str());
 
   nsCOMPtr<nsIDocument> document(GetDocument());
   nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
 
   nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, mInnerSize);
-  uint32_t presShellId;
-  mozilla::layers::FrameMetrics::ViewID viewId;
+  uint32_t presShellId = 0;
+  mozilla::layers::FrameMetrics::ViewID viewId = FrameMetrics::NULL_SCROLL_ID;
   bool scrollIdentifiersValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
         document->GetDocumentElement(), &presShellId, &viewId);
   if (scrollIdentifiersValid) {
     ZoomConstraints constraints(
       viewportInfo.IsZoomAllowed(),
       viewportInfo.IsDoubleTapZoomAllowed(),
       viewportInfo.GetMinZoom(),
       viewportInfo.GetMaxZoom());
--- a/dom/media/RtspMediaResource.cpp
+++ b/dom/media/RtspMediaResource.cpp
@@ -683,24 +683,24 @@ RtspMediaResource::OnConnected(uint8_t a
   // video, we give up moving forward.
   if (!IsVideoEnabled() && IsVideo(tracks, meta)) {
     // Give up, report error to media element.
     nsCOMPtr<nsIRunnable> event =
       NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
     NS_DispatchToMainThread(event);
     return NS_ERROR_FAILURE;
   }
-  uint64_t duration = 0;
+  uint64_t durationUs = 0;
   for (int i = 0; i < tracks; ++i) {
     nsCString rtspTrackId("RtspTrack");
     rtspTrackId.AppendInt(i);
     nsCOMPtr<nsIStreamingProtocolMetaData> trackMeta;
     mMediaStreamController->GetTrackMetaData(i, getter_AddRefs(trackMeta));
     MOZ_ASSERT(trackMeta);
-    trackMeta->GetDuration(&duration);
+    trackMeta->GetDuration(&durationUs);
 
     // Here is a heuristic to estimate the slot size.
     // For video track, calculate the width*height.
     // For audio track, use the BUFFER_SLOT_DEFAULT_SIZE because the w*h is 0.
     // Finally clamp them into (BUFFER_SLOT_DEFAULT_SIZE,BUFFER_SLOT_MAX_SIZE)
     uint32_t w, h;
     uint32_t slotSize;
     trackMeta->GetWidth(&w);
@@ -711,22 +711,22 @@ RtspMediaResource::OnConnected(uint8_t a
                                                    i, slotSize));
     mTrackBuffer[i]->Start();
   }
 
   if (!mDecoder) {
     return NS_ERROR_FAILURE;
   }
 
-  // If the duration is 0, imply the stream is live stream.
-  if (duration) {
+  // If the durationUs is 0, imply the stream is live stream.
+  if (durationUs) {
     // Not live stream.
     mRealTime = false;
     mDecoder->SetInfinite(false);
-    mDecoder->SetDuration(duration);
+    mDecoder->SetDuration((double)(durationUs) / USECS_PER_S);
   } else {
     // Live stream.
     // Check the preference "media.realtime_decoder.enabled".
     if (!Preferences::GetBool("media.realtime_decoder.enabled", false)) {
       // Give up, report error to media element.
       nsCOMPtr<nsIRunnable> event =
         NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
       NS_DispatchToMainThread(event);
--- a/dom/media/RtspMediaResource.h
+++ b/dom/media/RtspMediaResource.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #if !defined(RtspMediaResource_h_)
 #define RtspMediaResource_h_
 
 #include "MediaResource.h"
 #include "mozilla/Monitor.h"
 #include "nsITimer.h"
+#include "VideoUtils.h"
 
 namespace mozilla {
 
 class RtspTrackBuffer;
 
 /* RtspMediaResource
  * RtspMediaResource provides an interface to deliver and control RTSP media
  * data to RtspDecoder.
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -363,17 +363,17 @@ skip-if = (toolkit == 'android' && proce
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
 [test_eme_canvas_blocked.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_eme_playback.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_eme_stream_capture_blocked.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_error_in_video_document.html]
-skip-if = toolkit == 'android' # bug 608634
+skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634
 [test_error_on_404.html]
 [test_fastSeek.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_fastSeek-forwards.html]
 [test_imagecapture.html]
 [test_info_leak.html]
 [test_invalid_reject.html]
 [test_invalid_reject_play.html]
new file mode 100644
--- /dev/null
+++ b/editor/reftests/1088158-ref.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<textarea placeholder="placeholder"></textarea>
new file mode 100644
--- /dev/null
+++ b/editor/reftests/1088158.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script>
+  onload = function() {
+    var t = document.createElement('textarea');
+    t.placeholder = "placeholder";
+    document.body.appendChild(t.cloneNode(true));
+  }
+</script>
--- a/editor/reftests/reftest.list
+++ b/editor/reftests/reftest.list
@@ -122,8 +122,9 @@ needs-focus == spellcheck-contenteditabl
 == spellcheck-contenteditable-property-dynamic.html spellcheck-contenteditable-disabled-ref.html
 == spellcheck-contenteditable-property-dynamic-inherit.html spellcheck-contenteditable-disabled-ref.html
 == spellcheck-contenteditable-attr-dynamic-override.html spellcheck-contenteditable-disabled-ref.html
 == spellcheck-contenteditable-attr-dynamic-override-inherit.html spellcheck-contenteditable-disabled-ref.html
 == spellcheck-contenteditable-property-dynamic-override.html spellcheck-contenteditable-disabled-ref.html
 == spellcheck-contenteditable-property-dynamic-override-inherit.html spellcheck-contenteditable-disabled-ref.html
 needs-focus == 969773.html 969773-ref.html
 == 997805.html 997805-ref.html
+== 1088158.html 1088158-ref.html
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -505,16 +505,17 @@ NeedIntermediateSurface(const Pattern& a
   if (aOptions.mAlpha == 1.0)
     return false;
 
   return true;
 }
 
 DrawTargetCairo::DrawTargetCairo()
   : mContext(nullptr)
+  , mSurface(nullptr)
   , mLockedBits(nullptr)
 {
 }
 
 DrawTargetCairo::~DrawTargetCairo()
 {
   cairo_destroy(mContext);
   if (mSurface) {
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -31,63 +31,48 @@
 #include "gfxWindowsPlatform.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 UserDataKey gfxContext::sDontUseAsSourceKey;
 
-/* This class lives on the stack and allows gfxContext users to easily, and
- * performantly get a gfx::Pattern to use for drawing in their current context.
- */
-class PatternFromState
-{
-public:    
-  explicit PatternFromState(gfxContext *aContext) : mContext(aContext), mPattern(nullptr) {}
-  ~PatternFromState() { if (mPattern) { mPattern->~Pattern(); } }
-
-  operator mozilla::gfx::Pattern&()
-  {
-    gfxContext::AzureState &state = mContext->CurrentState();
-
-    if (state.pattern) {
-      return *state.pattern->GetPattern(mContext->mDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
-    } else if (state.sourceSurface) {
-      Matrix transform = state.surfTransform;
 
-      if (state.patternTransformChanged) {
-        Matrix mat = mContext->GetDTTransform();
-        if (!mat.Invert()) {
-          mPattern = new (mColorPattern.addr())
-          ColorPattern(Color()); // transparent black to paint nothing
-          return *mPattern;
-        }
-        transform = transform * state.patternTransform * mat;
-      }
+PatternFromState::operator mozilla::gfx::Pattern&()
+{
+  gfxContext::AzureState &state = mContext->CurrentState();
 
-      mPattern = new (mSurfacePattern.addr())
-        SurfacePattern(state.sourceSurface, ExtendMode::CLAMP, transform);
-      return *mPattern;
-    } else {
-      mPattern = new (mColorPattern.addr())
-        ColorPattern(state.color);
-      return *mPattern;
-    }
+  if (state.pattern) {
+    return *state.pattern->GetPattern(mContext->mDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
   }
 
-private:
-  union {
-    mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern;
-    mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern;
-  };
+  if (state.sourceSurface) {
+    Matrix transform = state.surfTransform;
 
-  gfxContext *mContext;
-  Pattern *mPattern;
-};
+    if (state.patternTransformChanged) {
+      Matrix mat = mContext->GetDTTransform();
+      if (!mat.Invert()) {
+        mPattern = new (mColorPattern.addr())
+        ColorPattern(Color()); // transparent black to paint nothing
+        return *mPattern;
+      }
+      transform = transform * state.patternTransform * mat;
+    }
+
+    mPattern = new (mSurfacePattern.addr())
+    SurfacePattern(state.sourceSurface, ExtendMode::CLAMP, transform);
+    return *mPattern;
+  }
+
+  mPattern = new (mColorPattern.addr())
+  ColorPattern(state.color);
+  return *mPattern;
+}
+
 
 gfxContext::gfxContext(DrawTarget *aTarget, const Point& aDeviceOffset)
   : mPathIsRect(false)
   , mTransformChanged(false)
   , mRefCairo(nullptr)
   , mDT(aTarget)
   , mOriginalDT(aTarget)
 {
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -707,77 +707,16 @@ public:
   }
 
 private:
   gfxContext *mContext;
 };
 
 /**
  * Sentry helper class for functions with multiple return points that need to
- * back up the current path of a context and have it automatically restored
- * before they return. This class assumes that the transformation matrix will
- * be the same when Save and Restore are called. The calling function must
- * ensure that this is the case or the path will be copied incorrectly.
- */
-class gfxContextPathAutoSaveRestore
-{
-    typedef mozilla::gfx::Path Path;
-
-public:
-    gfxContextPathAutoSaveRestore() : mContext(nullptr) {}
-
-    explicit gfxContextPathAutoSaveRestore(gfxContext *aContext, bool aSave = true) : mContext(aContext)
-    {
-        if (aSave)
-            Save();       
-    }
-
-    ~gfxContextPathAutoSaveRestore()
-    {
-        Restore();
-    }
-
-    void SetContext(gfxContext *aContext, bool aSave = true)
-    {
-        mContext = aContext;
-        if (aSave)
-            Save();
-    }
-
-    /**
-     * If a path is already saved, does nothing. Else copies the current path
-     * so that it may be restored.
-     */
-    void Save()
-    {
-        if (!mPath && mContext) {
-            mPath = mContext->GetPath();
-        }
-    }
-
-    /**
-     * If no path is saved, does nothing. Else replaces the context's path with
-     * a copy of the saved one, and clears the saved path.
-     */
-    void Restore()
-    {
-        if (mPath) {
-            mContext->SetPath(mPath);
-            mPath = nullptr;
-        }
-    }
-
-private:
-    gfxContext *mContext;
-
-    mozilla::RefPtr<Path> mPath;
-};
-
-/**
- * Sentry helper class for functions with multiple return points that need to
  * back up the current matrix of a context and have it automatically restored
  * before they return.
  */
 class gfxContextMatrixAutoSaveRestore
 {
 public:
     gfxContextMatrixAutoSaveRestore() :
         mContext(nullptr)
@@ -839,9 +778,30 @@ public:
         }
     }
 
 private:
     mozilla::RefPtr<mozilla::gfx::DrawTarget> mDT;
     bool mSubpixelAntialiasingEnabled;
 };
 
+/* This class lives on the stack and allows gfxContext users to easily, and
+ * performantly get a gfx::Pattern to use for drawing in their current context.
+ */
+class PatternFromState
+{
+public:
+  explicit PatternFromState(gfxContext *aContext) : mContext(aContext), mPattern(nullptr) {}
+  ~PatternFromState() { if (mPattern) { mPattern->~Pattern(); } }
+
+  operator mozilla::gfx::Pattern&();
+
+private:
+  union {
+    mozilla::AlignedStorage2<mozilla::gfx::ColorPattern> mColorPattern;
+    mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern;
+  };
+
+  gfxContext *mContext;
+  mozilla::gfx::Pattern *mPattern;
+};
+
 #endif /* GFX_CONTEXT_H */
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -1,23 +1,25 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "gfxFont.h"
+
 #include "mozilla/BinarySearch.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/gfx/2D.h"
 #include "mozilla/MathAlgorithms.h"
 
 #include "prlog.h"
 
 #include "nsExpirationTracker.h"
 #include "nsITimer.h"
 
-#include "gfxFont.h"
 #include "gfxGlyphExtents.h"
 #include "gfxPlatform.h"
 #include "gfxTextRun.h"
 #include "nsGkAtoms.h"
 
 #include "gfxTypes.h"
 #include "gfxContext.h"
 #include "gfxFontMissingGlyphs.h"
@@ -651,18 +653,18 @@ gfxShapedText::SetMissingGlyph(uint32_t 
 
     details->mGlyphID = aChar;
     if (IsDefaultIgnorable(aChar)) {
         // Setting advance width to zero will prevent drawing the hexbox
         details->mAdvance = 0;
     } else {
         gfxFloat width =
             std::max(aFont->GetMetrics(gfxFont::eHorizontal).aveCharWidth,
-                     gfxFontMissingGlyphs::GetDesiredMinWidth(aChar,
-                         mAppUnitsPerDevUnit));
+                     gfxFloat(gfxFontMissingGlyphs::GetDesiredMinWidth(aChar,
+                                mAppUnitsPerDevUnit)));
         details->mAdvance = uint32_t(width * mAppUnitsPerDevUnit);
     }
     details->mXOffset = 0;
     details->mYOffset = 0;
     GetCharacterGlyphs()[aIndex].SetMissing(1);
 }
 
 bool
@@ -1792,39 +1794,40 @@ gfxFont::DrawGlyphs(gfxShapedText       
                             double glyphY = aPt->y;
                             if (aRunParams.isRTL) {
                                 if (aFontParams.isVerticalFont) {
                                     glyphY -= advance;
                                 } else {
                                     glyphX -= advance;
                                 }
                             }
-                            gfxPoint pt(ToDeviceUnits(glyphX, aRunParams.devPerApp),
-                                        ToDeviceUnits(glyphY, aRunParams.devPerApp));
-                            gfxFloat advanceDevUnits =
-                                ToDeviceUnits(advance, aRunParams.devPerApp);
-                            gfxFloat height = GetMetrics(eHorizontal).maxAscent;
-                            gfxRect glyphRect = aFontParams.isVerticalFont ?
-                                gfxRect(pt.x - height / 2, pt.y,
-                                        height, advanceDevUnits) :
-                                gfxRect(pt.x, pt.y - height,
-                                        advanceDevUnits, height);
+                            Point pt(Float(ToDeviceUnits(glyphX, aRunParams.devPerApp)),
+                                     Float(ToDeviceUnits(glyphY, aRunParams.devPerApp)));
+                            Float advanceDevUnits =
+                                Float(ToDeviceUnits(advance, aRunParams.devPerApp));
+                            Float height = GetMetrics(eHorizontal).maxAscent;
+                            Rect glyphRect = aFontParams.isVerticalFont ?
+                                Rect(pt.x - height / 2, pt.y,
+                                     height, advanceDevUnits) :
+                                Rect(pt.x, pt.y - height,
+                                     advanceDevUnits, height);
 
                             // If there's a fake-italic skew in effect as part
                             // of the drawTarget's transform, we need to remove
                             // this before drawing the hexbox. (Bug 983985)
                             Matrix oldMat;
                             if (aFontParams.passedInvMatrix) {
                                 oldMat = aRunParams.dt->GetTransform();
                                 aRunParams.dt->SetTransform(
                                     *aFontParams.passedInvMatrix * oldMat);
                             }
 
                             gfxFontMissingGlyphs::DrawMissingGlyph(
-                                aRunParams.context, glyphRect, details->mGlyphID,
+                                details->mGlyphID, glyphRect, *aRunParams.dt,
+                                PatternFromState(aRunParams.context),
                                 aShapedText->GetAppUnitsPerDevUnit());
 
                             // Restore the matrix, if we modified it before
                             // drawing the hexbox.
                             if (aFontParams.passedInvMatrix) {
                                 aRunParams.dt->SetTransform(oldMat);
                             }
                         }
--- a/gfx/thebes/gfxFontMissingGlyphs.cpp
+++ b/gfx/thebes/gfxFontMissingGlyphs.cpp
@@ -1,17 +1,25 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxFontMissingGlyphs.h"
+
+#include "gfxUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Helpers.h"
+#include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/RefPtr.h"
 #include "nsDeviceContext.h"
-#include "gfxContext.h"
-#include "gfxColor.h"
+#include "nsLayoutUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
 
 #define CHAR_BITS(b00, b01, b02, b10, b11, b12, b20, b21, b22, b30, b31, b32, b40, b41, b42) \
   ((b00 << 0) | (b01 << 1) | (b02 << 2) | (b10 << 3) | (b11 << 4) | (b12 << 5) | \
    (b20 << 6) | (b21 << 7) | (b22 << 8) | (b30 << 9) | (b31 << 10) | (b32 << 11) | \
    (b40 << 12) | (b41 << 13) | (b42 << 14))
 
 static const uint16_t glyphMicroFont[16] = {
   CHAR_BITS(0, 1, 0,
@@ -134,147 +142,147 @@ static const int HEX_CHAR_GAP = 1;
  */
 static const int BOX_HORIZONTAL_INSET = 1;
 /** The width of the border */
 static const int BOX_BORDER_WIDTH = 1;
 /**
  * The scaling factor for the border opacity; this is multiplied by the current
  * opacity being used to draw the text.
  */
-static const gfxFloat BOX_BORDER_OPACITY = 0.5;
+static const Float BOX_BORDER_OPACITY = 0.5;
 /**
  * Draw a single hex character using the current color. A nice way to do this
  * would be to fill in an A8 image surface and then use it as a mask
  * to paint the current color. Tragically this doesn't currently work with the
  * Quartz cairo backend which doesn't generally support masking with surfaces.
  * So for now we just paint a bunch of rectangles...
  */
 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
 static void
-DrawHexChar(gfxContext *aContext, const gfxPoint& aPt, uint32_t aDigit)
+DrawHexChar(uint32_t aDigit, const Point& aPt, DrawTarget& aDrawTarget,
+            const Pattern &aPattern)
 {
-    aContext->NewPath();
+    // To avoid the potential for seams showing between rects when we're under
+    // a transform we concat all the rects into a PathBuilder and fill the
+    // resulting Path (rather than using DrawTarget::FillRect).
+    RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
     uint32_t glyphBits = glyphMicroFont[aDigit];
-    int x, y;
-    for (y = 0; y < MINIFONT_HEIGHT; ++y) {
-        for (x = 0; x < MINIFONT_WIDTH; ++x) {
+    for (int y = 0; y < MINIFONT_HEIGHT; ++y) {
+        for (int x = 0; x < MINIFONT_WIDTH; ++x) {
             if (glyphBits & 1) {
-                aContext->Rectangle(gfxRect(x, y, 1, 1) + aPt, true);
+                Rect r(aPt.x + x, aPt.y + y, 1, 1);
+                MaybeSnapToDevicePixels(r, aDrawTarget, true);
+                builder->MoveTo(r.TopLeft());
+                builder->LineTo(r.TopRight());
+                builder->LineTo(r.BottomRight());
+                builder->LineTo(r.BottomLeft());
+                builder->Close();
             }
             glyphBits >>= 1;
         }
     }
-    aContext->Fill();
+    RefPtr<Path> path = builder->Finish();
+    aDrawTarget.Fill(path, aPattern);
 }
 #endif // MOZ_GFX_OPTIMIZE_MOBILE
 
 void
-gfxFontMissingGlyphs::DrawMissingGlyph(gfxContext    *aContext,
-                                       const gfxRect& aRect,
-                                       uint32_t       aChar,
-                                       uint32_t       aAppUnitsPerDevPixel)
+gfxFontMissingGlyphs::DrawMissingGlyph(uint32_t aChar,
+                                       const Rect& aRect,
+                                       DrawTarget& aDrawTarget,
+                                       const Pattern& aPattern,
+                                       uint32_t aAppUnitsPerDevPixel)
 {
-    aContext->Save();
-
-    gfxRGBA currentColor;
-    if (!aContext->GetDeviceColor(currentColor)) {
-        // We're currently drawing with some kind of pattern... Just draw
-        // the missing-glyph data in black.
-        currentColor = gfxRGBA(0,0,0,1);
-    }
+    // If we're currently drawing with some kind of pattern, we just draw the
+    // missing-glyph data in black.
+    ColorPattern color = aPattern.GetType() == PatternType::COLOR ?
+        static_cast<const ColorPattern&>(aPattern) :
+        ColorPattern(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f)));
 
     // Stroke a rectangle so that the stroke's left edge is inset one pixel
     // from the left edge of the glyph box and the stroke's right edge
     // is inset one pixel from the right edge of the glyph box.
-    gfxFloat halfBorderWidth = BOX_BORDER_WIDTH / 2.0;
-    gfxFloat borderLeft = aRect.X() + BOX_HORIZONTAL_INSET + halfBorderWidth;
-    gfxFloat borderRight = aRect.XMost() - BOX_HORIZONTAL_INSET - halfBorderWidth;
-    gfxRect borderStrokeRect(borderLeft, aRect.Y() + halfBorderWidth,
-                             borderRight - borderLeft,
-                             aRect.Height() - 2.0 * halfBorderWidth);
+    Float halfBorderWidth = BOX_BORDER_WIDTH / 2.0;
+    Float borderLeft = aRect.X() + BOX_HORIZONTAL_INSET + halfBorderWidth;
+    Float borderRight = aRect.XMost() - BOX_HORIZONTAL_INSET - halfBorderWidth;
+    Rect borderStrokeRect(borderLeft, aRect.Y() + halfBorderWidth,
+                          borderRight - borderLeft,
+                          aRect.Height() - 2.0 * halfBorderWidth);
     if (!borderStrokeRect.IsEmpty()) {
-        aContext->SetLineWidth(BOX_BORDER_WIDTH);
-        aContext->SetDash(gfxContext::gfxLineSolid);
-        aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
-        aContext->SetLineJoin(gfxContext::LINE_JOIN_MITER);
-        gfxRGBA color = currentColor;
-        color.a *= BOX_BORDER_OPACITY;
-        aContext->SetDeviceColor(color);
-        aContext->NewPath();
-        aContext->Rectangle(borderStrokeRect);
-
+        ColorPattern adjustedColor = color;
+        color.mColor.a *= BOX_BORDER_OPACITY;
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
-        aContext->Fill();
+        aDrawTarget.FillRect(borderStrokeRect, adjustedColor);
 #else
-        aContext->Stroke();
+        StrokeOptions strokeOptions(BOX_BORDER_WIDTH);
+        aDrawTarget.StrokeRect(borderStrokeRect, adjustedColor, strokeOptions);
 #endif
     }
 
 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
-    gfxPoint center(aRect.X() + aRect.Width() / 2,
-                    aRect.Y() + aRect.Height() / 2);
-    gfxFloat halfGap = HEX_CHAR_GAP / 2.0;
-    gfxFloat top = -(MINIFONT_HEIGHT + halfGap);
-    aContext->SetDeviceColor(currentColor);
+    Point center = aRect.Center();
+    Float halfGap = HEX_CHAR_GAP / 2.f;
+    Float top = -(MINIFONT_HEIGHT + halfGap);
     // We always want integer scaling, otherwise the "bitmap" glyphs will look
     // even uglier than usual when zoomed
-    int32_t scale =
+    int32_t devPixelsPerCSSPx =
         std::max<int32_t>(1, nsDeviceContext::AppUnitsPerCSSPixel() /
                              aAppUnitsPerDevPixel);
-    aContext->SetMatrix(
-      aContext->CurrentMatrix().Translate(center).Scale(scale, scale));
+    AutoRestoreTransform autoRestoreTransform(&aDrawTarget);
+    aDrawTarget.SetTransform(
+      aDrawTarget.GetTransform().PreTranslate(center).
+                                 PreScale(devPixelsPerCSSPx,
+                                          devPixelsPerCSSPx));
     if (aChar < 0x10000) {
         if (aRect.Width() >= 2 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
             aRect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
             // Draw 4 digits for BMP
-            gfxFloat left = -(MINIFONT_WIDTH + halfGap);
-            DrawHexChar(aContext,
-                        gfxPoint(left, top), (aChar >> 12) & 0xF);
-            DrawHexChar(aContext,
-                        gfxPoint(halfGap, top), (aChar >> 8) & 0xF);
-            DrawHexChar(aContext,
-                        gfxPoint(left, halfGap), (aChar >> 4) & 0xF);
-            DrawHexChar(aContext,
-                        gfxPoint(halfGap, halfGap), aChar & 0xF);
+            Float left = -(MINIFONT_WIDTH + halfGap);
+            DrawHexChar((aChar >> 12) & 0xF,
+                        Point(left, top), aDrawTarget, color);
+            DrawHexChar((aChar >> 8) & 0xF,
+                        Point(halfGap, top), aDrawTarget, color);
+            DrawHexChar((aChar >> 4) & 0xF,
+                        Point(left, halfGap), aDrawTarget, color);
+            DrawHexChar(aChar & 0xF,
+                        Point(halfGap, halfGap), aDrawTarget, color);
         }
     } else {
         if (aRect.Width() >= 3 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
             aRect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
             // Draw 6 digits for non-BMP
-            gfxFloat first = -(MINIFONT_WIDTH * 1.5 + HEX_CHAR_GAP);
-            gfxFloat second = -(MINIFONT_WIDTH / 2.0);
-            gfxFloat third = (MINIFONT_WIDTH / 2.0 + HEX_CHAR_GAP);
-            DrawHexChar(aContext,
-                        gfxPoint(first, top), (aChar >> 20) & 0xF);
-            DrawHexChar(aContext,
-                        gfxPoint(second, top), (aChar >> 16) & 0xF);
-            DrawHexChar(aContext,
-                        gfxPoint(third, top), (aChar >> 12) & 0xF);
-            DrawHexChar(aContext,
-                        gfxPoint(first, halfGap), (aChar >> 8) & 0xF);
-            DrawHexChar(aContext,
-                        gfxPoint(second, halfGap), (aChar >> 4) & 0xF);
-            DrawHexChar(aContext,
-                        gfxPoint(third, halfGap), aChar & 0xF);
+            Float first = -(MINIFONT_WIDTH * 1.5 + HEX_CHAR_GAP);
+            Float second = -(MINIFONT_WIDTH / 2.0);
+            Float third = (MINIFONT_WIDTH / 2.0 + HEX_CHAR_GAP);
+            DrawHexChar((aChar >> 20) & 0xF,
+                        Point(first, top), aDrawTarget, color);
+            DrawHexChar((aChar >> 16) & 0xF,
+                        Point(second, top), aDrawTarget, color);
+            DrawHexChar((aChar >> 12) & 0xF,
+                        Point(third, top), aDrawTarget, color);
+            DrawHexChar((aChar >> 8) & 0xF,
+                        Point(first, halfGap), aDrawTarget, color);
+            DrawHexChar((aChar >> 4) & 0xF,
+                        Point(second, halfGap), aDrawTarget, color);
+            DrawHexChar(aChar & 0xF,
+                        Point(third, halfGap), aDrawTarget, color);
         }
     }
 #endif
-
-    aContext->Restore();
 }
 
-gfxFloat
+Float
 gfxFontMissingGlyphs::GetDesiredMinWidth(uint32_t aChar,
                                          uint32_t aAppUnitsPerDevPixel)
 {
 /**
  * The minimum desired width for a missing-glyph glyph box. I've laid it out
  * like this so you can see what goes where.
  */
-    gfxFloat width = BOX_HORIZONTAL_INSET + BOX_BORDER_WIDTH + HEX_CHAR_GAP +
+    Float width = BOX_HORIZONTAL_INSET + BOX_BORDER_WIDTH + HEX_CHAR_GAP +
         MINIFONT_WIDTH + HEX_CHAR_GAP + MINIFONT_WIDTH +
          ((aChar < 0x10000) ? 0 : HEX_CHAR_GAP + MINIFONT_WIDTH) +
         HEX_CHAR_GAP + BOX_BORDER_WIDTH + BOX_HORIZONTAL_INSET;
     // Note that this will give us floating-point division, so the width will
     // -not- be snapped to integer multiples of its basic pixel value
-    width *= gfxFloat(nsDeviceContext::AppUnitsPerCSSPixel()) / aAppUnitsPerDevPixel;
+    width *= Float(nsDeviceContext::AppUnitsPerCSSPixel()) / aAppUnitsPerDevPixel;
     return width;
 }
--- a/gfx/thebes/gfxFontMissingGlyphs.h
+++ b/gfx/thebes/gfxFontMissingGlyphs.h
@@ -1,40 +1,55 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_FONTMISSINGGLYPHS_H
 #define GFX_FONTMISSINGGLYPHS_H
 
-#include "gfxTypes.h"
-#include "gfxRect.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/gfx/Rect.h"
 
-class gfxContext;
+namespace mozilla {
+namespace gfx {
+class DrawTarget;
+class Pattern;
+}
+}
 
 /**
  * This class should not be instantiated. It's just a container
  * for some helper functions.
  */
-class gfxFontMissingGlyphs {
+class gfxFontMissingGlyphs MOZ_FINAL
+{
+    typedef mozilla::gfx::DrawTarget DrawTarget;
+    typedef mozilla::gfx::Float Float;
+    typedef mozilla::gfx::Pattern Pattern;
+    typedef mozilla::gfx::Rect Rect;
+
+    gfxFontMissingGlyphs() MOZ_DELETE; // prevent instantiation
+
 public:
     /**
      * Draw hexboxes for a missing glyph.
-     * @param aContext the context to draw to
+     * @param aChar the UTF16 codepoint for the character
      * @param aRect the glyph-box for the glyph that is missing
-     * @param aChar the UTF16 codepoint for the character
+     * @param aDrawTarget the DrawTarget to draw to
+     * @param aPattern the pattern currently being used to paint
      * @param aAppUnitsPerDevPixel the appUnits to devPixel ratio we're using,
      *                             (so we can scale glyphs to a sensible size)
      */
-    static void DrawMissingGlyph(gfxContext    *aContext,
-                                 const gfxRect& aRect,
-                                 uint32_t       aChar,
-                                 uint32_t       aAppUnitsPerDevPixel);
+    static void DrawMissingGlyph(uint32_t aChar,
+                                 const Rect& aRect,
+                                 DrawTarget& aDrawTarget,
+                                 const Pattern& aPattern,
+                                 uint32_t aAppUnitsPerDevPixel);
     /**
      * @return the desired minimum width for a glyph-box that will allow
      * the hexboxes to be drawn reasonably.
      */
-    static gfxFloat GetDesiredMinWidth(uint32_t aChar,
-                                       uint32_t aAppUnitsPerDevUnit);
+    static Float GetDesiredMinWidth(uint32_t aChar,
+                                    uint32_t aAppUnitsPerDevUnit);
 };
 
 #endif
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -2,16 +2,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxTextRun.h"
 #include "gfxGlyphExtents.h"
 #include "gfxPlatformFontList.h"
 #include "gfxUserFontSet.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/PathHelpers.h"
 #include "nsGkAtoms.h"
 #include "nsILanguageAtomService.h"
 #include "nsServiceManagerUtils.h"
 
 #include "gfxContext.h"
 #include "gfxFontConstants.h"
 #include "gfxFontMissingGlyphs.h"
 #include "gfxScriptItemizer.h"
@@ -19,16 +21,17 @@
 #include "nsUnicodeRange.h"
 #include "nsStyleConsts.h"
 #include "mozilla/Likely.h"
 #include "gfx2DGlue.h"
 
 #include "cairo.h"
 
 using namespace mozilla;
+using namespace mozilla::gfx;
 using namespace mozilla::unicode;
 
 static const char16_t kEllipsisChar[] = { 0x2026, 0x0 };
 static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
 
 #ifdef DEBUG_roc
 #define DEBUG_TEXT_RUN_STORAGE_METRICS
 #endif
@@ -449,40 +452,28 @@ gfxTextRun::DrawPartialLigature(gfxFont 
         ClipPartialLigature(this, &start, &end, aPt->y, &data);
     } else {
         start = clipExtents.X() * mAppUnitsPerDevUnit;
         end = clipExtents.XMost() * mAppUnitsPerDevUnit;
         ClipPartialLigature(this, &start, &end, aPt->x, &data);
     }
 
     {
-      // Need to preserve the path, otherwise this can break canvas text-on-path;
-      // in general it seems like a good thing, as naive callers probably won't
-      // expect gfxTextRun::Draw to implicitly destroy the current path.
-      gfxContextPathAutoSaveRestore savePath(aParams.context);
-
       // use division here to ensure that when the rect is aligned on multiples
       // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
       // Also, make sure we snap the rectangle to device pixels.
+      Rect clipRect = aParams.isVerticalRun ?
+          Rect(clipExtents.X(), start / mAppUnitsPerDevUnit,
+               clipExtents.Width(), (end - start) / mAppUnitsPerDevUnit) :
+          Rect(start / mAppUnitsPerDevUnit, clipExtents.Y(),
+               (end - start) / mAppUnitsPerDevUnit, clipExtents.Height());
+      MaybeSnapToDevicePixels(clipRect, *aParams.dt, true);
+
       aParams.context->Save();
-      aParams.context->NewPath();
-      if (aParams.isVerticalRun) {
-          aParams.context->Rectangle(gfxRect(clipExtents.X(),
-                                             start / mAppUnitsPerDevUnit,
-                                             clipExtents.Width(),
-                                             (end - start) / mAppUnitsPerDevUnit),
-                                     true);
-      } else {
-          aParams.context->Rectangle(gfxRect(start / mAppUnitsPerDevUnit,
-                                             clipExtents.Y(),
-                                             (end - start) / mAppUnitsPerDevUnit,
-                                             clipExtents.Height()),
-                                     true);
-      }
-      aParams.context->Clip();
+      aParams.context->Clip(clipRect);
     }
 
     gfxPoint pt;
     if (aParams.isVerticalRun) {
         pt = gfxPoint(aPt->x, aPt->y - aParams.direction * data.mPartAdvance);
     } else {
         pt = gfxPoint(aPt->x - aParams.direction * data.mPartAdvance, aPt->y);
     }
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -303,17 +303,20 @@ AsmJSModule::lookupHeapAccess(void *pc) 
 
 bool
 AsmJSModule::finish(ExclusiveContext *cx, TokenStream &tokenStream, MacroAssembler &masm,
                     const Label &interruptLabel)
 {
     MOZ_ASSERT(isFinishedWithFunctionBodies() && !isFinished());
 
     uint32_t endBeforeCurly = tokenStream.currentToken().pos.end;
-    uint32_t endAfterCurly = tokenStream.peekTokenPos().end;
+    TokenPos pos;
+    if (!tokenStream.peekTokenPos(&pos))
+        return false;
+    uint32_t endAfterCurly = pos.end;
     MOZ_ASSERT(endBeforeCurly >= srcBodyStart_);
     MOZ_ASSERT(endAfterCurly >= srcBodyStart_);
     pod.srcLength_ = endBeforeCurly - srcStart_;
     pod.srcLengthWithRightBrace_ = endAfterCurly - srcStart_;
 
     // The global data section sits immediately after the executable (and
     // other) data allocated by the MacroAssembler, so ensure it is
     // SIMD-aligned.
@@ -1970,17 +1973,19 @@ class ModuleChars
     Vector<PropertyNameWrapper, 0, SystemAllocPolicy> funCtorArgs_;
 
   public:
     static uint32_t beginOffset(AsmJSParser &parser) {
       return parser.pc->maybeFunction->pn_pos.begin;
     }
 
     static uint32_t endOffset(AsmJSParser &parser) {
-      return parser.tokenStream.peekTokenPos().end;
+      TokenPos pos;
+      MOZ_ALWAYS_TRUE(parser.tokenStream.peekTokenPos(&pos));
+      return pos.end;
     }
 };
 
 class ModuleCharsForStore : ModuleChars
 {
     uint32_t uncompressedSize_;
     uint32_t compressedSize_;
     Vector<char, 0, SystemAllocPolicy> compressedBuffer_;
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -358,29 +358,38 @@ SkipEmptyStatements(ParseNode *pn)
 }
 
 static inline ParseNode *
 NextNonEmptyStatement(ParseNode *pn)
 {
     return SkipEmptyStatements(pn->pn_next);
 }
 
-static TokenKind
-PeekToken(AsmJSParser &parser)
+static bool
+PeekToken(AsmJSParser &parser, TokenKind *tkp)
 {
     TokenStream &ts = parser.tokenStream;
-    while (ts.peekToken(TokenStream::Operand) == TOK_SEMI)
+    TokenKind tk;
+    while (true) {
+        if (!ts.peekToken(&tk, TokenStream::Operand))
+            return false;
+        if (tk != TOK_SEMI)
+            break;
         ts.consumeKnownToken(TOK_SEMI);
-    return ts.peekToken(TokenStream::Operand);
+    }
+    *tkp = tk;
+    return true;
 }
 
 static bool
 ParseVarOrConstStatement(AsmJSParser &parser, ParseNode **var)
 {
-    TokenKind tk = PeekToken(parser);
+    TokenKind tk;
+    if (!PeekToken(parser, &tk))
+        return false;
     if (tk != TOK_VAR && tk != TOK_CONST) {
         *var = nullptr;
         return true;
     }
 
     *var = parser.statement();
     if (!*var)
         return false;
@@ -1447,17 +1456,20 @@ class MOZ_STACK_CLASS ModuleCompiler
         if (pn)
             return failOffset(pn->pn_pos.begin, str);
 
         // The exact rooting static analysis does not perform dataflow analysis, so it believes
         // that unrooted things on the stack during compilation may still be accessed after this.
         // Since pn is typically only null under OOM, this suppression simply forces any GC to be
         // delayed until the compilation is off the stack and more memory can be freed.
         gc::AutoSuppressGC nogc(cx_);
-        return failOffset(tokenStream().peekTokenPos().begin, str);
+        TokenPos pos;
+        if (!tokenStream().peekTokenPos(&pos))
+            return false;
+        return failOffset(pos.begin, str);
     }
 
     bool failfVA(ParseNode *pn, const char *fmt, va_list ap) {
         MOZ_ASSERT(!errorString_);
         MOZ_ASSERT(errorOffset_ == UINT32_MAX);
         MOZ_ASSERT(fmt);
         errorOffset_ = pn ? pn->pn_pos.begin : tokenStream().currentToken().pos.end;
         errorString_.reset(JS_vsmprintf(fmt, ap));
@@ -3858,23 +3870,28 @@ CheckModuleGlobal(ModuleCompiler &m, Par
     return m.fail(initNode, "unsupported import expression");
 }
 
 static bool
 CheckModuleProcessingDirectives(ModuleCompiler &m)
 {
     TokenStream &ts = m.parser().tokenStream;
     while (true) {
-        if (!ts.matchToken(TOK_STRING))
+        bool matched;
+        if (!ts.matchToken(&matched, TOK_STRING))
+            return false;
+        if (!matched)
             return true;
 
         if (!IsIgnoredDirectiveName(m.cx(), ts.currentToken().atom()))
             return m.fail(nullptr, "unsupported processing directive");
 
-        if (!ts.matchToken(TOK_SEMI))
+        if (!ts.matchToken(&matched, TOK_SEMI))
+            return false;
+        if (!matched)
             return m.fail(nullptr, "expected semicolon after string literal");
     }
 }
 
 static bool
 CheckModuleGlobals(ModuleCompiler &m)
 {
     while (true) {
@@ -6853,25 +6870,26 @@ CheckChangeHeap(ModuleCompiler &m, Parse
     return m.addChangeHeap(changeHeapName, fn, mask, min, max);
 }
 
 static bool
 ParseFunction(ModuleCompiler &m, ParseNode **fnOut)
 {
     TokenStream &tokenStream = m.tokenStream();
 
-    DebugOnly<TokenKind> tk = tokenStream.getToken();
-    MOZ_ASSERT(tk == TOK_FUNCTION);
+    tokenStream.consumeKnownToken(TOK_FUNCTION);
 
     RootedPropertyName name(m.cx());
 
-    TokenKind tt = tokenStream.getToken();
-    if (tt == TOK_NAME) {
+    TokenKind tk;
+    if (!tokenStream.getToken(&tk))
+        return false;
+    if (tk == TOK_NAME) {
         name = tokenStream.currentName();
-    } else if (tt == TOK_YIELD) {
+    } else if (tk == TOK_YIELD) {
         if (!m.parser().checkYieldNameValidity())
             return false;
         name = m.cx()->names().yield;
     } else {
         return false;  // The regular parser will throw a SyntaxError, no need to m.fail.
     }
 
     ParseNode *fn = m.parser().handler.newFunctionDefinition();
@@ -7041,17 +7059,23 @@ CheckAllFunctionsDefined(ModuleCompiler 
 static bool
 CheckFunctionsSequential(ModuleCompiler &m)
 {
     // Use a single LifoAlloc to allocate all the temporary compiler IR.
     // All allocated LifoAlloc'd memory is released after compiling each
     // function by the LifoAllocScope inside the loop.
     LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
 
-    while (PeekToken(m.parser()) == TOK_FUNCTION) {
+    while (true) {
+        TokenKind tk;
+        if (!PeekToken(m.parser(), &tk))
+            return false;
+        if (tk != TOK_FUNCTION)
+            break;
+
         LifoAllocScope scope(&lifo);
 
         MIRGenerator *mir;
         ModuleCompiler::Func *func;
         if (!CheckFunction(m, lifo, &mir, &func))
             return false;
 
         // In the case of the change-heap function, no MIR is produced.
@@ -7203,17 +7227,23 @@ CheckFunctionsParallel(ModuleCompiler &m
         AutoLockHelperThreadState lock;
         MOZ_ASSERT(HelperThreadState().asmJSWorklist().empty());
         MOZ_ASSERT(HelperThreadState().asmJSFinishedList().empty());
     }
 #endif
     HelperThreadState().resetAsmJSFailureState();
 
     AsmJSParallelTask *task = nullptr;
-    for (unsigned i = 0; PeekToken(m.parser()) == TOK_FUNCTION; i++) {
+    for (unsigned i = 0;; i++) {
+        TokenKind tk;
+        if (!PeekToken(m.parser(), &tk))
+            return false;
+        if (tk != TOK_FUNCTION)
+            break;
+
         if (!task && !GetUnusedTask(group, i, &task) && !GetUsedTask(m, group, &task))
             return false;
 
         // Generate MIR into the LifoAlloc on the main thread.
         MIRGenerator *mir;
         ModuleCompiler::Func *func;
         if (!CheckFunction(m, task->lifo, &mir, &func))
             return false;
@@ -7455,18 +7485,20 @@ CheckModuleExportObject(ModuleCompiler &
     }
 
     return true;
 }
 
 static bool
 CheckModuleReturn(ModuleCompiler &m)
 {
-    if (PeekToken(m.parser()) != TOK_RETURN) {
-        TokenKind tk = PeekToken(m.parser());
+    TokenKind tk;
+    if (!PeekToken(m.parser(), &tk))
+        return false;
+    if (tk != TOK_RETURN) {
         if (tk == TOK_RC || tk == TOK_EOF)
             return m.fail(nullptr, "expecting return statement");
         return m.fail(nullptr, "invalid asm.js statement");
     }
 
     ParseNode *returnStmt = m.parser().statement();
     if (!returnStmt)
         return false;
@@ -8568,17 +8600,19 @@ CheckModule(ExclusiveContext *cx, AsmJSP
     m.finishFunctionBodies();
 
     if (!CheckFuncPtrTables(m))
         return false;
 
     if (!CheckModuleReturn(m))
         return false;
 
-    TokenKind tk = PeekToken(m.parser());
+    TokenKind tk;
+    if (!PeekToken(m.parser(), &tk))
+        return false;
     if (tk != TOK_EOF && tk != TOK_RC)
         return m.fail(nullptr, "top-level export (return) must be the last statement");
 
     // The instruction cache is flushed when dynamically linking, so can inhibit now.
     AutoFlushICache afc("CheckModule", /* inhibit= */ true);
 
     ScopedJSDeletePtr<AsmJSModule> module;
     if (!FinishModule(m, &module))
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -324,23 +324,21 @@ frontend::CompileScript(ExclusiveContext
                                                   directives, fun->generatorKind());
         if (!funbox)
             return nullptr;
         bce.objectList.add(funbox);
     }
 
     bool canHaveDirectives = true;
     for (;;) {
-        TokenKind tt = parser.tokenStream.peekToken(TokenStream::Operand);
-        if (tt <= TOK_EOF) {
-            if (tt == TOK_EOF)
-                break;
-            MOZ_ASSERT(tt == TOK_ERROR);
+        TokenKind tt;
+        if (!parser.tokenStream.peekToken(&tt, TokenStream::Operand))
             return nullptr;
-        }
+        if (tt == TOK_EOF)
+            break;
 
         TokenStream::Position pos(parser.keepAtoms);
         parser.tokenStream.tell(&pos);
 
         ParseNode *pn = parser.statement(canHaveDirectives);
         if (!pn) {
             if (parser.hadAbortedSyntaxParse()) {
                 // Parsing inner functions lazily may lead the parser into an
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -54,20 +54,21 @@ typedef Rooted<StaticBlockObject*> Roote
 typedef Handle<StaticBlockObject*> HandleStaticBlockObject;
 typedef Rooted<NestedScopeObject*> RootedNestedScopeObject;
 typedef Handle<NestedScopeObject*> HandleNestedScopeObject;
 
 
 /* Read a token. Report an error and return null() if that token isn't of type tt. */
 #define MUST_MATCH_TOKEN(tt, errno)                                                         \
     JS_BEGIN_MACRO                                                                          \
-        TokenKind token = tokenStream.getToken();                                           \
+        TokenKind token;                                                                    \
+        if (!tokenStream.getToken(&token))                                                  \
+            return null();                                                                  \
         if (token != tt) {                                                                  \
-            if (token != TOK_ERROR)                                                         \
-                report(ParseError, false, null(), errno);                                   \
+            report(ParseError, false, null(), errno);                                       \
             return null();                                                                  \
         }                                                                                   \
     JS_END_MACRO
 
 static const unsigned BlockIdLimit = 1 << ParseNode::NumBlockIdBits;
 
 template <typename ParseHandler>
 bool
@@ -692,19 +693,25 @@ Parser<ParseHandler>::parse(JSObject *ch
                                         &globalsc, /* newDirectives = */ nullptr,
                                         /* staticLevel = */ 0, /* bodyid = */ 0,
                                         /* blockScopeDepth = */ 0);
     if (!globalpc.init(tokenStream))
         return null();
 
     Node pn = statements();
     if (pn) {
-        if (!tokenStream.matchToken(TOK_EOF)) {
+        bool matched;
+        if (!tokenStream.matchToken(&matched, TOK_EOF))
+            return null();
+        if (!matched) {
+            TokenKind tt;
+            if (!tokenStream.peekToken(&tt))
+                return null();
             report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT,
-                   "script", TokenKindToDesc(tokenStream.peekToken()));
+                   "script", TokenKindToDesc(tt));
             return null();
         }
         if (foldConstants) {
             if (!FoldConstants(context, &pn, this))
                 return null();
         }
     }
     return pn;
@@ -811,19 +818,25 @@ Parser<FullParseHandler>::standaloneFunc
         if (!defineArg(fn, formals[i]))
             return null();
     }
 
     ParseNode *pn = functionBody(Statement, StatementListBody);
     if (!pn)
         return null();
 
-    if (!tokenStream.matchToken(TOK_EOF)) {
+    bool matched;
+    if (!tokenStream.matchToken(&matched, TOK_EOF))
+        return null();
+    if (!matched) {
+        TokenKind tt;
+        if (!tokenStream.peekToken(&tt))
+            return null();
         report(ParseError, false, null(), JSMSG_GARBAGE_AFTER_INPUT,
-               "function body", TokenKindToDesc(tokenStream.peekToken()));
+               "function body", TokenKindToDesc(tt));
         return null();
     }
 
     if (!FoldConstants(context, &pn, this))
         return null();
 
     InternalHandle<Bindings*> funboxBindings =
         InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
@@ -1210,27 +1223,27 @@ Parser<ParseHandler>::newFunction(Generi
     if (options().selfHostingMode)
         fun->setIsSelfHostedBuiltin();
     return fun;
 }
 
 static bool
 MatchOrInsertSemicolon(TokenStream &ts)
 {
-    TokenKind tt = ts.peekTokenSameLine(TokenStream::Operand);
-    if (tt == TOK_ERROR)
+    TokenKind tt;
+    if (!ts.peekTokenSameLine(&tt, TokenStream::Operand))
         return false;
     if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
         /* Advance the scanner for proper error location reporting. */
-        ts.getToken(TokenStream::Operand);
+        ts.consumeKnownToken(tt);
         ts.reportError(JSMSG_SEMI_BEFORE_STMNT);
         return false;
     }
-    (void) ts.matchToken(TOK_SEMI);
-    return true;
+    bool ignored;
+    return ts.matchToken(&ignored, TOK_SEMI);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::DefinitionNode
 Parser<ParseHandler>::getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom)
 {
     AtomDefnAddPtr p = pc->lexdeps->lookupForAdd(atom);
     if (p)
@@ -1518,47 +1531,67 @@ bool
 Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, Node funcpn,
                                         bool *hasRest)
 {
     FunctionBox *funbox = pc->sc->asFunctionBox();
 
     *hasRest = false;
 
     bool parenFreeArrow = false;
-    if (kind == Arrow && tokenStream.peekToken() == TOK_NAME) {
-        parenFreeArrow = true;
-    } else {
-        if (tokenStream.getToken() != TOK_LP) {
+    if (kind == Arrow) {
+        TokenKind tt;
+        if (!tokenStream.peekToken(&tt))
+            return false;
+        if (tt == TOK_NAME)
+            parenFreeArrow = true;
+    }
+    if (!parenFreeArrow) {
+        TokenKind tt;
+        if (!tokenStream.getToken(&tt))
+            return false;
+        if (tt != TOK_LP) {
             report(ParseError, false, null(),
                    kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
             return false;
         }
 
         // Record the start of function source (for FunctionToString). If we
         // are parenFreeArrow, we will set this below, after consuming the NAME.
         funbox->setStart(tokenStream);
     }
 
     Node argsbody = handler.newList(PNK_ARGSBODY);
     if (!argsbody)
         return false;
     handler.setFunctionBody(funcpn, argsbody);
 
-    if (parenFreeArrow || !tokenStream.matchToken(TOK_RP)) {
+    bool hasArguments = false;
+    if (parenFreeArrow) {
+        hasArguments = true;
+    } else {
+        bool matched;
+        if (!tokenStream.matchToken(&matched, TOK_RP))
+            return false;
+        if (!matched)
+            hasArguments = true;
+    }
+    if (hasArguments) {
         bool hasDefaults = false;
         Node duplicatedArg = null();
         Node list = null();
 
-        do {
+        while (true) {
             if (*hasRest) {
                 report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST);
                 return false;
             }
 
-            TokenKind tt = tokenStream.getToken();
+            TokenKind tt;
+            if (!tokenStream.getToken(&tt))
+                return false;
             MOZ_ASSERT_IF(parenFreeArrow, tt == TOK_NAME);
             switch (tt) {
               case TOK_LB:
               case TOK_LC:
               {
                 /* See comment below in the TOK_NAME case. */
                 if (duplicatedArg) {
                     report(ParseError, false, duplicatedArg, JSMSG_BAD_DUP_ARGS);
@@ -1616,37 +1649,40 @@ Parser<ParseHandler>::functionArguments(
               case TOK_YIELD:
                 if (!checkYieldNameValidity())
                     return false;
                 goto TOK_NAME;
 
               case TOK_TRIPLEDOT:
               {
                 *hasRest = true;
-                tt = tokenStream.getToken();
+                if (!tokenStream.getToken(&tt))
+                    return false;
                 if (tt != TOK_NAME) {
-                    if (tt != TOK_ERROR)
-                        report(ParseError, false, null(), JSMSG_NO_REST_NAME);
+                    report(ParseError, false, null(), JSMSG_NO_REST_NAME);
                     return false;
                 }
                 goto TOK_NAME;
               }
 
               TOK_NAME:
               case TOK_NAME:
               {
                 if (parenFreeArrow)
                     funbox->setStart(tokenStream);
 
                 RootedPropertyName name(context, tokenStream.currentName());
                 bool disallowDuplicateArgs = funbox->hasDestructuringArgs || hasDefaults;
                 if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg))
                     return false;
 
-                if (tokenStream.matchToken(TOK_ASSIGN)) {
+                bool matched;
+                if (!tokenStream.matchToken(&matched, TOK_ASSIGN))
+                    return false;
+                if (matched) {
                     // A default argument without parentheses would look like:
                     // a = expr => body, but both operators are right-associative, so
                     // that would have been parsed as a = (expr => body) instead.
                     // Therefore it's impossible to get here with parenFreeArrow.
                     MOZ_ASSERT(!parenFreeArrow);
 
                     if (*hasRest) {
                         report(ParseError, false, null(), JSMSG_REST_WITH_DEFAULT);
@@ -1669,25 +1705,37 @@ Parser<ParseHandler>::functionArguments(
                     handler.setLastFunctionArgumentDefault(funcpn, def_expr);
                 }
 
                 break;
               }
 
               default:
                 report(ParseError, false, null(), JSMSG_MISSING_FORMAL);
-                /* FALL THROUGH */
-              case TOK_ERROR:
                 return false;
             }
-        } while (!parenFreeArrow && tokenStream.matchToken(TOK_COMMA));
-
-        if (!parenFreeArrow && tokenStream.getToken() != TOK_RP) {
-            report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL);
-            return false;
+
+            if (parenFreeArrow)
+                break;
+
+            bool matched;
+            if (!tokenStream.matchToken(&matched, TOK_COMMA))
+                return false;
+            if (!matched)
+                break;
+        }
+
+        if (!parenFreeArrow) {
+            TokenKind tt;
+            if (!tokenStream.getToken(&tt))
+                return false;
+            if (tt != TOK_RP) {
+                report(ParseError, false, null(), JSMSG_PAREN_AFTER_FORMAL);
+                return false;
+            }
         }
 
         if (!hasDefaults)
             funbox->length = pc->numArgs() - *hasRest;
     }
 
     return true;
 }
@@ -1972,34 +2020,32 @@ Parser<SyntaxParseHandler>::checkFunctio
         return abortIfSyntaxParser();
     }
 
     return true;
 }
 
 template <typename ParseHandler>
 bool
-Parser<ParseHandler>::addExprAndGetNextTemplStrToken(Node nodeList, TokenKind &tt)
+Parser<ParseHandler>::addExprAndGetNextTemplStrToken(Node nodeList, TokenKind *ttp)
 {
     Node pn = expr();
     if (!pn)
         return false;
     handler.addList(nodeList, pn);
 
-    tt = tokenStream.getToken();
-    if (tt != TOK_RC) {
-        if (tt != TOK_ERROR)
-            report(ParseError, false, null(), JSMSG_TEMPLSTR_UNTERM_EXPR);
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt))
         return false;
-    }
-
-    tt = tokenStream.getToken(TokenStream::TemplateTail);
-    if (tt == TOK_ERROR)
+    if (tt != TOK_RC) {
+        report(ParseError, false, null(), JSMSG_TEMPLSTR_UNTERM_EXPR);
         return false;
-    return true;
+    }
+
+    return tokenStream.getToken(ttp, TokenStream::TemplateTail);
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::taggedTemplate(Node nodeList, TokenKind tt)
 {
     Node callSiteObjNode = handler.newCallSiteObject(pos().begin, pc->blockidGen);
     if (!callSiteObjNode)
@@ -2007,17 +2053,17 @@ Parser<ParseHandler>::taggedTemplate(Nod
     handler.addList(nodeList, callSiteObjNode);
 
     while (true) {
         if (!appendToCallSiteObj(callSiteObjNode))
             return false;
         if (tt != TOK_TEMPLATE_HEAD)
             break;
 
-        if (!addExprAndGetNextTemplStrToken(nodeList, tt))
+        if (!addExprAndGetNextTemplStrToken(nodeList, &tt))
             return false;
     }
     handler.setEndPosition(nodeList, callSiteObjNode);
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
@@ -2025,17 +2071,17 @@ Parser<ParseHandler>::templateLiteral()
 {
     Node pn = noSubstitutionTemplate();
     if (!pn)
         return null();
     Node nodeList = handler.newList(PNK_TEMPLATE_STRING_LIST, pn);
 
     TokenKind tt;
     do {
-        if (!addExprAndGetNextTemplStrToken(nodeList, tt))
+        if (!addExprAndGetNextTemplStrToken(nodeList, &tt))
             return null();
 
         pn = noSubstitutionTemplate();
         if (!pn)
             return null();
 
         handler.addList(nodeList, pn);
     } while (tt == TOK_TEMPLATE_HEAD);
@@ -2416,24 +2462,32 @@ Parser<ParseHandler>::functionArgsAndBod
         report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s");
         return false;
     }
     if (type == Setter && fun->nargs() != 1) {
         report(ParseError, false, null(), JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
         return false;
     }
 
-    if (kind == Arrow && !tokenStream.matchToken(TOK_ARROW)) {
-        report(ParseError, false, null(), JSMSG_BAD_ARROW_ARGS);
-        return false;
+    if (kind == Arrow) {
+        bool matched;
+        if (!tokenStream.matchToken(&matched, TOK_ARROW))
+            return false;
+        if (!matched) {
+            report(ParseError, false, null(), JSMSG_BAD_ARROW_ARGS);
+            return false;
+        }
     }
 
     // Parse the function body.
     FunctionBodyType bodyType = StatementListBody;
-    if (tokenStream.getToken(TokenStream::Operand) != TOK_LC) {
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
+        return false;
+    if (tt != TOK_LC) {
         if (funbox->isStarGenerator()) {
             report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
             return false;
         }
 
         if (kind != Arrow)
             sawDeprecatedExpressionClosure = true;
 
@@ -2447,17 +2501,20 @@ Parser<ParseHandler>::functionArgsAndBod
         return false;
 
     if (fun->name() && !checkStrictBinding(fun->name(), pn))
         return false;
 
 #if JS_HAS_EXPR_CLOSURES
     if (bodyType == StatementListBody) {
 #endif
-        if (!tokenStream.matchToken(TOK_RC)) {
+        bool matched;
+        if (!tokenStream.matchToken(&matched, TOK_RC))
+            return false;
+        if (!matched) {
             report(ParseError, false, null(), JSMSG_CURLY_AFTER_BODY);
             return false;
         }
         funbox->bufEnd = pos().begin + 1;
 #if JS_HAS_EXPR_CLOSURES
     } else {
         if (tokenStream.hadError())
             return false;
@@ -2486,31 +2543,32 @@ Parser<ParseHandler>::checkYieldNameVali
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionStmt()
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
     RootedPropertyName name(context);
     GeneratorKind generatorKind = NotGenerator;
-    TokenKind tt = tokenStream.getToken();
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt))
+        return null();
 
     if (tt == TOK_MUL) {
         generatorKind = StarGenerator;
-        tt = tokenStream.getToken();
+        if (!tokenStream.getToken(&tt))
+            return null();
     }
 
     if (tt == TOK_NAME) {
         name = tokenStream.currentName();
     } else if (tt == TOK_YIELD) {
         if (!checkYieldNameValidity())
             return null();
         name = tokenStream.currentName();
-    } else if (tt == TOK_ERROR) {
-        return null();
     } else {
         /* Unnamed function expressions are forbidden in statement context. */
         report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
         return null();
     }
 
     /* We forbid function statements in strict mode code. */
     if (!pc->atBodyLevel() && pc->sc->needStrictChecks() &&
@@ -2522,32 +2580,33 @@ Parser<ParseHandler>::functionStmt()
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionExpr()
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
     GeneratorKind generatorKind = NotGenerator;
-    TokenKind tt = tokenStream.getToken();
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt))
+        return null();
 
     if (tt == TOK_MUL) {
         generatorKind = StarGenerator;
-        tt = tokenStream.getToken();
+        if (!tokenStream.getToken(&tt))
+            return null();
     }
 
     RootedPropertyName name(context);
     if (tt == TOK_NAME) {
         name = tokenStream.currentName();
     } else if (tt == TOK_YIELD) {
         if (!checkYieldNameValidity())
             return null();
         name = tokenStream.currentName();
-    } else if (tt == TOK_ERROR) {
-        return null();
     } else {
         tokenStream.ungetToken();
     }
 
     return functionDef(name, Normal, Expression, generatorKind);
 }
 
 /*
@@ -2706,25 +2765,24 @@ Parser<ParseHandler>::statements()
     if (!pn)
         return null();
 
     Node saveBlock = pc->blockNode;
     pc->blockNode = pn;
 
     bool canHaveDirectives = pc->atBodyLevel();
     for (;;) {
-        TokenKind tt = tokenStream.peekToken(TokenStream::Operand);
-        if (tt <= TOK_EOF || tt == TOK_RC) {
-            if (tt == TOK_ERROR) {
-                if (tokenStream.isEOF())
-                    isUnexpectedEOF_ = true;
-                return null();
-            }
+        TokenKind tt;
+        if (!tokenStream.peekToken(&tt, TokenStream::Operand)) {
+            if (tokenStream.isEOF())
+                isUnexpectedEOF_ = true;
+            return null();
+        }
+        if (tt == TOK_EOF || tt == TOK_RC)
             break;
-        }
         Node next = statement(canHaveDirectives);
         if (!next) {
             if (tokenStream.isEOF())
                 isUnexpectedEOF_ = true;
             return null();
         }
 
         if (canHaveDirectives) {
@@ -2764,18 +2822,18 @@ Parser<ParseHandler>::condition()
     }
     return pn;
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::matchLabel(MutableHandle<PropertyName*> label)
 {
-    TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand);
-    if (tt == TOK_ERROR)
+    TokenKind tt;
+    if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return false;
     if (tt == TOK_NAME) {
         tokenStream.consumeKnownToken(TOK_NAME);
         label.set(tokenStream.currentName());
     } else if (tt == TOK_YIELD) {
         tokenStream.consumeKnownToken(TOK_YIELD);
         if (!checkYieldNameValidity())
             return false;
@@ -3494,38 +3552,51 @@ Parser<ParseHandler>::letBlock(LetContex
         return null();
 
     Node pnlet = handler.newBinary(PNK_LET, vars, block);
     if (!pnlet)
         return null();
     handler.setBeginPosition(pnlet, begin);
 
     bool needExprStmt = false;
-    if (letContext == LetStatement && !tokenStream.matchToken(TOK_LC, TokenStream::Operand)) {
-        /*
-         * Strict mode eliminates a grammar ambiguity with unparenthesized
-         * LetExpressions in an ExpressionStatement. If followed immediately
-         * by an arguments list, it's ambiguous whether the let expression
-         * is the callee or the call is inside the let expression body.
-         *
-         * See bug 569464.
-         */
-        if (!report(ParseStrictError, pc->sc->strict, pnlet,
-                    JSMSG_STRICT_CODE_LET_EXPR_STMT))
-        {
-            return null();
-        }
-
-        /*
-         * If this is really an expression in let statement guise, then we
-         * need to wrap the PNK_LET node in a PNK_SEMI node so that we pop
-         * the return value of the expression.
-         */
-        needExprStmt = true;
-        letContext = LetExpresion;
+    if (letContext == LetStatement) {
+        bool matched;
+        if (!tokenStream.matchToken(&matched, TOK_LC, TokenStream::Operand))
+            return null();
+        if (!matched) {
+            /*
+             * Strict mode eliminates a grammar ambiguity with unparenthesized
+             * LetExpressions in an ExpressionStatement. If followed immediately
+             * by an arguments list, it's ambiguous whether the let expression
+             * is the callee or the call is inside the let expression body.
+             *
+             *   function id(x) { return x; }
+             *   var x = "outer";
+             *   // Does this parse as
+             *   //   (let (loc = "inner") id)(loc) // "outer"
+             *   // or as
+             *   //   let (loc = "inner") (id(loc)) // "inner"
+             *   let (loc = "inner") id(loc);
+             *
+             * See bug 569464.
+             */
+            if (!report(ParseStrictError, pc->sc->strict, pnlet,
+                        JSMSG_STRICT_CODE_LET_EXPR_STMT))
+            {
+                return null();
+            }
+
+            /*
+             * If this is really an expression in let statement guise, then we
+             * need to wrap the PNK_LET node in a PNK_SEMI node so that we pop
+             * the return value of the expression.
+             */
+            needExprStmt = true;
+            letContext = LetExpresion;
+        }
     }
 
     Node expr;
     if (letContext == LetStatement) {
         expr = statements();
         if (!expr)
             return null();
         MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
@@ -3645,116 +3716,131 @@ Parser<ParseHandler>::variables(ParseNod
     BindData<ParseHandler> data(context);
     if (kind == PNK_LET)
         data.initLet(varContext, blockObj, JSMSG_TOO_MANY_LOCALS);
     else
         data.initVarOrConst(op);
 
     bool first = true;
     Node pn2;
-    do {
-        if (psimple && !first)
-            *psimple = false;
-        first = false;
-
-        TokenKind tt = tokenStream.getToken();
-        if (tt == TOK_LB || tt == TOK_LC) {
-            if (psimple)
+    while (true) {
+        do {
+            if (psimple && !first)
                 *psimple = false;
-
-            pc->inDeclDestructuring = true;
-            pn2 = primaryExpr(tt);
-            pc->inDeclDestructuring = false;
-            if (!pn2)
+            first = false;
+
+            TokenKind tt;
+            if (!tokenStream.getToken(&tt))
                 return null();
-
-            bool ignored;
-            bool parsingForInOrOfInit = pc->parsingForInit && matchInOrOf(&ignored);
-
-            // See comment below for bindBeforeInitializer in the code that
-            // handles the non-destructuring case.
-            bool bindBeforeInitializer = kind != PNK_LET || parsingForInOrOfInit;
-            if (bindBeforeInitializer && !checkDestructuring(&data, pn2))
-                return null();
-
-            if (parsingForInOrOfInit) {
-                tokenStream.ungetToken();
+            if (tt == TOK_LB || tt == TOK_LC) {
+                if (psimple)
+                    *psimple = false;
+
+                pc->inDeclDestructuring = true;
+                pn2 = primaryExpr(tt);
+                pc->inDeclDestructuring = false;
+                if (!pn2)
+                    return null();
+
+                bool parsingForInOrOfInit = false;
+                if (pc->parsingForInit) {
+                    bool isForIn, isForOf;
+                    if (!matchInOrOf(&isForIn, &isForOf))
+                        return null();
+                    parsingForInOrOfInit = isForIn || isForOf;
+                }
+
+                // See comment below for bindBeforeInitializer in the code that
+                // handles the non-destructuring case.
+                bool bindBeforeInitializer = kind != PNK_LET || parsingForInOrOfInit;
+                if (bindBeforeInitializer && !checkDestructuring(&data, pn2))
+                    return null();
+
+                if (parsingForInOrOfInit) {
+                    tokenStream.ungetToken();
+                    handler.addList(pn, pn2);
+                    break;
+                }
+
+                MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
+
+                Node init = assignExpr();
+                if (!init)
+                    return null();
+
+                if (!bindBeforeInitializer && !checkDestructuring(&data, pn2))
+                    return null();
+
+                pn2 = handler.newBinaryOrAppend(PNK_ASSIGN, pn2, init, pc);
+                if (!pn2)
+                    return null();
                 handler.addList(pn, pn2);
-                continue;
+                break;
             }
 
-            MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
-
-            Node init = assignExpr();
-            if (!init)
-                return null();
-
-            if (!bindBeforeInitializer && !checkDestructuring(&data, pn2))
-                return null();
-
-            pn2 = handler.newBinaryOrAppend(PNK_ASSIGN, pn2, init, pc);
+            if (tt != TOK_NAME) {
+                if (tt == TOK_YIELD) {
+                    if (!checkYieldNameValidity())
+                        return null();
+                } else {
+                    report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
+                    return null();
+                }
+            }
+
+            RootedPropertyName name(context, tokenStream.currentName());
+            pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext);
             if (!pn2)
                 return null();
+            if (data.op == JSOP_DEFCONST)
+                handler.setFlag(pn2, PND_CONST);
+            data.pn = pn2;
+
             handler.addList(pn, pn2);
-            continue;
-        }
-
-        if (tt != TOK_NAME) {
-            if (tt == TOK_YIELD) {
-                if (!checkYieldNameValidity())
+
+            bool matched;
+            if (!tokenStream.matchToken(&matched, TOK_ASSIGN))
+                return null();
+            if (matched) {
+                if (psimple)
+                    *psimple = false;
+
+                // In ES6, lexical bindings may not be accessed until
+                // initialized. So a declaration of the form |let x = x| results
+                // in a ReferenceError, as the 'x' on the RHS is accessing the let
+                // binding before it is initialized.
+                //
+                // If we are not parsing a let declaration, bind the name
+                // now. Otherwise we must wait until after parsing the initializing
+                // assignment.
+                bool bindBeforeInitializer = kind != PNK_LET;
+                if (bindBeforeInitializer && !data.binder(&data, name, this))
+                    return null();
+
+                Node init = assignExpr();
+                if (!init)
+                    return null();
+
+                if (!bindBeforeInitializer && !data.binder(&data, name, this))
+                    return null();
+
+                if (!handler.finishInitializerAssignment(pn2, init, data.op))
                     return null();
             } else {
-                if (tt != TOK_ERROR)
-                    report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
-                return null();
+                if (!data.binder(&data, name, this))
+                    return null();
             }
-        }
-
-        RootedPropertyName name(context, tokenStream.currentName());
-        pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext);
-        if (!pn2)
-            return null();
-        if (data.op == JSOP_DEFCONST)
-            handler.setFlag(pn2, PND_CONST);
-        data.pn = pn2;
-
-        handler.addList(pn, pn2);
-
-        if (tokenStream.matchToken(TOK_ASSIGN)) {
-            if (psimple)
-                *psimple = false;
-
-            // In ES6, lexical bindings may not be accessed until
-            // initialized. So a declaration of the form |let x = x| results
-            // in a ReferenceError, as the 'x' on the RHS is accessing the let
-            // binding before it is initialized.
-            //
-            // If we are not parsing a let declaration, bind the name
-            // now. Otherwise we must wait until after parsing the initializing
-            // assignment.
-            bool bindBeforeInitializer = kind != PNK_LET;
-            if (bindBeforeInitializer && !data.binder(&data, name, this))
-                return null();
-
-            Node init = assignExpr();
-            if (!init)
-                return null();
-
-            if (!bindBeforeInitializer && !data.binder(&data, name, this))
-                return null();
-
-            if (!handler.finishInitializerAssignment(pn2, init, data.op))
-                return null();
-        } else {
-            if (!data.binder(&data, name, this))
-                return null();
-        }
-
-
-    } while (tokenStream.matchToken(TOK_COMMA));
+        } while (false);
+
+        bool matched;
+        if (!tokenStream.matchToken(&matched, TOK_COMMA))
+            return null();
+        if (!matched)
+            break;
+    }
 
     return pn;
 }
 
 template <>
 ParseNode *
 Parser<FullParseHandler>::letDeclaration()
 {
@@ -3895,17 +3981,20 @@ Parser<SyntaxParseHandler>::letDeclarati
 template <>
 ParseNode *
 Parser<FullParseHandler>::letStatement()
 {
     handler.disableSyntaxParser();
 
     /* Check for a let statement or let expression. */
     ParseNode *pn;
-    if (tokenStream.peekToken() == TOK_LP) {
+    TokenKind tt;
+    if (!tokenStream.peekToken(&tt))
+        return null();
+    if (tt == TOK_LP) {
         pn = letBlock(LetStatement);
         MOZ_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI));
     } else {
         pn = letDeclaration();
     }
     return pn;
 }
 
@@ -3924,17 +4013,19 @@ Parser<ParseHandler>::importDeclaration(
     MOZ_ASSERT(tokenStream.currentToken().type == TOK_IMPORT);
 
     if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) {
         report(ParseError, false, null(), JSMSG_IMPORT_DECL_AT_TOP_LEVEL);
         return null();
     }
 
     uint32_t begin = pos().begin;
-    TokenKind tt = tokenStream.getToken();
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt))
+        return null();
 
     Node importSpecSet = handler.newList(PNK_IMPORT_SPEC_LIST);
     if (!importSpecSet)
         return null();
 
     if (tt == TOK_NAME || tt == TOK_LC) {
         if (tt == TOK_NAME) {
             // Handle the form |import a from 'b'|, by adding a single import
@@ -3950,38 +4041,39 @@ Parser<ParseHandler>::importDeclaration(
                 return null();
 
             Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
             if (!importSpec)
                 return null();
 
             handler.addList(importSpecSet, importSpec);
         } else {
-            do {
+            while (true) {
                 // Handle the forms |import {} from 'a'| and
                 // |import { ..., } from 'a'| (where ... is non empty), by
                 // escaping the loop early if the next token is }.
-                tt = tokenStream.peekToken(TokenStream::KeywordIsName);
-                if (tt == TOK_ERROR)
+                if (!tokenStream.peekToken(&tt, TokenStream::KeywordIsName))
                     return null();
                 if (tt == TOK_RC)
                     break;
 
                 // If the next token is a keyword, the previous call to
                 // peekToken matched it as a TOK_NAME, and put it in the
                 // lookahead buffer, so this call will match keywords as well.
                 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
                 Node importName = newName(tokenStream.currentName());
                 if (!importName)
                     return null();
 
-                if (tokenStream.getToken() == TOK_NAME &&
-                    tokenStream.currentName() == context->names().as)
-                {
-                    if (tokenStream.getToken() != TOK_NAME) {
+                if (!tokenStream.getToken(&tt))
+                    return null();
+                if (tt == TOK_NAME && tokenStream.currentName() == context->names().as) {
+                    if (!tokenStream.getToken(&tt))
+                        return null();
+                    if (tt != TOK_NAME) {
                         report(ParseError, false, null(), JSMSG_NO_BINDING_NAME);
                         return null();
                     }
                 } else {
                     // Keywords cannot be bound to themselves, so an import name
                     // that is a keyword is a syntax error if it is not followed
                     // by the keyword 'as'.
                     if (IsKeyword(importName->name())) {
@@ -3997,24 +4089,30 @@ Parser<ParseHandler>::importDeclaration(
                 if (!bindingName)
                     return null();
 
                 Node importSpec = handler.newBinary(PNK_IMPORT_SPEC, importName, bindingName);
                 if (!importSpec)
                     return null();
 
                 handler.addList(importSpecSet, importSpec);
-            } while (tokenStream.matchToken(TOK_COMMA));
+
+                bool matched;
+                if (!tokenStream.matchToken(&matched, TOK_COMMA))
+                    return null();
+                if (!matched)
+                    break;
+            }
 
             MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_IMPORT_SPEC_LIST);
         }
 
-        if (tokenStream.getToken() != TOK_NAME ||
-            tokenStream.currentName() != context->names().from)
-        {
+        if (!tokenStream.getToken(&tt))
+            return null();
+        if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) {
             report(ParseError, false, null(), JSMSG_FROM_AFTER_IMPORT_SPEC_SET);
             return null();
         }
 
         MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
     } else {
         if (tt != TOK_STRING) {
             report(ParseError, false, null(), JSMSG_DECLARATION_AFTER_IMPORT);
@@ -4054,73 +4152,83 @@ Parser<ParseHandler>::exportDeclaration(
     if (pc->sc->isFunctionBox() || !pc->atBodyLevel()) {
         report(ParseError, false, null(), JSMSG_EXPORT_DECL_AT_TOP_LEVEL);
         return null();
     }
 
     uint32_t begin = pos().begin;
 
     Node kid;
-    switch (TokenKind tt = tokenStream.getToken()) {
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt))
+        return null();
+    switch (tt) {
       case TOK_LC:
       case TOK_MUL:
         kid = handler.newList(PNK_EXPORT_SPEC_LIST);
         if (!kid)
             return null();
 
         if (tt == TOK_LC) {
-            do {
+            while (true) {
                 // Handle the forms |export {}| and |export { ..., }| (where ...
                 // is non empty), by escaping the loop early if the next token
                 // is }.
-                tt = tokenStream.peekToken();
-                if (tt == TOK_ERROR)
+                if (!tokenStream.peekToken(&tt))
                     return null();
                 if (tt == TOK_RC)
                     break;
 
                 MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_BINDING_NAME);
                 Node bindingName = newName(tokenStream.currentName());
                 if (!bindingName)
                     return null();
 
-                if (tokenStream.getToken() == TOK_NAME &&
-                    tokenStream.currentName() == context->names().as)
-                {
-                    if (tokenStream.getToken(TokenStream::KeywordIsName) != TOK_NAME) {
+                if (!tokenStream.getToken(&tt))
+                    return null();
+                if (tt == TOK_NAME && tokenStream.currentName() == context->names().as) {
+                    if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+                        return null();
+                    if (tt != TOK_NAME) {
                         report(ParseError, false, null(), JSMSG_NO_EXPORT_NAME);
                         return null();
                     }
                 } else {
                     tokenStream.ungetToken();
                 }
                 Node exportName = newName(tokenStream.currentName());
                 if (!exportName)
                     return null();
 
                 Node exportSpec = handler.newBinary(PNK_EXPORT_SPEC, bindingName, exportName);
                 if (!exportSpec)
                     return null();
 
                 handler.addList(kid, exportSpec);
-            } while (tokenStream.matchToken(TOK_COMMA));
+
+                bool matched;
+                if (!tokenStream.matchToken(&matched, TOK_COMMA))
+                    return null();
+                if (!matched)
+                    break;
+            }
 
             MUST_MATCH_TOKEN(TOK_RC, JSMSG_RC_AFTER_EXPORT_SPEC_LIST);
         } else {
             // Handle the form |export *| by adding a special export batch
             // specifier to the list.
             Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos());
             if (!kid)
                 return null();
 
             handler.addList(kid, exportSpec);
         }
-        if (tokenStream.getToken() == TOK_NAME &&
-            tokenStream.currentName() == context->names().from)
-        {
+        if (!tokenStream.getToken(&tt))
+            return null();
+        if (tt == TOK_NAME && tokenStream.currentName() == context->names().from) {
             MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
 
             Node moduleSpec = stringLiteral();
             if (!moduleSpec)
                 return null();
 
             if (!MatchOrInsertSemicolon(tokenStream))
                 return null();
@@ -4200,30 +4308,35 @@ Parser<ParseHandler>::ifStatement()
 {
     uint32_t begin = pos().begin;
 
     /* An IF node has three kids: condition, then, and optional else. */
     Node cond = condition();
     if (!cond)
         return null();
 
-    if (tokenStream.peekToken(TokenStream::Operand) == TOK_SEMI &&
-        !report(ParseExtraWarning, false, null(), JSMSG_EMPTY_CONSEQUENT))
-    {
-        return null();
+    TokenKind tt;
+    if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+        return null();
+    if (tt == TOK_SEMI) {
+        if (!report(ParseExtraWarning, false, null(), JSMSG_EMPTY_CONSEQUENT))
+            return null();
     }
 
     StmtInfoPC stmtInfo(context);
     PushStatementPC(pc, &stmtInfo, STMT_IF);
     Node thenBranch = statement();
     if (!thenBranch)
         return null();
 
     Node elseBranch;
-    if (tokenStream.matchToken(TOK_ELSE, TokenStream::Operand)) {
+    bool matched;
+    if (!tokenStream.matchToken(&matched, TOK_ELSE, TokenStream::Operand))
+        return null();
+    if (matched) {
         stmtInfo.type = STMT_ELSE;
         elseBranch = statement();
         if (!elseBranch)
             return null();
     } else {
         elseBranch = null();
     }
 
@@ -4247,17 +4360,19 @@ Parser<ParseHandler>::doWhileStatement()
         return null();
     PopStatementPC(tokenStream, pc);
 
     // The semicolon after do-while is even more optional than most
     // semicolons in JS.  Web compat required this by 2004:
     //   http://bugzilla.mozilla.org/show_bug.cgi?id=238945
     // ES3 and ES5 disagreed, but ES6 conforms to Web reality:
     //   https://bugs.ecmascript.org/show_bug.cgi?id=157
-    tokenStream.matchToken(TOK_SEMI);
+    bool ignored;
+    if (!tokenStream.matchToken(&ignored, TOK_SEMI))
+        return null();
     return handler.newDoWhileStatement(body, cond, TokenPos(begin, pos().end));
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::whileStatement()
 {
     uint32_t begin = pos().begin;
@@ -4270,27 +4385,26 @@ Parser<ParseHandler>::whileStatement()
     if (!body)
         return null();
     PopStatementPC(tokenStream, pc);
     return handler.newWhileStatement(begin, cond, body);
 }
 
 template <typename ParseHandler>
 bool
-Parser<ParseHandler>::matchInOrOf(bool *isForOfp)
-{
-    if (tokenStream.matchToken(TOK_IN)) {
-        *isForOfp = false;
-        return true;
-    }
-    if (tokenStream.matchContextualKeyword(context->names().of)) {
-        *isForOfp = true;
-        return true;
-    }
-    return false;
+Parser<ParseHandler>::matchInOrOf(bool *isForInp, bool *isForOfp)
+{
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt))
+        return false;
+    *isForInp = tt == TOK_IN;
+    *isForOfp = tt == TOK_NAME && tokenStream.currentToken().name() == context->names().of;
+    if (!*isForInp && !*isForOfp)
+        tokenStream.ungetToken();
+    return true;
 }
 
 template <>
 bool
 Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion version,
                                                  bool isForDecl, bool isForEach,
                                                  ParseNodeKind headKind)
 {
@@ -4343,20 +4457,25 @@ Parser<FullParseHandler>::forStatement()
     uint32_t begin = pos().begin;
 
     StmtInfoPC forStmt(context);
     PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
 
     bool isForEach = false;
     unsigned iflags = 0;
 
-    if (allowsForEachIn() && tokenStream.matchContextualKeyword(context->names().each)) {
-        iflags = JSITER_FOREACH;
-        isForEach = true;
-        sawDeprecatedForEach = true;
+    if (allowsForEachIn()) {
+        bool matched;
+        if (!tokenStream.matchContextualKeyword(&matched, context->names().each))
+            return null();
+        if (matched) {
+            iflags = JSITER_FOREACH;
+            isForEach = true;
+            sawDeprecatedForEach = true;
+        }
     }
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
     /*
      * True if we have 'for (var/let/const ...)', except in the oddball case
      * where 'let' begins a let-expression in 'for (let (...) ...)'.
      */
@@ -4364,17 +4483,19 @@ Parser<FullParseHandler>::forStatement()
 
     /* Non-null when isForDecl is true for a 'for (let ...)' statement. */
     RootedStaticBlockObject blockObj(context);
 
     /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
     ParseNode *pn1;
 
     {
-        TokenKind tt = tokenStream.peekToken(TokenStream::Operand);
+        TokenKind tt;
+        if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+            return null();
         if (tt == TOK_SEMI) {
             pn1 = nullptr;
         } else {
             /*
              * Set pn1 to a var list or an initializing expression.
              *
              * Set the parsingForInit flag during parsing of the first clause
              * of the for statement.  This flag will be used by the RelExpr
@@ -4388,18 +4509,20 @@ Parser<FullParseHandler>::forStatement()
              */
             pc->parsingForInit = true;
             if (tt == TOK_VAR || tt == TOK_CONST) {
                 isForDecl = true;
                 tokenStream.consumeKnownToken(tt);
                 pn1 = variables(tt == TOK_VAR ? PNK_VAR : PNK_CONST);
             } else if (tt == TOK_LET) {
                 handler.disableSyntaxParser();
-                (void) tokenStream.getToken();
-                if (tokenStream.peekToken() == TOK_LP) {
+                tokenStream.consumeKnownToken(tt);
+                if (!tokenStream.peekToken(&tt))
+                    return null();
+                if (tt == TOK_LP) {
                     pn1 = letBlock(LetExpresion);
                 } else {
                     isForDecl = true;
                     blockObj = StaticBlockObject::create(context);
                     if (!blockObj)
                         return null();
                     pn1 = variables(PNK_LET, nullptr, blockObj, DontHoistVars);
                 }
@@ -4433,19 +4556,23 @@ Parser<FullParseHandler>::forStatement()
      * keyword here, even if JavaScript recognizes 'in' as an operator,
      * as we've excluded 'in' from being parsed in RelExpr by setting
      * pc->parsingForInit.
      */
     StmtInfoPC letStmt(context); /* used if blockObj != nullptr. */
     ParseNode *pn2, *pn3;      /* forHead->pn_kid2 and pn_kid3. */
     ParseNodeKind headKind = PNK_FORHEAD;
     if (pn1) {
-        bool isForOf;
-        if (matchInOrOf(&isForOf))
-            headKind = isForOf ? PNK_FOROF : PNK_FORIN;
+        bool isForIn, isForOf;
+        if (!matchInOrOf(&isForIn, &isForOf))
+            return null();
+        if (isForIn)
+            headKind = PNK_FORIN;
+        else if (isForOf)
+            headKind = PNK_FOROF;
     }
 
     if (headKind == PNK_FOROF || headKind == PNK_FORIN) {
         /*
          * Parse the rest of the for/in or for/of head.
          *
          * Here pn1 is everything to the left of 'in' or 'of'. At the end of
          * this block, pn1 is a decl or nullptr, pn2 is the assignment target
@@ -4597,27 +4724,32 @@ Parser<FullParseHandler>::forStatement()
             letStmt.isForLetBlock = true;
 
             forLetDecl = pn1;
             pn1 = nullptr;
         }
 
         /* Parse the loop condition or null into pn2. */
         MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
-        if (tokenStream.peekToken(TokenStream::Operand) == TOK_SEMI) {
+        TokenKind tt;
+        if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+            return null();
+        if (tt == TOK_SEMI) {
             pn2 = nullptr;
         } else {
             pn2 = expr();
             if (!pn2)
                 return null();
         }
 
         /* Parse the update expression or null into pn3. */
         MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
-        if (tokenStream.peekToken(TokenStream::Operand) == TOK_RP) {
+        if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+            return null();
+        if (tt == TOK_RP) {
             pn3 = nullptr;
         } else {
             pn3 = expr();
             if (!pn3)
                 return null();
         }
     }
 
@@ -4673,17 +4805,19 @@ Parser<SyntaxParseHandler>::forStatement
      */
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
 
     StmtInfoPC forStmt(context);
     PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
 
     /* Don't parse 'for each' loops. */
     if (allowsForEachIn()) {
-        TokenKind tt = tokenStream.peekToken();
+        TokenKind tt;
+        if (!tokenStream.peekToken(&tt))
+            return null();
         // Not all "yield" tokens are names, but the ones that aren't names are
         // invalid in this context anyway.
         if (tt == TOK_NAME || tt == TOK_YIELD) {
             JS_ALWAYS_FALSE(abortIfSyntaxParser());
             return null();
         }
     }
 
@@ -4692,17 +4826,19 @@ Parser<SyntaxParseHandler>::forStatement
     /* True if we have 'for (var ...)'. */
     bool isForDecl = false;
     bool simpleForDecl = true;
 
     /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */
     Node lhsNode;
 
     {
-        TokenKind tt = tokenStream.peekToken(TokenStream::Operand);
+        TokenKind tt;
+        if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+            return null();
         if (tt == TOK_SEMI) {
             lhsNode = null();
         } else {
             /* Set lhsNode to a var list or an initializing expression. */
             pc->parsingForInit = true;
             if (tt == TOK_VAR) {
                 isForDecl = true;
                 tokenStream.consumeKnownToken(tt);
@@ -4722,18 +4858,22 @@ Parser<SyntaxParseHandler>::forStatement
     }
 
     /*
      * We can be sure that it's a for/in loop if there's still an 'in'
      * keyword here, even if JavaScript recognizes 'in' as an operator,
      * as we've excluded 'in' from being parsed in RelExpr by setting
      * pc->parsingForInit.
      */
-    bool isForOf;
-    if (lhsNode && matchInOrOf(&isForOf)) {
+    bool isForIn = false, isForOf = false;
+    if (lhsNode) {
+        if (!matchInOrOf(&isForIn, &isForOf))
+            return null();
+    }
+    if (isForIn || isForOf) {
         /* Parse the rest of the for/in or for/of head. */
         forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
 
         /* Check that the left side of the 'in' or 'of' is valid. */
         if (!isForDecl &&
             lhsNode != SyntaxParseHandler::NodeName &&
             lhsNode != SyntaxParseHandler::NodeGetProp &&
             lhsNode != SyntaxParseHandler::NodeLValue)
@@ -4750,24 +4890,29 @@ Parser<SyntaxParseHandler>::forStatement
         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(TokenStream::Operand) != TOK_SEMI) {
+        TokenKind tt;
+        if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+            return null();
+        if (tt != TOK_SEMI) {
             if (!expr())
                 return null();
         }
 
         /* Parse the update expression or null. */
         MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_COND);
-        if (tokenStream.peekToken(TokenStream::Operand) != TOK_RP) {
+        if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+            return null();
+        if (tt != TOK_RP) {
             if (!expr())
                 return null();
         }
     }
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
 
     /* Parse the loop body. */
@@ -4804,17 +4949,21 @@ Parser<ParseHandler>::switchStatement()
     if (!caseList)
         return null();
 
     Node saveBlock = pc->blockNode;
     pc->blockNode = caseList;
 
     bool seenDefault = false;
     TokenKind tt;
-    while ((tt = tokenStream.getToken()) != TOK_RC) {
+    while (true) {
+        if (!tokenStream.getToken(&tt))
+            return null();
+        if (tt == TOK_RC)
+            break;
         uint32_t caseBegin = pos().begin;
 
         Node caseExpr;
         switch (tt) {
           case TOK_DEFAULT:
             if (seenDefault) {
                 report(ParseError, false, null(), JSMSG_TOO_MANY_DEFAULTS);
                 return null();
@@ -4824,34 +4973,32 @@ Parser<ParseHandler>::switchStatement()
             break;
 
           case TOK_CASE:
             caseExpr = expr();
             if (!caseExpr)
                 return null();
             break;
 
-          case TOK_ERROR:
-            return null();
-
           default:
             report(ParseError, false, null(), JSMSG_BAD_SWITCH);
             return null();
         }
 
         MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_AFTER_CASE);
 
         Node body = handler.newStatementList(pc->blockid(), pos());
         if (!body)
             return null();
 
-        while ((tt = tokenStream.peekToken(TokenStream::Operand)) != TOK_RC &&
-               tt != TOK_CASE && tt != TOK_DEFAULT) {
-            if (tt == TOK_ERROR)
+        while (true) {
+            if (!tokenStream.peekToken(&tt, TokenStream::Operand))
                 return null();
+            if (tt == TOK_RC || tt == TOK_CASE || tt == TOK_DEFAULT)
+                break;
             Node stmt = statement();
             if (!stmt)
                 return null();
             handler.addList(body, stmt);
         }
 
         // In ES6, lexical bindings canot be accessed until initialized. If
         // there was a 'let' declaration in the case we just parsed, remember
@@ -4984,19 +5131,20 @@ Parser<ParseHandler>::returnStatement()
         report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
         return null();
     }
 
     // Parse an optional operand.
     //
     // This is ugly, but we don't want to require a semicolon.
     Node exprNode;
-    switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
-      case TOK_ERROR:
-        return null();
+    TokenKind tt;
+    if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
+        return null();
+    switch (tt) {
       case TOK_EOF:
       case TOK_EOL:
       case TOK_SEMI:
       case TOK_RC:
         exprNode = null();
         pc->funHasReturnVoid = true;
         break;
       default: {
@@ -5050,19 +5198,20 @@ Parser<ParseHandler>::yieldExpression()
       case StarGenerator:
       {
         MOZ_ASSERT(pc->sc->isFunctionBox());
 
         pc->lastYieldOffset = begin;
 
         Node exprNode;
         ParseNodeKind kind = PNK_YIELD;
-        switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
-          case TOK_ERROR:
-            return null();
+        TokenKind tt;
+        if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
+            return null();
+        switch (tt) {
           // TOK_EOL is special; it implements the [no LineTerminator here]
           // quirk in the grammar.
           case TOK_EOL:
           // The rest of these make up the complete set of tokens that can
           // appear after any of the places where AssignmentExpression is used
           // throughout the grammar.  Conveniently, none of them can also be the
           // start an expression.
           case TOK_EOF:
@@ -5117,19 +5266,20 @@ Parser<ParseHandler>::yieldExpression()
         // We are in a legacy generator: a function that has already seen a
         // yield, or in a legacy generator comprehension.
         MOZ_ASSERT(pc->sc->isFunctionBox());
 
         pc->lastYieldOffset = begin;
 
         // Legacy generators do not require a value.
         Node exprNode;
-        switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
-          case TOK_ERROR:
-            return null();
+        TokenKind tt;
+        if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
+            return null();
+        switch (tt) {
           case TOK_EOF:
           case TOK_EOL:
           case TOK_SEMI:
           case TOK_RC:
           case TOK_RB:
           case TOK_RP:
           case TOK_COLON:
           case TOK_COMMA:
@@ -5256,18 +5406,18 @@ Parser<ParseHandler>::labeledStatement()
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::throwStatement()
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_THROW));
     uint32_t begin = pos().begin;
 
     /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */
-    TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand);
-    if (tt == TOK_ERROR)
+    TokenKind tt;
+    if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
         return null();
     if (tt == TOK_EOF || tt == TOK_SEMI || tt == TOK_RC) {
         report(ParseError, false, null(), JSMSG_MISSING_EXPR_AFTER_THROW);
         return null();
     }
     if (tt == TOK_EOL) {
         report(ParseError, false, null(), JSMSG_LINE_BREAK_AFTER_THROW);
         return null();
@@ -5315,17 +5465,19 @@ Parser<ParseHandler>::tryStatement()
     Node innerBlock = statements();
     if (!innerBlock)
         return null();
     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_TRY);
     PopStatementPC(tokenStream, pc);
 
     bool hasUnconditionalCatch = false;
     Node catchList = null();
-    TokenKind tt = tokenStream.getToken();
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt))
+        return null();
     if (tt == TOK_CATCH) {
         catchList = handler.newList(PNK_CATCH);
         if (!catchList)
             return null();
 
         do {
             Node pnblock;
             BindData<ParseHandler> data(context);
@@ -5358,17 +5510,18 @@ Parser<ParseHandler>::tryStatement()
              * Contrary to ECMA Ed. 3, the catch variable is lexically
              * scoped, not a property of a new Object instance.  This is
              * an intentional change that anticipates ECMA Ed. 4.
              */
             data.initLet(HoistVars, &pc->staticScope->template as<StaticBlockObject>(),
                          JSMSG_TOO_MANY_CATCH_VARS);
             MOZ_ASSERT(data.let.blockObj);
 
-            tt = tokenStream.getToken();
+            if (!tokenStream.getToken(&tt))
+                return null();
             Node catchName;
             switch (tt) {
               case TOK_LB:
               case TOK_LC:
                 catchName = destructuringExpr(&data, tt);
                 if (!catchName)
                     return null();
                 break;
@@ -5396,17 +5549,20 @@ Parser<ParseHandler>::tryStatement()
 
             Node catchGuard = null();
 #if JS_HAS_CATCH_GUARD
             /*
              * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
              * to avoid conflicting with the JS2/ECMAv4 type annotation
              * catchguard syntax.
              */
-            if (tokenStream.matchToken(TOK_IF)) {
+            bool matched;
+            if (!tokenStream.matchToken(&matched, TOK_IF))
+                return null();
+            if (matched) {
                 catchGuard = expr();
                 if (!catchGuard)
                     return null();
             }
 #endif
             MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
 
             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
@@ -5419,17 +5575,18 @@ Parser<ParseHandler>::tryStatement()
             if (!catchGuard)
                 hasUnconditionalCatch = true;
 
             if (!handler.addCatchBlock(catchList, pnblock, catchName, catchGuard, catchBody))
                 return null();
             handler.setEndPosition(catchList, pos().end);
             handler.setEndPosition(pnblock, pos().end);
 
-            tt = tokenStream.getToken(TokenStream::Operand);
+            if (!tokenStream.getToken(&tt, TokenStream::Operand))
+                return null();
         } while (tt == TOK_CATCH);
     }
 
     Node finallyBlock = null();
 
     if (tt == TOK_FINALLY) {
         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
         if (!PushBlocklikeStatement(tokenStream, &stmtInfo, STMT_FINALLY, pc))
@@ -5467,17 +5624,20 @@ Parser<ParseHandler>::debuggerStatement(
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::statement(bool canHaveDirectives)
 {
     JS_CHECK_RECURSION(context, return null());
 
-    switch (TokenKind tt = tokenStream.getToken(TokenStream::Operand)) {
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
+        return null();
+    switch (tt) {
       case TOK_LC:
         return blockStatement();
 
       case TOK_CONST:
         if (!abortIfSyntaxParser())
             return null();
         // FALL THROUGH
       case TOK_VAR: {
@@ -5532,66 +5692,82 @@ Parser<ParseHandler>::statement(bool can
       case TOK_CATCH:
         report(ParseError, false, null(), JSMSG_CATCH_WITHOUT_TRY);
         return null();
 
       case TOK_FINALLY:
         report(ParseError, false, null(), JSMSG_FINALLY_WITHOUT_TRY);
         return null();
 
-      case TOK_ERROR:
-        return null();
-
       case TOK_STRING:
         if (!canHaveDirectives && tokenStream.currentToken().atom() == context->names().useAsm) {
             if (!abortIfSyntaxParser())
                 return null();
             if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL))
                 return null();
         }
         return expressionStatement();
 
-      case TOK_YIELD:
-        if (tokenStream.peekToken() == TOK_COLON) {
+      case TOK_YIELD: {
+        TokenKind next;
+        if (!tokenStream.peekToken(&next))
+            return null();
+        if (next == TOK_COLON) {
             if (!checkYieldNameValidity())
                 return null();
             return labeledStatement();
         }
         return expressionStatement();
-
-      case TOK_NAME:
-        if (tokenStream.peekToken() == TOK_COLON)
+      }
+
+      case TOK_NAME: {
+        TokenKind next;
+        if (!tokenStream.peekToken(&next))
+            return null();
+        if (next == TOK_COLON)
             return labeledStatement();
         return expressionStatement();
+      }
 
       default:
         return expressionStatement();
     }
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::expr()
 {
     Node pn = assignExpr();
-    if (pn && tokenStream.matchToken(TOK_COMMA)) {
+    if (!pn)
+        return null();
+
+    bool matched;
+    if (!tokenStream.matchToken(&matched, TOK_COMMA))
+        return null();
+    if (matched) {
         Node seq = handler.newList(PNK_COMMA, pn);
         if (!seq)
             return null();
-        do {
+        while (true) {
             if (handler.isUnparenthesizedYield(pn)) {
                 report(ParseError, false, pn, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
                 return null();
             }
 
             pn = assignExpr();
             if (!pn)
                 return null();
             handler.addList(seq, pn);
-        } while (tokenStream.matchToken(TOK_COMMA));
+
+            if (!tokenStream.matchToken(&matched, TOK_COMMA))
+                return null();
+            if (!matched)
+                break;
+        }
         return seq;
     }
     return pn;
 }
 
 static const JSOp ParseNodeKindToJSOp[] = {
     JSOP_OR,
     JSOP_AND,
@@ -5699,18 +5875,18 @@ Parser<ParseHandler>::orExpr1()
     Node pn;
     for (;;) {
         pn = unaryExpr();
         if (!pn)
             return pn;
 
         // If a binary operator follows, consume it and compute the
         // corresponding operator.
-        TokenKind tok = tokenStream.getToken();
-        if (tok == TOK_ERROR)
+        TokenKind tok;
+        if (!tokenStream.getToken(&tok))
             return null();
         ParseNodeKind pnk;
         if (IsBinaryOpToken(tok, oldParsingForInit)) {
             pnk = BinaryOpTokenKindToParseNodeKind(tok);
         } else {
             tok = TOK_EOF;
             pnk = PNK_LIMIT;
         }
@@ -5767,17 +5943,20 @@ Parser<ParseHandler>::condExpr1()
         return null();
 
     MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
 
     Node elseExpr = assignExpr();
     if (!elseExpr)
         return null();
 
-    tokenStream.getToken(); /* read one token past the end */
+    // Advance to the next token; the caller is responsible for interpreting it.
+    TokenKind ignored;
+    if (!tokenStream.getToken(&ignored))
+        return null();
     return handler.newConditional(condition, thenExpr, elseExpr);
 }
 
 template <>
 bool
 Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode *pn, AssignmentFlavor flavor)
 {
     switch (pn->getKind()) {
@@ -5856,26 +6035,42 @@ Parser<ParseHandler>::assignExpr()
     //
     // (In Parsemark this happens 81.4% of the time;  in code with large
     // numeric arrays, such as some Kraken benchmarks, it happens more often.)
     //
     // In such cases, we can avoid the full expression parsing route through
     // assignExpr(), condExpr1(), orExpr1(), unaryExpr(), memberExpr(), and
     // primaryExpr().
 
-    TokenKind tt = tokenStream.getToken(TokenStream::Operand);
-
-    if (tt == TOK_NAME && tokenStream.nextTokenEndsExpr())
-        return identifierName();
-
-    if (tt == TOK_NUMBER && tokenStream.nextTokenEndsExpr())
-        return newNumber(tokenStream.currentToken());
-
-    if (tt == TOK_STRING && tokenStream.nextTokenEndsExpr())
-        return stringLiteral();
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
+        return null();
+
+    bool endsExpr;
+
+    if (tt == TOK_NAME) {
+        if (!tokenStream.nextTokenEndsExpr(&endsExpr))
+            return null();
+        if (endsExpr)
+            return identifierName();
+    }
+
+    if (tt == TOK_NUMBER) {
+        if (!tokenStream.nextTokenEndsExpr(&endsExpr))
+            return null();
+        if (endsExpr)
+            return newNumber(tokenStream.currentToken());
+    }
+
+    if (tt == TOK_STRING) {
+        if (!tokenStream.nextTokenEndsExpr(&endsExpr))
+            return null();
+        if (endsExpr)
+            return stringLiteral();
+    }
 
     if (tt == TOK_YIELD && (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()))
         return yieldExpression();
 
     tokenStream.ungetToken();
 
     // Save the tokenizer state in case we find an arrow function and have to
     // rewind.
@@ -5902,19 +6097,19 @@ Parser<ParseHandler>::assignExpr()
       case TOK_DIVASSIGN:    kind = PNK_DIVASSIGN;    op = JSOP_DIV;    break;
       case TOK_MODASSIGN:    kind = PNK_MODASSIGN;    op = JSOP_MOD;    break;
 
       case TOK_ARROW: {
         tokenStream.seek(start);
         if (!abortIfSyntaxParser())
             return null();
 
-        if (tokenStream.getToken() == TOK_ERROR)
-            return null();
-        tokenStream.ungetToken();
+        TokenKind ignored;
+        if (!tokenStream.peekToken(&ignored))
+            return null();
 
         return functionDef(NullPtr(), Normal, Arrow, NotGenerator);
       }
 
       default:
         MOZ_ASSERT(!tokenStream.isCurrentTokenAssignment());
         tokenStream.ungetToken();
         return lhs;
@@ -5988,17 +6183,19 @@ Parser<ParseHandler>::unaryOpExpr(ParseN
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::unaryExpr()
 {
     Node pn, pn2;
 
     JS_CHECK_RECURSION(context, return null());
 
-    TokenKind tt = tokenStream.getToken(TokenStream::Operand);
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
+        return null();
     uint32_t begin = pos().begin;
     switch (tt) {
       case TOK_TYPEOF:
         return unaryOpExpr(PNK_TYPEOF, JSOP_TYPEOF, begin);
       case TOK_VOID:
         return unaryOpExpr(PNK_VOID, JSOP_VOID, begin);
       case TOK_NOT:
         return unaryOpExpr(PNK_NOT, JSOP_NOT, begin);
@@ -6007,17 +6204,19 @@ Parser<ParseHandler>::unaryExpr()
       case TOK_ADD:
         return unaryOpExpr(PNK_POS, JSOP_POS, begin);
       case TOK_SUB:
         return unaryOpExpr(PNK_NEG, JSOP_NEG, begin);
 
       case TOK_INC:
       case TOK_DEC:
       {
-        TokenKind tt2 = tokenStream.getToken(TokenStream::Operand);
+        TokenKind tt2;
+        if (!tokenStream.getToken(&tt2, TokenStream::Operand))
+            return null();
         pn2 = memberExpr(tt2, true);
         if (!pn2)
             return null();
         if (!checkAndMarkAsIncOperand(pn2, tt, true))
             return null();
         return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
                                 JSOP_NOP,
                                 begin,
@@ -6035,26 +6234,24 @@ Parser<ParseHandler>::unaryExpr()
             if (!report(ParseStrictError, pc->sc->strict, expr, JSMSG_DEPRECATED_DELETE_OPERAND))
                 return null();
             pc->sc->setBindingsAccessedDynamically();
         }
 
         return handler.newDelete(begin, expr);
       }
 
-      case TOK_ERROR:
-        return null();
-
       default:
         pn = memberExpr(tt, true);
         if (!pn)
             return null();
 
         /* Don't look across a newline boundary for a postfix incop. */
-        tt = tokenStream.peekTokenSameLine(TokenStream::Operand);
+        if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
+            return null();
         if (tt == TOK_INC || tt == TOK_DEC) {
             tokenStream.consumeKnownToken(tt);
             if (!checkAndMarkAsIncOperand(pn, tt, false))
                 return null();
             return handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT,
                                     JSOP_NOP,
                                     begin,
                                     pn);
@@ -6418,36 +6615,42 @@ Parser<FullParseHandler>::legacyComprehe
         return null();
 
     if (!transplanter.transplant(bodyExpr))
         return null();
 
     MOZ_ASSERT(pc->staticScope && pc->staticScope == pn->pn_objbox->object);
     data.initLet(HoistVars, &pc->staticScope->as<StaticBlockObject>(), JSMSG_ARRAY_INIT_TOO_BIG);
 
-    do {
+    while (true) {
         /*
          * FOR node is binary, left is loop control and right is body.  Use
          * index to count each block-local let-variable on the left-hand side
          * of the in/of.
          */
         pn2 = BinaryNode::create(PNK_FOR, &handler);
         if (!pn2)
             return null();
 
         pn2->setOp(JSOP_ITER);
         pn2->pn_iflags = JSITER_ENUMERATE;
-        if (allowsForEachIn() && tokenStream.matchContextualKeyword(context->names().each))
-            pn2->pn_iflags |= JSITER_FOREACH;
+        if (allowsForEachIn()) {
+            bool matched;
+            if (!tokenStream.matchContextualKeyword(&matched, context->names().each))
+                return null();
+            if (matched)
+                pn2->pn_iflags |= JSITER_FOREACH;
+        }
         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
         uint32_t startYieldOffset = pc->lastYieldOffset;
 
         RootedPropertyName name(context);
-        tt = tokenStream.getToken();
+        if (!tokenStream.getToken(&tt))
+            return null();
         switch (tt) {
           case TOK_LB:
           case TOK_LC:
             pc->inDeclDestructuring = true;
             pn3 = primaryExpr(tt);
             pc->inDeclDestructuring = false;
             if (!pn3)
                 return null();
@@ -6465,23 +6668,23 @@ Parser<FullParseHandler>::legacyComprehe
              */
             pn3 = newBindingNode(name, false);
             if (!pn3)
                 return null();
             break;
 
           default:
             report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
-
-          case TOK_ERROR:
-            return null();
-        }
-
-        bool isForOf;
-        if (!matchInOrOf(&isForOf)) {
+            return null();
+        }
+
+        bool isForIn, isForOf;
+        if (!matchInOrOf(&isForIn, &isForOf))
+            return null();
+        if (!isForIn && !isForOf) {
             report(ParseError, false, null(), JSMSG_IN_AFTER_FOR_NAME);
             return null();
         }
         ParseNodeKind headKind = PNK_FORIN;
         if (isForOf) {
             if (pn2->pn_iflags != JSITER_ENUMERATE) {
                 MOZ_ASSERT(pn2->pn_iflags == (JSITER_FOREACH | JSITER_ENUMERATE));
                 report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
@@ -6555,19 +6758,28 @@ Parser<FullParseHandler>::legacyComprehe
         if (!pn3)
             return null();
 
         pn2->pn_left = handler.newTernary(headKind, lets, pn3, pn4);
         if (!pn2->pn_left)
             return null();
         *pnp = pn2;
         pnp = &pn2->pn_right;
-    } while (tokenStream.matchToken(TOK_FOR));
-
-    if (tokenStream.matchToken(TOK_IF)) {
+
+        bool matched;
+        if (!tokenStream.matchToken(&matched, TOK_FOR))
+            return null();
+        if (!matched)
+            break;
+    }
+
+    bool matched;
+    if (!tokenStream.matchToken(&matched, TOK_IF))
+        return null();
+    if (matched) {
         pn2 = TernaryNode::create(PNK_IF, &handler);
         if (!pn2)
             return null();
         pn2->pn_kid1 = condition();
         if (!pn2->pn_kid1)
             return null();
         *pnp = pn2;
         pnp = &pn2->pn_kid2;
@@ -6823,17 +7035,20 @@ Parser<ParseHandler>::comprehensionFor(G
     // FIXME: Destructuring binding (bug 980828).
 
     MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_VARIABLE_NAME);
     RootedPropertyName name(context, tokenStream.currentName());
     if (name == context->names().let) {
         report(ParseError, false, null(), JSMSG_LET_COMP_BINDING);
         return null();
     }
-    if (!tokenStream.matchContextualKeyword(context->names().of)) {
+    bool matched;
+    if (!tokenStream.matchContextualKeyword(&matched, context->names().of))
+        return null();
+    if (!matched) {
         report(ParseError, false, null(), JSMSG_OF_AFTER_FOR_NAME);
         return null();
     }
 
     Node rhs = assignExpr();
     if (!rhs)
         return null();
 
@@ -6910,20 +7125,25 @@ Parser<ParseHandler>::comprehensionIf(Ge
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::comprehensionTail(GeneratorKind comprehensionKind)
 {
     JS_CHECK_RECURSION(context, return null());
 
-    if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand))
+    bool matched;
+    if (!tokenStream.matchToken(&matched, TOK_FOR, TokenStream::Operand))
+        return null();
+    if (matched)
         return comprehensionFor(comprehensionKind);
 
-    if (tokenStream.matchToken(TOK_IF, TokenStream::Operand))
+    if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand))
+        return null();
+    if (matched)
         return comprehensionIf(comprehensionKind);
 
     uint32_t begin = pos().begin;
 
     Node bodyExpr = assignExpr();
     if (!bodyExpr)
         return null();
 
@@ -7021,69 +7241,98 @@ Parser<ParseHandler>::assignExprWithoutY
     }
     return res;
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::argumentList(Node listNode, bool *isSpread)
 {
-    if (tokenStream.matchToken(TOK_RP, TokenStream::Operand)) {
+    bool matched;
+    if (!tokenStream.matchToken(&matched, TOK_RP, TokenStream::Operand))
+        return false;
+    if (matched) {
         handler.setEndPosition(listNode, pos().end);
         return true;
     }
 
     uint32_t startYieldOffset = pc->lastYieldOffset;
     bool arg0 = true;
 
-    do {
+    while (true) {
         bool spread = false;
         uint32_t begin = 0;
-        if (tokenStream.matchToken(TOK_TRIPLEDOT, TokenStream::Operand)) {
+        if (!tokenStream.matchToken(&matched, TOK_TRIPLEDOT, TokenStream::Operand))
+            return false;
+        if (matched) {
             spread = true;
             begin = pos().begin;
             *isSpread = true;
         }
 
         Node argNode = assignExpr();
         if (!argNode)
             return false;
         if (spread) {
             argNode = handler.newUnary(PNK_SPREAD, JSOP_NOP, begin, argNode);
             if (!argNode)
-                return null();
-        }
-
-        if (handler.isOperationWithoutParens(argNode, PNK_YIELD) &&
-            tokenStream.peekToken() == TOK_COMMA) {
-            report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
-            return false;
+                return false;
+        }
+
+        if (handler.isOperationWithoutParens(argNode, PNK_YIELD)) {
+            TokenKind tt;
+            if (!tokenStream.peekToken(&tt))
+                return false;
+            if (tt == TOK_COMMA) {
+                report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
+                return false;
+            }
         }
 #if JS_HAS_GENERATOR_EXPRS
-        if (!spread && tokenStream.matchToken(TOK_FOR)) {
-            if (pc->lastYieldOffset != startYieldOffset) {
-                reportWithOffset(ParseError, false, pc->lastYieldOffset,
-                                 JSMSG_BAD_GENEXP_BODY, js_yield_str);
+        if (!spread) {
+            if (!tokenStream.matchToken(&matched, TOK_FOR))
                 return false;
-            }
-            argNode = legacyGeneratorExpr(argNode);
-            if (!argNode)
-                return false;
-            if (!arg0 || tokenStream.peekToken() == TOK_COMMA) {
-                report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
-                return false;
+            if (matched) {
+                if (pc->lastYieldOffset != startYieldOffset) {
+                    reportWithOffset(ParseError, false, pc->lastYieldOffset,
+                                     JSMSG_BAD_GENEXP_BODY, js_yield_str);
+                    return false;
+                }
+                argNode = legacyGeneratorExpr(argNode);
+                if (!argNode)
+                    return false;
+                if (!arg0) {
+                    report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
+                    return false;
+                }
+                TokenKind tt;
+                if (!tokenStream.peekToken(&tt))
+                    return false;
+                if (tt == TOK_COMMA) {
+                    report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
+                    return false;
+                }
             }
         }
 #endif
         arg0 = false;
 
         handler.addList(listNode, argNode);
-    } while (tokenStream.matchToken(TOK_COMMA));
-
-    if (tokenStream.getToken() != TOK_RP) {
+
+        bool matched;
+        if (!tokenStream.matchToken(&matched, TOK_COMMA))
+            return false;
+        if (!matched)
+            break;
+    }
+
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt))
+        return false;
+    if (tt != TOK_RP) {
         report(ParseError, false, null(), JSMSG_PAREN_AFTER_ARGS);
         return false;
     }
     handler.setEndPosition(listNode, pos().end);
     return true;
 }
 
 template <typename ParseHandler>
@@ -7097,41 +7346,49 @@ Parser<ParseHandler>::memberExpr(TokenKi
     JS_CHECK_RECURSION(context, return null());
 
     /* Check for new expression first. */
     if (tt == TOK_NEW) {
         lhs = handler.newList(PNK_NEW, null(), JSOP_NEW);
         if (!lhs)
             return null();
 
-        tt = tokenStream.getToken(TokenStream::Operand);
+        if (!tokenStream.getToken(&tt, TokenStream::Operand))
+            return null();
         Node ctorExpr = memberExpr(tt, false);
         if (!ctorExpr)
             return null();
 
         handler.addList(lhs, ctorExpr);
 
-        if (tokenStream.matchToken(TOK_LP)) {
+        bool matched;
+        if (!tokenStream.matchToken(&matched, TOK_LP))
+            return null();
+        if (matched) {
             bool isSpread = false;
             if (!argumentList(lhs, &isSpread))
                 return null();
             if (isSpread)
                 handler.setOp(lhs, JSOP_SPREADNEW);
         }
     } else {
         lhs = primaryExpr(tt);
         if (!lhs)
             return null();
     }
 
-    while ((tt = tokenStream.getToken()) > TOK_EOF) {
+    while (true) {
+        if (!tokenStream.getToken(&tt))
+            return null();
+        if (tt == TOK_EOF)
+            break;
+
         Node nextMember;
         if (tt == TOK_DOT) {
-            tt = tokenStream.getToken(TokenStream::KeywordIsName);
-            if (tt == TOK_ERROR)
+            if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
                 return null();
             if (tt == TOK_NAME) {
                 PropertyName *field = tokenStream.currentName();
                 nextMember = handler.newPropertyAccess(lhs, field, pos().end);
                 if (!nextMember)
                     return null();
             } else {
                 report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT);
@@ -7200,18 +7457,16 @@ Parser<ParseHandler>::memberExpr(TokenKi
             handler.setOp(nextMember, op);
         } else {
             tokenStream.ungetToken();
             return lhs;
         }
 
         lhs = nextMember;
     }
-    if (tt == TOK_ERROR)
-        return null();
     return lhs;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::newName(PropertyName *name)
 {
     return handler.newName(name, pc->blockid(), pos());
@@ -7286,35 +7541,42 @@ Parser<ParseHandler>::arrayInitializer()
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LB));
 
     uint32_t begin = pos().begin;
     Node literal = handler.newArrayLiteral(begin, pc->blockidGen);
     if (!literal)
         return null();
 
-    if (tokenStream.matchToken(TOK_RB, TokenStream::Operand)) {
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
+        return null();
+    if (tt == TOK_RB) {
         /*
          * Mark empty arrays as non-constant, since we cannot easily
          * determine their type.
          */
         handler.setListFlag(literal, PNX_NONCONST);
-    } else if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand)) {
+    } else if (tt == TOK_FOR) {
         // ES6 array comprehension.
         return arrayComprehension(begin);
     } else {
+        tokenStream.ungetToken();
+
         bool spread = false, missingTrailingComma = false;
         uint32_t index = 0;
         for (; ; index++) {
             if (index == NativeObject::NELEMENTS_LIMIT) {
                 report(ParseError, false, null(), JSMSG_ARRAY_INIT_TOO_BIG);
                 return null();
             }
 
-            TokenKind tt = tokenStream.peekToken(TokenStream::Operand);
+            TokenKind tt;
+            if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+                return null();
             if (tt == TOK_RB)
                 break;
 
             if (tt == TOK_COMMA) {
                 tokenStream.consumeKnownToken(TOK_COMMA);
                 if (!handler.addElision(literal, pos()))
                     return null();
             } else if (tt == TOK_TRIPLEDOT) {
@@ -7333,17 +7595,20 @@ Parser<ParseHandler>::arrayInitializer()
                 if (foldConstants && !FoldConstants(context, &element, this))
                     return null();
                 if (!handler.addArrayElement(literal, element))
                     return null();
             }
 
             if (tt != TOK_COMMA) {
                 /* If we didn't already match TOK_COMMA in above case. */
-                if (!tokenStream.matchToken(TOK_COMMA)) {
+                bool matched;
+                if (!tokenStream.matchToken(&matched, TOK_COMMA))
+                    return null();
+                if (!matched) {
                     missingTrailingComma = true;
                     break;
                 }
             }
         }
 
         /*
          * At this point, (index == 0 && missingTrailingComma) implies one
@@ -7386,18 +7651,23 @@ Parser<ParseHandler>::arrayInitializer()
          * time. A block-local var is accessed by the JSOP_GETLOCAL and
          * JSOP_SETLOCAL ops. These ops have an immediate operand, the local
          * slot's stack index from fp->spbase.
          *
          * The legacy array comprehension iteration step, array.push(i * j) in
          * the example above, is done by <i * j>; JSOP_ARRAYPUSH <array>, where
          * <array> is the index of array's stack slot.
          */
-        if (index == 0 && !spread && tokenStream.matchToken(TOK_FOR) && missingTrailingComma)
-            return legacyArrayComprehension(literal);
+        if (index == 0 && !spread) {
+            bool matched;
+            if (!tokenStream.matchToken(&matched, TOK_FOR))
+                return null();
+            if (matched && missingTrailingComma)
+                return legacyArrayComprehension(literal);
+        }
 
         MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
     }
     handler.setEndPosition(literal, pos().end);
     return literal;
 }
 
 static JSAtom*
@@ -7441,33 +7711,34 @@ Parser<ParseHandler>::objectLiteral()
 
     Node literal = handler.newObjectLiteral(pos().begin);
     if (!literal)
         return null();
 
     bool seenPrototypeMutation = false;
     RootedAtom atom(context);
     for (;;) {
-        TokenKind ltok = tokenStream.getToken(TokenStream::KeywordIsName);
+        TokenKind ltok;
+        if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
+            return null();
         if (ltok == TOK_RC)
             break;
 
         bool isGenerator = false;
         if (ltok == TOK_MUL) {
             isGenerator = true;
-            ltok = tokenStream.getToken(TokenStream::KeywordIsName);
+            if (!tokenStream.getToken(&ltok, TokenStream::KeywordIsName))
+                return null();
         }
 
         atom = nullptr;
 
         JSOp op = JSOP_INITPROP;
         Node propname;
         switch (ltok) {
-          case TOK_ERROR:
-            return null();
           case TOK_NUMBER:
             atom = DoubleToAtom(context, tokenStream.currentToken().number());
             if (!atom)
                 return null();
             propname = newNumber(tokenStream.currentToken());
             break;
 
           case TOK_LB: {
@@ -7495,17 +7766,19 @@ Parser<ParseHandler>::objectLiteral()
                 propname = handler.newIdentifier(atom, pos());
                 if (!propname)
                     return null();
                 break;
             }
 
             // We have parsed |get| or |set|. Look for an accessor property
             // name next.
-            TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
+            TokenKind tt;
+            if (!tokenStream.getToken(&tt, TokenStream::KeywordIsName))
+                return null();
             if (tt == TOK_NAME) {
                 atom = tokenStream.currentName();
                 propname = newName(atom->asPropertyName());
                 if (!propname)
                     return null();
             } else if (tt == TOK_STRING) {
                 atom = tokenStream.currentToken().atom();
 
@@ -7563,18 +7836,18 @@ Parser<ParseHandler>::objectLiteral()
           }
 
           default:
             report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
             return null();
         }
 
         if (op == JSOP_INITPROP) {
-            TokenKind tt = tokenStream.getToken();
-            if (tt == TOK_ERROR)
+            TokenKind tt;
+            if (!tokenStream.getToken(&tt))
                 return null();
 
             if (tt == TOK_COLON) {
                 if (isGenerator) {
                     report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
                     return null();
                 }
 
@@ -7641,17 +7914,19 @@ Parser<ParseHandler>::objectLiteral()
         } else {
             /* NB: Getter function in { get x(){} } is unnamed. */
             if (!methodDefinition(literal, propname, op == JSOP_INITPROP_GETTER ? Getter : Setter,
                                   Expression, NotGenerator, op)) {
                 return null();
             }
         }
 
-        TokenKind tt = tokenStream.getToken();
+        TokenKind tt;
+        if (!tokenStream.getToken(&tt))
+            return null();
         if (tt == TOK_RC)
             break;
         if (tt != TOK_COMMA) {
             report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST);
             return null();
         }
     }
 
@@ -7729,18 +8004,18 @@ Parser<ParseHandler>::primaryExpr(TokenK
       case TOK_FALSE:
         return handler.newBooleanLiteral(false, pos());
       case TOK_THIS:
         return handler.newThisLiteral(pos());
       case TOK_NULL:
         return handler.newNullLiteral(pos());
 
       case TOK_RP: {
-        TokenKind next = tokenStream.peekToken();
-        if (next == TOK_ERROR)
+        TokenKind next;
+        if (!tokenStream.peekToken(&next))
             return null();
 
         // Not valid expression syntax, but this is valid in an arrow function
         // with no params: `() => body`.
         if (next == TOK_ARROW) {
             tokenStream.ungetToken();  // put back right paren
 
             // Now just return something that will allow parsing to continue.
@@ -7753,102 +8028,103 @@ Parser<ParseHandler>::primaryExpr(TokenK
 
       case TOK_TRIPLEDOT: {
         TokenKind next;
 
         // This isn't valid expression syntax, but it's valid in an arrow
         // function as a trailing rest param: `(a, b, ...rest) => body`.  Check
         // for a name, closing parenthesis, and arrow, and allow it only if all
         // are present.
-        next = tokenStream.getToken();
-        if (next == TOK_ERROR)
+        if (!tokenStream.getToken(&next))
             return null();
         if (next != TOK_NAME) {
             report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                    "rest argument name", TokenKindToDesc(next));
             return null();
         }
 
-        next = tokenStream.getToken();
-        if (next == TOK_ERROR)
+        if (!tokenStream.getToken(&next))
             return null();
         if (next != TOK_RP) {
             report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                    "closing parenthesis", TokenKindToDesc(next));
             return null();
         }
 
-        next = tokenStream.peekToken();
-        if (next == TOK_ERROR)
+        if (!tokenStream.peekToken(&next))
             return null();
         if (next != TOK_ARROW) {
             report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                    "'=>' after argument list", TokenKindToDesc(next));
             return null();
         }
 
         tokenStream.ungetToken();  // put back right paren
 
         // Return an arbitrary expression node. See case TOK_RP above.
         return handler.newNullLiteral(pos());
       }
 
-      case TOK_ERROR:
-        /* The scanner or one of its subroutines reported the error. */
-        return null();
-
       default:
       unexpected_token:
         report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
                "expression", TokenKindToDesc(tt));
         return null();
     }
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::parenExprOrGeneratorComprehension()
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
     uint32_t begin = pos().begin;
     uint32_t startYieldOffset = pc->lastYieldOffset;
 
-    if (tokenStream.matchToken(TOK_FOR, TokenStream::Operand))
+    bool matched;
+    if (!tokenStream.matchToken(&matched, TOK_FOR, TokenStream::Operand))
+        return null();
+    if (matched)
         return generatorComprehension(begin);
 
     /*
      * 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();
 
 #if JS_HAS_GENERATOR_EXPRS
-    if (tokenStream.matchToken(TOK_FOR)) {
+    if (!tokenStream.matchToken(&matched, TOK_FOR))
+        return null();
+    if (matched) {
         if (pc->lastYieldOffset != startYieldOffset) {
             reportWithOffset(ParseError, false, pc->lastYieldOffset,
                              JSMSG_BAD_GENEXP_BODY, js_yield_str);
             return null();
         }
         if (handler.isOperationWithoutParens(pn, PNK_COMMA)) {
             report(ParseError, false, null(),
                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
             return null();
         }
         pn = legacyGeneratorExpr(pn);
         if (!pn)
             return null();
         handler.setBeginPosition(pn, begin);
-        if (tokenStream.getToken() != TOK_RP) {
+        TokenKind tt;
+        if (!tokenStream.getToken(&tt))
+            return null();
+        if (tt != TOK_RP) {
             report(ParseError, false, null(),
                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
             return null();
         }
         handler.setEndPosition(pn, pos().end);
         handler.setInParens(pn);
         return pn;
     }
@@ -7896,17 +8172,20 @@ Parser<ParseHandler>::exprInParens()
     pc->parsingForInit = false;
     Node pn = expr();
     pc->parsingForInit = oldParsingForInit;
 
     if (!pn)
         return null();
 
 #if JS_HAS_GENERATOR_EXPRS
-    if (tokenStream.matchToken(TOK_FOR)) {
+    bool matched;
+    if (!tokenStream.matchToken(&matched, TOK_FOR))
+        return null();
+    if (matched) {
         if (pc->lastYieldOffset != startYieldOffset) {
             reportWithOffset(ParseError, false, pc->lastYieldOffset,
                              JSMSG_BAD_GENEXP_BODY, js_yield_str);
             return null();
         }
         if (handler.isOperationWithoutParens(pn, PNK_COMMA)) {
             report(ParseError, false, null(),
                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -456,17 +456,17 @@ class Parser : private JS::AutoGCRooter,
 
     JSAtom * stopStringCompression();
 
     Node stringLiteral();
     Node noSubstitutionTemplate();
     Node templateLiteral();
     bool taggedTemplate(Node nodeList, TokenKind tt);
     bool appendToCallSiteObj(Node callSiteObj);
-    bool addExprAndGetNextTemplStrToken(Node nodeList, TokenKind &tt);
+    bool addExprAndGetNextTemplStrToken(Node nodeList, TokenKind *ttp);
 
     inline Node newName(PropertyName *name);
     inline Node newYieldExpression(uint32_t begin, Node expr, bool isYieldStar = false);
 
     inline bool abortIfSyntaxParser();
 
   public:
 
@@ -617,17 +617,17 @@ class Parser : private JS::AutoGCRooter,
     enum AssignmentFlavor {
         PlainAssignment,
         CompoundAssignment,
         KeyedDestructuringAssignment,
         IncDecAssignment
     };
 
     bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor);
-    bool matchInOrOf(bool *isForOfp);
+    bool matchInOrOf(bool *isForInp, bool *isForOfp);
 
     bool checkFunctionArguments();
     bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom, bool *pbodyLevelHoistedUse);
     bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind,
                                  bool *pbodyProcessed, bool *pbodyLevelHoistedUse);
     bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body);
     bool addFreeVariablesFromLazyFunction(JSFunction *fun, ParseContext<ParseHandler> *pc,
                                           bool bodyLevelHoistedUse);
--- a/js/src/frontend/TokenKind.h
+++ b/js/src/frontend/TokenKind.h
@@ -191,17 +191,16 @@
     FOR_EACH_TOKEN_KIND_WITH_RANGE(macro, TOKEN_KIND_RANGE_EMIT_NONE)
 
 namespace js {
 namespace frontend {
 
 // Values of this type are used to index into arrays such as isExprEnding[],
 // so the first value must be zero.
 enum TokenKind {
-    TOK_ERROR = 0,
 #define EMIT_ENUM(name, desc) TOK_##name,
 #define EMIT_ENUM_RANGE(name, value) TOK_##name = TOK_##value,
     FOR_EACH_TOKEN_KIND_WITH_RANGE(EMIT_ENUM, EMIT_ENUM_RANGE)
 #undef EMIT_ENUM
 #undef EMIT_ENUM_RANGE
     TOK_LIMIT                      // domain size
 };
 
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -511,23 +511,24 @@ TokenStream::TokenBuf::findEOLMax(const 
         n++;
     }
     return p;
 }
 
 void
 TokenStream::advance(size_t position)
 {
+    MOZ_ASSERT(position <= mozilla::PointerRangeSize(userbuf.base(), userbuf.limit()));
     const char16_t *end = userbuf.base() + position;
     while (userbuf.addressOfNextRawChar() < end)
         getChar();
 
     Token *cur = &tokens[cursor];
     cur->pos.begin = userbuf.addressOfNextRawChar() - userbuf.base();
-    cur->type = TOK_ERROR;
+    MOZ_MAKE_MEM_UNDEFINED(&cur->type, sizeof(cur->type));
     lookahead = 0;
 }
 
 void
 TokenStream::tell(Position *pos)
 {
     pos->buf = userbuf.addressOfNextRawChar(/* allowPoisoned = */ true);
     pos->flags = flags;
@@ -938,17 +939,17 @@ TokenStream::atomize(ExclusiveContext *c
 }
 
 #ifdef DEBUG
 static bool
 IsTokenSane(Token *tp)
 {
     // Nb: TOK_EOL should never be used in an actual Token;  it should only be
     // returned as a TokenKind from peekTokenSameLine().
-    if (tp->type < TOK_ERROR || tp->type >= TOK_LIMIT || tp->type == TOK_EOL)
+    if (tp->type < 0 || tp->type >= TOK_LIMIT || tp->type == TOK_EOL)
         return false;
 
     if (tp->pos.end < tp->pos.begin)
         return false;
 
     return true;
 }
 #endif
@@ -1088,18 +1089,18 @@ static const uint8_t firstCharKinds[] = 
 #undef T_COLON
 #undef T_BITNOT
 #undef Templat
 #undef _______
 
 static_assert(LastCharKind < (1 << (sizeof(firstCharKinds[0]) * 8)),
               "Elements of firstCharKinds[] are too small");
 
-TokenKind
-TokenStream::getTokenInternal(Modifier modifier)
+bool
+TokenStream::getTokenInternal(TokenKind *ttp, Modifier modifier)
 {
     int c, qc;
     Token *tp;
     FirstCharKind c1kind;
     const char16_t *numStart;
     bool hasExp;
     DecimalPoint decimalPoint;
     const char16_t *identStart;
@@ -1620,25 +1621,33 @@ TokenStream::getTokenInternal(Modifier m
     }
 
     MOZ_CRASH("should have jumped to |out| or |error|");
 
   out:
     flags.isDirtyLine = true;
     tp->pos.end = userbuf.addressOfNextRawChar() - userbuf.base();
     MOZ_ASSERT(IsTokenSane(tp));
-    return tp->type;
+    *ttp = tp->type;
+    return true;
 
   error:
     flags.isDirtyLine = true;
     tp->pos.end = userbuf.addressOfNextRawChar() - userbuf.base();
-    tp->type = TOK_ERROR;
-    MOZ_ASSERT(IsTokenSane(tp));
-    onError();
-    return TOK_ERROR;
+    MOZ_MAKE_MEM_UNDEFINED(&tp->type, sizeof(tp->type));
+    flags.hadError = true;
+#ifdef DEBUG
+    // Poisoning userbuf on error establishes an invariant: once an erroneous
+    // token has been seen, userbuf will not be consulted again.  This is true
+    // because the parser will deal with the illegal token by aborting parsing
+    // immediately.
+    userbuf.poison();
+#endif
+    MOZ_MAKE_MEM_UNDEFINED(ttp, sizeof(*ttp));
+    return false;
 }
 
 bool TokenStream::getStringOrTemplateToken(int qc, Token **tp)
 {
     *tp = newToken(-1);
     int c;
     int nc = -1;
     tokenbuf.clear();
@@ -1758,32 +1767,16 @@ bool TokenStream::getStringOrTemplateTok
             (*tp)->type = TOK_TEMPLATE_HEAD;
         else
             (*tp)->type = TOK_NO_SUBS_TEMPLATE;
     }
     (*tp)->setAtom(atom);
     return true;
 }
 
-void
-TokenStream::onError()
-{
-    flags.hadError = true;
-#ifdef DEBUG
-    // Poisoning userbuf on error establishes an invariant: once an erroneous
-    // token has been seen, userbuf will not be consulted again.  This is true
-    // because the parser will either (a) deal with the TOK_ERROR token by
-    // aborting parsing immediately; or (b) if the TOK_ERROR token doesn't
-    // match what it expected, it will unget the token, and the next getToken()
-    // call will immediately return the just-gotten TOK_ERROR token again
-    // without consulting userbuf, thanks to the lookahead buffer.
-    userbuf.poison();
-#endif
-}
-
 JS_FRIEND_API(int)
 js_fgets(char *buf, int size, FILE *file)
 {
     int n, i, c;
     bool crflag;
 
     n = size - 1;
     if (n < 0)
@@ -1809,34 +1802,30 @@ js_fgets(char *buf, int size, FILE *file
 
 const char *
 frontend::TokenKindToDesc(TokenKind tt)
 {
     switch (tt) {
 #define EMIT_CASE(name, desc) case TOK_##name: return desc;
       FOR_EACH_TOKEN_KIND(EMIT_CASE)
 #undef EMIT_CASE
-      case TOK_ERROR:
-        MOZ_ASSERT_UNREACHABLE("TOK_ERROR should not be passed.");
-        break;
       case TOK_LIMIT:
         MOZ_ASSERT_UNREACHABLE("TOK_LIMIT should not be passed.");
         break;
     }
 
     return "<bad TokenKind>";
 }
 
 #ifdef DEBUG
 const char *
 TokenKindToString(TokenKind tt)
 {
     switch (tt) {
 #define EMIT_CASE(name, desc) case TOK_##name: return "TOK_" #name;
       FOR_EACH_TOKEN_KIND(EMIT_CASE)
 #undef EMIT_CASE
-      case TOK_ERROR: return "TOK_ERROR";
       case TOK_LIMIT: break;
     }
 
     return "<bad TokenKind>";
 }
 #endif
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -100,19 +100,19 @@ struct Token
     // constructor on each element, which would call the TokenPos constructor
     // for Token::pos and do nothing.  (All of which is equivalent to just
     // zeroing TokenStream::tokens.)  But MSVC 2013 (2010/2012 don't have this
     // bug) doesn't zero out each element, so we need this extra constructor to
     // make it do the right thing.  (Token is used primarily by reference or
     // pointer, and it's only initialized a very few places, so having a
     // user-defined constructor won't hurt perf.)  See also bug 920318.
     Token()
-      : type(TOK_ERROR),
-        pos(0, 0)
+      : pos(0, 0)
     {
+        MOZ_MAKE_MEM_UNDEFINED(&type, sizeof(type));
     }
 
     // Mutators
 
     void setName(PropertyName *name) {
         MOZ_ASSERT(type == TOK_NAME);
         MOZ_ASSERT(!IsPoisonedPtr(name));
         u.name = name;
@@ -334,26 +334,26 @@ class MOZ_STACK_CLASS TokenStream
     }
 
   private:
     // These are private because they should only be called by the tokenizer
     // while tokenizing not by, for example, BytecodeEmitter.
     bool reportStrictModeError(unsigned errorNumber, ...);
     bool strictMode() const { return strictModeGetter && strictModeGetter->strictMode(); }
 
-    void onError();
     static JSAtom *atomize(ExclusiveContext *cx, CharBuffer &cb);
     bool putIdentInTokenbuf(const char16_t *identStart);
 
     struct Flags
     {
         bool isEOF:1;           // Hit end of file.
         bool isDirtyLine:1;     // Non-whitespace since start of line.
         bool sawOctalEscape:1;  // Saw an octal character escape.
-        bool hadError:1;        // Returned TOK_ERROR from getToken.
+        bool hadError:1;        // Hit a syntax error, at start or during a
+                                // token.
 
         Flags()
           : isEOF(), isDirtyLine(), sawOctalEscape(), hadError()
         {}
     };
 
   public:
     // Sometimes the parser needs to modify how tokens are created.
@@ -363,107 +363,146 @@ class MOZ_STACK_CLASS TokenStream
         Operand,        // Looking for an operand, not an operator.  In
                         //   practice, this means that when '/' is seen,
                         //   we look for a regexp instead of just returning
                         //   TOK_DIV.
         KeywordIsName,  // Treat keywords as names by returning TOK_NAME.
         TemplateTail,   // Treat next characters as part of a template string
     };
 
-    // Get the next token from the stream, make it the current token, and
-    // return its kind.
-    TokenKind getToken(Modifier modifier = None) {
+    // Advance to the next token.  If the token stream encountered an error,
+    // return false.  Otherwise return true and store the token kind in |*ttp|.
+    bool getToken(TokenKind *ttp, Modifier modifier = None) {
         // Check for a pushed-back token resulting from mismatching lookahead.
         if (lookahead != 0) {
+            MOZ_ASSERT(!flags.hadError);
             lookahead--;
             cursor = (cursor + 1) & ntokensMask;
             TokenKind tt = currentToken().type;
             MOZ_ASSERT(tt != TOK_EOL);
-            return tt;
+            *ttp = tt;
+            return true;
         }
 
-        return getTokenInternal(modifier);
+        return getTokenInternal(ttp, modifier);
     }
 
     // Push the last scanned token back into the stream.
     void ungetToken() {
         MOZ_ASSERT(lookahead < maxLookahead);
         lookahead++;
         cursor = (cursor - 1) & ntokensMask;
     }
 
-    TokenKind peekToken(Modifier modifier = None) {
-        if (lookahead != 0)
-            return tokens[(cursor + 1) & ntokensMask].type;
-        TokenKind tt = getTokenInternal(modifier);
+    bool peekToken(TokenKind *ttp, Modifier modifier = None) {
+        if (lookahead > 0) {
+            MOZ_ASSERT(!flags.hadError);
+            *ttp = tokens[(cursor + 1) & ntokensMask].type;
+            return true;
+        }
+        if (!getTokenInternal(ttp, modifier))
+            return false;
         ungetToken();
-        return tt;
+        return true;
     }
 
-    TokenPos peekTokenPos(Modifier modifier = None) {
-        if (lookahead != 0)
-            return tokens[(cursor + 1) & ntokensMask].pos;
-        getTokenInternal(modifier);
-        ungetToken();
-        MOZ_ASSERT(lookahead != 0);
-        return tokens[(cursor + 1) & ntokensMask].pos;
+    bool peekTokenPos(TokenPos *posp, Modifier modifier = None) {
+        if (lookahead == 0) {
+            TokenKind tt;
+            if (!getTokenInternal(&tt, modifier))
+                return false;
+            ungetToken();
+            MOZ_ASSERT(lookahead != 0);
+        } else {
+            MOZ_ASSERT(!flags.hadError);
+        }
+        *posp = tokens[(cursor + 1) & ntokensMask].pos;
+        return true;
     }
 
     // This is like peekToken(), with one exception:  if there is an EOL
     // between the end of the current token and the start of the next token, it
-    // returns TOK_EOL.  In that case, no token with TOK_EOL is actually
-    // created, just a TOK_EOL TokenKind is returned, and currentToken()
-    // shouldn't be consulted.  (This is the only place TOK_EOL is produced.)
-    MOZ_ALWAYS_INLINE TokenKind peekTokenSameLine(Modifier modifier = None) {
-       const Token &curr = currentToken();
+    // return true and store TOK_EOL in |*ttp|.  In that case, no token with
+    // TOK_EOL is actually created, just a TOK_EOL TokenKind is returned, and
+    // currentToken() shouldn't be consulted.  (This is the only place TOK_EOL
+    // is produced.)
+    MOZ_ALWAYS_INLINE bool
+    peekTokenSameLine(TokenKind *ttp, Modifier modifier = None) {
+        const Token &curr = currentToken();
 
         // If lookahead != 0, we have scanned ahead at least one token, and
         // |lineno| is the line that the furthest-scanned token ends on.  If
         // it's the same as the line that the current token ends on, that's a
         // stronger condition than what we are looking for, and we don't need
         // to return TOK_EOL.
-        if (lookahead != 0 && srcCoords.isOnThisLine(curr.pos.end, lineno))
-            return tokens[(cursor + 1) & ntokensMask].type;
+        if (lookahead != 0 && srcCoords.isOnThisLine(curr.pos.end, lineno)) {
+            MOZ_ASSERT(!flags.hadError);
+            *ttp = tokens[(cursor + 1) & ntokensMask].type;
+            return true;
+        }
 
         // The above check misses two cases where we don't have to return
         // TOK_EOL.
         // - The next token starts on the same line, but is a multi-line token.
         // - The next token starts on the same line, but lookahead==2 and there
         //   is a newline between the next token and the one after that.
         // The following test is somewhat expensive but gets these cases (and
         // all others) right.
-        (void)getToken(modifier);
+        TokenKind tmp;
+        if (!getToken(&tmp, modifier))
+            return false;
         const Token &next = currentToken();
         ungetToken();
-        return srcCoords.lineNum(curr.pos.end) == srcCoords.lineNum(next.pos.begin)
-               ? next.type
-               : TOK_EOL;
+
+        *ttp = srcCoords.lineNum(curr.pos.end) == srcCoords.lineNum(next.pos.begin)
+             ? next.type
+             : TOK_EOL;
+        return true;
     }
 
     // Get the next token from the stream if its kind is |tt|.
-    bool matchToken(TokenKind tt, Modifier modifier = None) {
-        if (getToken(modifier) == tt)
-            return true;
-        ungetToken();
-        return false;
+    bool matchToken(bool *matchedp, TokenKind tt, Modifier modifier = None) {
+        TokenKind token;
+        if (!getToken(&token, modifier))
+            return false;
+        if (token == tt) {
+            *matchedp = true;
+        } else {
+            ungetToken();
+            *matchedp = false;
+        }
+        return true;
     }
 
     void consumeKnownToken(TokenKind tt) {
-        JS_ALWAYS_TRUE(matchToken(tt));
+        bool matched;
+        MOZ_ASSERT(lookahead != 0);
+        MOZ_ALWAYS_TRUE(matchToken(&matched, tt));
+        MOZ_ALWAYS_TRUE(matched);
     }
 
-    bool matchContextualKeyword(Handle<PropertyName*> keyword) {
-        if (getToken() == TOK_NAME && currentToken().name() == keyword)
-            return true;
-        ungetToken();
-        return false;
+    bool matchContextualKeyword(bool *matchedp, Handle<PropertyName*> keyword) {
+        TokenKind token;
+        if (!getToken(&token))
+            return false;
+        if (token == TOK_NAME && currentToken().name() == keyword) {
+            *matchedp = true;
+        } else {
+            *matchedp = false;
+            ungetToken();
+        }
+        return true;
     }
 
-    bool nextTokenEndsExpr() {
-        return isExprEnding[peekToken()];
+    bool nextTokenEndsExpr(bool *endsExpr) {
+        TokenKind tt;
+        if (!peekToken(&tt))
+            return false;
+        *endsExpr = isExprEnding[tt];
+        return true;
     }
 
     class MOZ_STACK_CLASS Position {
       public:
         // The Token fields may contain pointers to atoms, so for correct
         // rooting we must ensure collection of atoms is disabled while objects
         // of this class are live.  Do this by requiring a dummy AutoKeepAtoms
         // reference in the constructor.
@@ -694,17 +733,17 @@ class MOZ_STACK_CLASS TokenStream
         const char16_t *findEOLMax(const char16_t *p, size_t max);
 
       private:
         const char16_t *base_;          // base of buffer
         const char16_t *limit_;         // limit for quick bounds check
         const char16_t *ptr;            // next char to get
     };
 
-    TokenKind getTokenInternal(Modifier modifier);
+    bool getTokenInternal(TokenKind *ttp, Modifier modifier);
 
     bool getStringOrTemplateToken(int qc, Token **tp);
 
     int32_t getChar();
     int32_t getCharIgnoreEOL();
     void ungetChar(int32_t c);
     void ungetCharIgnoreEOL(int32_t c);
     Token *newToken(ptrdiff_t adjust);
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -244,17 +244,17 @@ class GCRuntime
     inline bool upcomingZealousGC();
     inline bool needZealousGC();
 
     template <typename T> bool addRoot(T *rp, const char *name, JSGCRootType rootType);
     void removeRoot(void *rp);
     void setMarkStackLimit(size_t limit);
 
     void setParameter(JSGCParamKey key, uint32_t value);
-    uint32_t getParameter(JSGCParamKey key);
+    uint32_t getParameter(JSGCParamKey key, const AutoLockGC &lock);
 
     bool isHeapBusy() { return heapState != js::Idle; }
     bool isHeapMajorCollecting() { return heapState == js::MajorCollecting; }
     bool isHeapMinorCollecting() { return heapState == js::MinorCollecting; }
     bool isHeapCollecting() { return isHeapMajorCollecting() || isHeapMinorCollecting(); }
 #ifdef JSGC_COMPACTING
     bool isHeapCompacting() { return isHeapMajorCollecting() && state() == COMPACT; }
 #else
@@ -451,18 +451,20 @@ class GCRuntime
         marker.setGCMode(mode);
     }
 
     inline void updateOnFreeArenaAlloc(const ChunkInfo &info);
     inline void updateOnArenaFree(const ChunkInfo &info);
 
     GCChunkSet::Range allChunks() { return chunkSet.all(); }
     inline Chunk **getAvailableChunkList(Zone *zone);
-    void moveChunkToFreePool(Chunk *chunk);
+    void moveChunkToFreePool(Chunk *chunk, const AutoLockGC &lock);
     bool hasChunk(Chunk *chunk) { return chunkSet.has(chunk); }
+    ChunkPool &emptyChunks(const AutoLockGC &lock) { return emptyChunks_; }
+    const ChunkPool &emptyChunks(const AutoLockGC &lock) const { return emptyChunks_; }
 
 #ifdef JS_GC_ZEAL
     void startVerifyPreBarriers();
     bool endVerifyPreBarriers();
     void startVerifyPostBarriers();
     bool endVerifyPostBarriers();
     void finishVerifier();
     bool isVerifyPreBarriersEnabled() const { return !!verifyPreData; }
@@ -485,18 +487,18 @@ class GCRuntime
     static void *refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind);
     static void *refillFreeListOffMainThread(ExclusiveContext *cx, AllocKind thingKind);
     static void *refillFreeListPJS(ForkJoinContext *cx, AllocKind thingKind);
 
     /*
      * Return the list of chunks that can be released outside the GC lock.
      * Must be called either during the GC or with the GC lock taken.
      */
-    Chunk *expireChunkPool(bool shrinkBuffers, bool releaseAll);
-    void expireAndFreeChunkPool(bool releaseAll);
+    Chunk *expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC &lock);
+    void freeEmptyChunks(JSRuntime *rt);
     void freeChunkList(Chunk *chunkListHead);
     void prepareToFreeChunk(ChunkInfo &info);
     void releaseChunk(Chunk *chunk);
 
     friend class BackgroundAllocTask;
     friend class AutoMaybeStartBackgroundAllocation;
     inline bool wantBackgroundAllocation(const AutoLockGC &lock) const;
     void startBackgroundAllocTaskIfIdle();
@@ -532,17 +534,17 @@ class GCRuntime
     void beginSweepingZoneGroup();
     bool shouldReleaseObservedTypes();
     void endSweepingZoneGroup();
     bool sweepPhase(SliceBudget &sliceBudget);
     void endSweepPhase(bool lastGC);
     void sweepZones(FreeOp *fop, bool lastGC);
     void decommitArenasFromAvailableList(Chunk **availableListHeadp);
     void decommitArenas();
-    void expireChunksAndArenas(bool shouldShrink);
+    void expireChunksAndArenas(bool shouldShrink, const AutoLockGC &lock);
     void sweepBackgroundThings();
     void assertBackgroundSweepingFinished();
     bool shouldCompact();
 #ifdef JSGC_COMPACTING
     void sweepTypesAfterCompacting(Zone *zone);
     void sweepZoneAfterCompacting(Zone *zone);
     void compactPhase();
     ArenaHeader *relocateArenas();
@@ -601,17 +603,17 @@ class GCRuntime
      * Doubly-linked lists of chunks from user and system compartments. The GC
      * allocates its arenas from the corresponding list and when all arenas
      * in the list head are taken, then the chunk is removed from the list.
      * During the GC when all arenas in a chunk become free, that chunk is
      * removed from the list and scheduled for release.
      */
     js::gc::Chunk         *systemAvailableChunkListHead;
     js::gc::Chunk         *userAvailableChunkListHead;
-    js::gc::ChunkPool     emptyChunks;
+    js::gc::ChunkPool     emptyChunks_;
 
     js::RootedValueMap    rootsHash;
 
     size_t                maxMallocBytes;
 
     /*
      * Number of the committed arenas in all GC chunks including empty chunks.
      */
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/asm.js/syntax-error-illegal-character.js
@@ -0,0 +1,57 @@
+var JSMSG_ILLEGAL_CHARACTER = "illegal character";
+
+function test_reflect(code) {
+  var caught = false;
+  try {
+    Reflect.parse(code);
+  } catch (e) {
+    caught = true;
+    assertEq(e instanceof SyntaxError, true, code);
+    assertEq(e.message, JSMSG_ILLEGAL_CHARACTER, code);
+  }
+  assertEq(caught, true);
+}
+
+function test_eval(code) {
+  var caught = false;
+  try {
+    eval(code);
+  } catch (e) {
+    caught = true;
+    assertEq(e instanceof SyntaxError, true, code);
+    assertEq(e.message, JSMSG_ILLEGAL_CHARACTER, code);
+  }
+  assertEq(caught, true);
+}
+
+
+function test(code) {
+  test_reflect(code);
+  test_eval(code);
+}
+
+test("(function() { 'use asm'; @");
+test("(function() { 'use asm'; var @");
+test("(function() { 'use asm'; var a @");
+test("(function() { 'use asm'; var a = @");
+test("(function() { 'use asm'; var a = 1 @");
+test("(function() { 'use asm'; var a = 1; @");
+test("(function() { 'use asm'; var a = 1; function @");
+test("(function() { 'use asm'; var a = 1; function f @");
+test("(function() { 'use asm'; var a = 1; function f( @");
+test("(function() { 'use asm'; var a = 1; function f() @");
+test("(function() { 'use asm'; var a = 1; function f() { @");
+test("(function() { 'use asm'; var a = 1; function f() { } @");
+test("(function() { 'use asm'; var a = 1; function f() { } var @");
+test("(function() { 'use asm'; var a = 1; function f() { } var tbl @");
+test("(function() { 'use asm'; var a = 1; function f() { } var tbl = @");
+test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [ @");
+test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f @");
+test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f] @");
+test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; @");
+test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return @");
+test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return f @");
+test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return f; @");
+test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return f; } @");
+test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return f; }) @");
+test("(function() { 'use asm'; var a = 1; function f() { } var tbl = [f]; return f; }); @");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/syntax-error-illegal-character.js
@@ -0,0 +1,1073 @@
+var JSMSG_ILLEGAL_CHARACTER = "illegal character";
+var JSMSG_UNTERMINATED_STRING = "unterminated string literal";
+
+function test_reflect(code) {
+  var caught = false;
+  try {
+    Reflect.parse(code);
+  } catch (e) {
+    caught = true;
+    assertEq(e instanceof SyntaxError, true, code);
+    assertEq(e.message, JSMSG_ILLEGAL_CHARACTER, code);
+  }
+  assertEq(caught, true);
+}
+
+function test_eval(code) {
+  var caught = false;
+  try {
+    eval(code);
+  } catch (e) {
+    caught = true;
+    assertEq(e instanceof SyntaxError, true, code);
+    assertEq(e.message, JSMSG_ILLEGAL_CHARACTER, code);
+  }
+  assertEq(caught, true);
+}
+
+
+function test(code) {
+  test_reflect(code);
+  test_reflect("'use strict'; " + code);
+  test_reflect("(function() { " + code);
+  test_reflect("(function() { 'use strict'; " + code);
+
+  test_eval(code);
+  test_eval("'use strict'; " + code);
+  test_eval("(function() { " + code);
+  test_eval("(function() { 'use strict'; " + code);
+}
+
+function test_no_strict(code) {
+  test_reflect(code);
+  test_reflect("(function() { " + code);
+
+  test_eval(code);
+  test_eval("(function() { " + code);
+}
+
+function test_no_fun_no_eval(code) {
+  test_reflect(code);
+  test_reflect("'use strict'; " + code);
+}
+
+
+function test_fun_arg(arg) {
+  var caught = false;
+  try {
+    new Function(arg, "");
+  } catch (e) {
+    caught = true;
+    assertEq(e.message, JSMSG_ILLEGAL_CHARACTER, arg);
+  }
+  assertEq(caught, true);
+}
+
+// ==== Statements and declarations ====
+
+// ---- Control flow ----
+
+// Block
+
+test("{ @");
+test("{ } @");
+
+test("{ 1 @");
+test("{ 1; @");
+test("{ 1; } @");
+
+// break
+
+test("a: for (;;) { break @");
+test("a: for (;;) { break; @");
+test("a: for (;;) { break a @");
+test("a: for (;;) { break a; @");
+
+test("a: for (;;) { break\n@");
+
+// continue
+
+test("a: for (;;) { continue @");
+test("a: for (;;) { continue; @");
+test("a: for (;;) { continue a @");
+test("a: for (;;) { continue a; @");
+
+test("a: for (;;) { continue\n@");
+
+// Empty
+
+test("@");
+test("; @");
+
+// if...else
+
+test("if @");
+test("if (@");
+test("if (x @");
+test("if (x) @");
+test("if (x) { @");
+test("if (x) {} @");
+test("if (x) {} else @");
+test("if (x) {} else { @");
+test("if (x) {} else {} @");
+test("if (x) x @");
+test("if (x) x; @");
+test("if (x) x; else @");
+test("if (x) x; else y @");
+test("if (x) x; else y; @");
+
+// switch
+
+test("switch @");
+test("switch (@");
+test("switch (x @");
+test("switch (x) @");
+test("switch (x) { @");
+test("switch (x) { case @");
+test("switch (x) { case 1 @");
+test("switch (x) { case 1: @");
+test("switch (x) { case 1: case @");
+test("switch (x) { case 1: case 2 @");
+test("switch (x) { case 1: case 2: @");
+test("switch (x) { case 1: case 2: x @");
+test("switch (x) { case 1: case 2: x; @");
+test("switch (x) { case 1: case 2: x; break @");
+test("switch (x) { case 1: case 2: x; break; @");
+test("switch (x) { case 1: case 2: x; break; case @");
+test("switch (x) { case 1: case 2: x; break; case 3 @");
+test("switch (x) { case 1: case 2: x; break; case 3: y @");
+test("switch (x) { case 1: case 2: x; break; case 3: y; @");
+test("switch (x) { case 1: case 2: x; break; case 3: y; default @");
+test("switch (x) { case 1: case 2: x; break; case 3: y; default: @");
+test("switch (x) { case 1: case 2: x; break; case 3: y; default: z @");
+test("switch (x) { case 1: case 2: x; break; case 3: y; default: z; @");
+test("switch (x) { case 1: case 2: x; break; case 3: y; default: z; } @");
+
+// throw
+
+test("throw @");
+test("throw x @");
+test("throw x; @");
+
+// try...catch
+
+test("try @");
+test("try { @");
+test("try {} @");
+test("try {} catch @");
+test("try {} catch ( @");
+test("try {} catch (e @");
+test("try {} catch (e) @");
+test("try {} catch (e) { @");
+test("try {} catch (e) {} @");
+test("try {} catch (e) {} finally @");
+test("try {} catch (e) {} finally { @");
+test("try {} catch (e) {} finally {} @");
+
+test("try {} catch (e if @");
+test("try {} catch (e if e  @");
+test("try {} catch (e if e instanceof @");
+test("try {} catch (e if e instanceof x @");
+test("try {} catch (e if e instanceof x) @");
+test("try {} catch (e if e instanceof x) { @");
+test("try {} catch (e if e instanceof x) {} @");
+
+// ---- Declarations ----
+
+// var
+
+test("var @");
+test("var x @");
+test("var x = @");
+test("var x = 1 @");
+test("var x = 1 + @");
+test("var x = 1 + 2 @");
+test("var x = 1 + 2, @");
+test("var x = 1 + 2, y @");
+test("var x = 1 + 2, y, @");
+test("var x = 1 + 2, y, z @");
+test("var x = 1 + 2, y, z; @");
+
+test("var [ @");
+test("var [ x @");
+test("var [ x, @");
+test("var [ x, ... @");
+test("var { @");
+test("var { x @");
+test("var { x: @");
+test("var { x: y @");
+test("var { x: y, @");
+test("var { x: y } @");
+test("var { x: y } = @");
+
+// let
+
+test("let @");
+test("let x @");
+test("let x = @");
+test("let x = 1 @");
+test("let x = 1 + @");
+test("let x = 1 + 2 @");
+test("let x = 1 + 2, @");
+test("let x = 1 + 2, y @");
+test("let x = 1 + 2, y, @");
+test("let x = 1 + 2, y, z @");
+test("let x = 1 + 2, y, z; @");
+
+test("let [ @");
+test("let [ x @");
+test("let [ x, @");
+test("let [ x, ... @");
+test("let { @");
+test("let { x @");
+test("let { x: @");
+test("let { x: y @");
+test("let { x: y, @");
+test("let { x: y } @");
+test("let { x: y } = @");
+
+// const
+
+test("const @");
+test("const x @");
+test("const x = @");
+test("const x = 1 @");
+test("const x = 1 + @");
+test("const x = 1 + 2 @");
+test("const x = 1 + 2, @");
+test("const x = 1 + 2, y @");
+test("const x = 1 + 2, y, @");
+test("const x = 1 + 2, y, z @");
+test("const x = 1 + 2, y, z; @");
+
+test("const [ @");
+test("const [ x @");
+test("const [ x, @");
+test("const [ x, ... @");
+test("const { @");
+test("const { x @");
+test("const { x: @");
+test("const { x: y @");
+test("const { x: y, @");
+test("const { x: y } @");
+test("const { x: y } = @");
+
+// ---- Functions ----
+
+// function
+
+test("function @");
+test("function f @");
+test("function f( @");
+test("function f(x @");
+test("function f(x, @");
+test("function f(x, [ @");
+test("function f(x, [y @");
+test("function f(x, [y, @");
+test("function f(x, [y, { @");
+test("function f(x, [y, {z @");
+test("function f(x, [y, {z: @");
+test("function f(x, [y, {z: zz @");
+test("function f(x, [y, {z: zz,  @");
+test("function f(x, [y, {z: zz, w @");
+test("function f(x, [y, {z: zz, w} @");
+test("function f(x, [y, {z: zz, w}] @");
+test("function f(x, [y, {z: zz, w}], @");
+test("function f(x, [y, {z: zz, w}], v @");
+test("function f(x, [y, {z: zz, w}], v= @");
+test("function f(x, [y, {z: zz, w}], v=1 @");
+test("function f(x, [y, {z: zz, w}], v=1, @");
+test("function f(x, [y, {z: zz, w}], v=1, ... @");
+test("function f(x, [y, {z: zz, w}], v=1, ...t @");
+test("function f(x, [y, {z: zz, w}], v=1, ...t) @");
+test("function f(x, [y, {z: zz, w}], v=1, ...t) {@");
+test("function f(x, [y, {z: zz, w}], v=1, ...t) { x @");
+test("function f(x, [y, {z: zz, w}], v=1, ...t) { x; @");
+test("function f(x, [y, {z: zz, w}], v=1, ...t) { x; } @");
+
+// star function
+
+test("function* @");
+test("function* f @");
+test("function* f( @");
+test("function* f(x @");
+test("function* f(x, @");
+test("function* f(x, ... @");
+test("function* f(x, ...t @");
+test("function* f(x, ...t) @");
+test("function* f(x, ...t) {@");
+test("function* f(x, ...t) { x @");
+test("function* f(x, ...t) { x; @");
+test("function* f(x, ...t) { x; } @");
+
+// return
+
+test("function f() { return @");
+test("function f() { return 1 @");
+test("function f() { return 1; @");
+test("function f() { return 1; } @");
+test("function f() { return; @");
+test("function f() { return\n@");
+
+// yield
+
+test("function* f() { yield @");
+test("function* f() { yield 1 @");
+test("function* f() { yield* @");
+test("function* f() { yield* 1 @");
+
+test("function* f() { yield\n@");
+test("function* f() { yield*\n@");
+
+// ---- Iterations ----
+
+// do...while
+
+test("do @");
+test("do {@");
+test("do {} @");
+test("do {} while @");
+test("do {} while ( @");
+test("do {} while (x @");
+test("do {} while (x) @");
+test("do {} while (x); @");
+
+test("do x @");
+test("do x; @");
+test("do x; while @");
+
+// for
+
+test("for @");
+test("for (@");
+test("for (x @");
+test("for (x; @");
+test("for (x; y @");
+test("for (x; y; @");
+test("for (x; y; z @");
+test("for (x; y; z) @");
+test("for (x; y; z) { @");
+test("for (x; y; z) {} @");
+
+test("for (x; y; z) x @");
+test("for (x; y; z) x; @");
+
+test("for (var @");
+test("for (var x @");
+test("for (var x = @");
+test("for (var x = y @");
+test("for (var x = y; @");
+
+test("for (let @");
+test("for (let x @");
+test("for (let x = @");
+test("for (let x = y @");
+test("for (let x = y; @");
+
+// for...in
+
+test("for (x in @");
+test("for (x in y @");
+test("for (x in y) @");
+
+test("for (var x in @");
+test("for (var x in y @");
+test("for (var x in y) @");
+
+test("for (let x in @");
+test("for (let x in y @");
+test("for (let x in y) @");
+
+// for...of
+
+test("for (x of @");
+test("for (x of y @");
+test("for (x of y) @");
+
+test("for (var x of @");
+test("for (var x of y @");
+test("for (var x of y) @");
+
+test("for (let x of @");
+test("for (let x of y @");
+test("for (let x of y) @");
+
+// while
+
+test("while @");
+test("while (@");
+test("while (x @");
+test("while (x) @");
+test("while (x) { @");
+test("while (x) {} @");
+
+test("while (x) x @");
+test("while (x) x; @");
+
+// ---- Others ----
+
+// debugger
+
+test("debugger @");
+test("debugger; @");
+
+// export
+
+test_no_fun_no_eval("export @");
+test_no_fun_no_eval("export x @");
+test_no_fun_no_eval("export x, @");
+test_no_fun_no_eval("export x, y @");
+test_no_fun_no_eval("export x, y; @");
+
+test_no_fun_no_eval("export { @");
+test_no_fun_no_eval("export { x @");
+test_no_fun_no_eval("export { x, @");
+test_no_fun_no_eval("export { x, y @");
+test_no_fun_no_eval("export { x, y as @");
+test_no_fun_no_eval("export { x, y as z @");
+test_no_fun_no_eval("export { x, y as z } @");
+test_no_fun_no_eval("export { x, y as z } from @");
+test_no_fun_no_eval("export { x, y as z } from 'a' @");
+test_no_fun_no_eval("export { x, y as z } from 'a'; @");
+
+test_no_fun_no_eval("export * @");
+test_no_fun_no_eval("export * from @");
+test_no_fun_no_eval("export * from 'a' @");
+test_no_fun_no_eval("export * from 'a'; @");
+
+test_no_fun_no_eval("export function @");
+test_no_fun_no_eval("export function f @");
+test_no_fun_no_eval("export function f( @");
+test_no_fun_no_eval("export function f() @");
+test_no_fun_no_eval("export function f() { @");
+test_no_fun_no_eval("export function f() {} @");
+test_no_fun_no_eval("export function f() {}; @");
+
+test_no_fun_no_eval("export var @");
+test_no_fun_no_eval("export var a @");
+test_no_fun_no_eval("export var a = @");
+test_no_fun_no_eval("export var a = 1 @");
+test_no_fun_no_eval("export var a = 1, @");
+test_no_fun_no_eval("export var a = 1, b @");
+test_no_fun_no_eval("export var a = 1, b = @");
+test_no_fun_no_eval("export var a = 1, b = 2 @");
+test_no_fun_no_eval("export var a = 1, b = 2; @");
+
+test_no_fun_no_eval("export let @");
+test_no_fun_no_eval("export let a @");
+test_no_fun_no_eval("export let a = @");
+test_no_fun_no_eval("export let a = 1 @");
+test_no_fun_no_eval("export let a = 1, @");
+test_no_fun_no_eval("export let a = 1, b @");
+test_no_fun_no_eval("export let a = 1, b = @");
+test_no_fun_no_eval("export let a = 1, b = 2 @");
+test_no_fun_no_eval("export let a = 1, b = 2; @");
+
+test_no_fun_no_eval("export const @");
+test_no_fun_no_eval("export const a @");
+test_no_fun_no_eval("export const a = @");
+test_no_fun_no_eval("export const a = 1 @");
+test_no_fun_no_eval("export const a = 1, @");
+test_no_fun_no_eval("export const a = 1, b @");
+test_no_fun_no_eval("export const a = 1, b = @");
+test_no_fun_no_eval("export const a = 1, b = 2 @");
+test_no_fun_no_eval("export const a = 1, b = 2; @");
+
+// import
+
+test_no_fun_no_eval("import @");
+test_no_fun_no_eval("import x @");
+test_no_fun_no_eval("import x from @");
+test_no_fun_no_eval("import x from 'a' @");
+test_no_fun_no_eval("import x from 'a'; @");
+
+test_no_fun_no_eval("import { @");
+test_no_fun_no_eval("import { x @");
+test_no_fun_no_eval("import { x, @");
+test_no_fun_no_eval("import { x, y @");
+test_no_fun_no_eval("import { x, y } @");
+test_no_fun_no_eval("import { x, y } from @");
+test_no_fun_no_eval("import { x, y } from 'a' @");
+test_no_fun_no_eval("import { x, y } from 'a'; @");
+
+test_no_fun_no_eval("import { x as @");
+test_no_fun_no_eval("import { x as y @");
+test_no_fun_no_eval("import { x as y } @");
+test_no_fun_no_eval("import { x as y } from @");
+test_no_fun_no_eval("import { x as y } from 'a' @");
+test_no_fun_no_eval("import { x as y } from 'a'; @");
+
+test_no_fun_no_eval("import 'a' @");
+test_no_fun_no_eval("import 'a'; @");
+
+// label
+
+test("a @");
+test("a: @");
+
+// with
+
+test_no_strict("with @");
+test_no_strict("with (@");
+test_no_strict("with (x @");
+test_no_strict("with (x) @");
+test_no_strict("with (x) { @");
+test_no_strict("with (x) {} @");
+
+test_no_strict("with (x) x @");
+test_no_strict("with (x) x; @");
+
+// ==== Expressions and operators ====
+
+// ---- Primary expressions ----
+
+// this
+
+test("this @");
+
+// function
+
+test("(function @");
+test("(function ( @");
+test("(function (x @");
+test("(function (x, @");
+test("(function (x, ... @");
+test("(function (x, ...t @");
+test("(function (x, ...t) @");
+test("(function (x, ...t) {@");
+test("(function (x, ...t) { x @");
+test("(function (x, ...t) { x; @");
+test("(function (x, ...t) { x; } @");
+test("(function (x, ...t) { x; }) @");
+
+// star function
+
+test("(function* @");
+test("(function* ( @");
+test("(function* (x @");
+test("(function* (x, @");
+test("(function* (x, ... @");
+test("(function* (x, ...t @");
+test("(function* (x, ...t) @");
+test("(function* (x, ...t) {@");
+test("(function* (x, ...t) { x @");
+test("(function* (x, ...t) { x; @");
+test("(function* (x, ...t) { x; } @");
+test("(function* (x, ...t) { x; }) @");
+
+// Array literal
+
+test("[ @");
+test("[] @");
+test("[1 @");
+test("[1, @");
+test("[1, ... @");
+test("[1, ...x @");
+test("[1, ...x] @");
+
+// object
+
+test("({ @");
+test("({ x @");
+test("({ x: @");
+test("({ x: 1 @");
+test("({ x: 1, @");
+test("({ x: 1, y @");
+test("({ x: 1, y: @");
+test("({ x: 1, y: 2 @");
+test("({ x: 1, y: 2, @");
+test("({ x: 1, y: 2, z @");
+test("({ x: 1, y: 2, z, @");
+test("({ x: 1, y: 2, z, w @");
+test("({ x: 1, y: 2, z, w } @");
+test("({ x: 1, y: 2, z, w }) @");
+
+// object: computed property
+
+test("({ [@");
+test("({ [k @");
+test("({ [k] @");
+test("({ [k]: @");
+test("({ [k]: 1 @");
+test("({ [k]: 1, @");
+
+// object: getter
+
+test("({ get @");
+test("({ get p @");
+test("({ get p( @");
+test("({ get p() @");
+test("({ get p() { @");
+test("({ get p() {} @");
+test("({ get p() {}, @");
+test("({ get p() {}, } @");
+
+test("({ get [ @");
+test("({ get [p @");
+test("({ get [p] @");
+test("({ get [p]( @");
+test("({ get [p]() @");
+
+// object: setter
+
+test("({ set @");
+test("({ set p @");
+test("({ set p( @");
+test("({ set p(v @");
+test("({ set p(v) @");
+test("({ set p(v) { @");
+test("({ set p(v) {} @");
+
+test("({ set [ @");
+test("({ set [p @");
+test("({ set [p] @");
+test("({ set [p]( @");
+test("({ set [p](v @");
+test("({ set [p](v) @");
+
+// object: method
+
+test("({ m @");
+test("({ m( @");
+test("({ m() @");
+test("({ m() { @");
+test("({ m() {} @");
+test("({ m() {}, @");
+
+test("({ [ @");
+test("({ [m @");
+test("({ [m] @");
+test("({ [m]( @");
+test("({ [m]() @");
+test("({ [m]() { @");
+test("({ [m]() {} @");
+test("({ [m]() {}, @");
+
+test("({ * @");
+test("({ *m @");
+test("({ *m( @");
+test("({ *m() @");
+test("({ *m() { @");
+test("({ *m() {} @");
+test("({ *m() {}, @");
+
+test("({ *[ @");
+test("({ *[m @");
+test("({ *[m] @");
+test("({ *[m]( @");
+test("({ *[m]() @");
+test("({ *[m]() { @");
+test("({ *[m]() {} @");
+test("({ *[m]() {}, @");
+
+// Regular expression literal
+
+test("/a/ @");
+test("/a/g @");
+
+// Array comprehensions
+
+test("[for @");
+test("[for ( @");
+test("[for (x @");
+test("[for (x of @");
+test("[for (x of y @");
+test("[for (x of y) @");
+test("[for (x of y) x @");
+test("[for (x of y) if @");
+test("[for (x of y) if ( @");
+test("[for (x of y) if (x @");
+test("[for (x of y) if (x == @");
+test("[for (x of y) if (x == 1 @");
+test("[for (x of y) if (x == 1) @");
+test("[for (x of y) if (x == 1) x @");
+test("[for (x of y) if (x == 1) x] @");
+
+// Generator comprehensions
+
+test("(for @");
+test("(for ( @");
+test("(for (x @");
+test("(for (x of @");
+test("(for (x of y @");
+test("(for (x of y) @");
+test("(for (x of y) x @");
+test("(for (x of y) if @");
+test("(for (x of y) if ( @");
+test("(for (x of y) if (x @");
+test("(for (x of y) if (x == @");
+test("(for (x of y) if (x == 1 @");
+test("(for (x of y) if (x == 1) @");
+test("(for (x of y) if (x == 1) x @");
+test("(for (x of y) if (x == 1) x) @");
+
+// ---- Left-hand-side expressions ----
+
+// property access
+
+test("a[ @");
+test("a[1 @");
+test("a[1] @");
+
+test("a. @");
+test("a.b @");
+test("a.b; @");
+
+// new
+
+test("new @");
+test("new f @");
+test("new f( @");
+test("new f() @");
+test("new f(); @");
+
+// ---- Increment and decrement ----
+
+test("a ++ @");
+test("a ++; @");
+
+test("-- @");
+test("-- a @");
+test("-- a; @");
+
+// ---- Unary operators ----
+
+// delete
+
+test("delete @");
+test("delete a @");
+test("delete a[ @");
+test("delete a[b @");
+test("delete a[b] @");
+test("delete a[b]; @");
+
+test("delete ( @");
+test("delete (a @");
+test("delete (a[ @");
+test("delete (a[b @");
+test("delete (a[b] @");
+test("delete (a[b]) @");
+test("delete (a[b]); @");
+
+// void
+
+test("void @");
+test("void a @");
+test("void a; @");
+
+test("void (@");
+test("void (a @");
+test("void (a) @");
+test("void (a); @");
+
+// typeof
+
+test("typeof @");
+test("typeof a @");
+test("typeof a; @");
+
+test("typeof (@");
+test("typeof (a @");
+test("typeof (a) @");
+test("typeof (a); @");
+
+// -
+
+test("- @");
+test("- 1 @");
+test("- 1; @");
+
+// +
+
+test("+ @");
+test("+ 1 @");
+test("+ 1; @");
+
+// ---- Arithmetic operators ----
+
+// +
+
+test("1 + @");
+test("1 + 1 @");
+test("1 + 1; @");
+
+// ---- Relational operators ----
+
+// in
+
+test("a in @");
+test("a in b @");
+test("a in b; @");
+
+// instanceof
+
+test("a instanceof @");
+test("a instanceof b @");
+test("a instanceof b; @");
+
+// ---- Equality operators ----
+
+// ==
+
+test("1 == @");
+test("1 == 1 @");
+test("1 == 1; @");
+
+// ---- Bitwise shift operators ----
+
+// <<
+
+test("1 << @");
+test("1 << 1 @");
+test("1 << 1; @");
+
+// ---- Binary bitwise operators ----
+
+// &
+
+test("1 & @");
+test("1 & 1 @");
+test("1 & 1; @");
+
+// ---- Binary logical operators ----
+
+// ||
+
+test("1 || @");
+test("1 || 1 @");
+test("1 || 1; @");
+
+// ---- Conditional (ternary) operator ----
+
+test("1 ? @");
+test("1 ? 2 @");
+test("1 ? 2 : @");
+test("1 ? 2 : 3 @");
+test("1 ? 2 : 3; @");
+
+// ---- Assignment operators ----
+
+test("x = @");
+test("x = 1 @");
+test("x = 1 + @");
+test("x = 1 + 2 @");
+test("x = 1 + 2; @");
+
+// ---- Comma operator ----
+
+test("1, @");
+test("1, 2 @");
+test("1, 2; @");
+
+// ---- Functions ----
+
+// Arrow functions
+
+test("a => @");
+test("a => 1 @");
+test("a => 1; @");
+test("a => { @");
+test("a => {} @");
+test("a => {}; @");
+
+test("( @");
+test("() @");
+test("() => @");
+
+test("(...@");
+test("(...a @");
+test("(...a) @");
+test("(...a) => @");
+
+test("([ @");
+test("([a @");
+test("([a] @");
+test("([a]) @");
+test("([a]) => @");
+
+test("({ @");
+test("({a @");
+test("({a} @");
+test("({a}) @");
+test("({a}) => @");
+test("({a: @");
+test("({a: b @");
+test("({a: b, @");
+test("({a: b} @");
+test("({a: b}) @");
+test("({a: b}) => @");
+
+// ---- Other ----
+
+// Literals
+
+test("a @");
+test("1 @");
+test("1. @");
+test("1.2 @");
+test("true @");
+test("false @");
+test("\"a\" @");
+test("'a' @");
+test("null @");
+
+// Template strings
+
+test("`${ @");
+test("`${a @");
+test("`${a}` @");
+
+// Unterminated Template strings
+// This should not be treated as Illegal character error.
+
+var caught = false;
+try {
+  Reflect.parse("`${1}@");
+} catch (e) {
+  caught = true;
+  assertEq(e.message, JSMSG_UNTERMINATED_STRING);
+}
+assertEq(caught, true);
+
+// Function calls
+
+test("f( @");
+test("f() @");
+test("f(); @");
+
+test("f(... @");
+test("f(...x @");
+test("f(...x) @");
+
+// Function constructors
+
+test_fun_arg("@");
+test_fun_arg("a @");
+test_fun_arg("... @");
+test_fun_arg("...a @");
+
+// ==== Legacy ====
+
+// let statement
+
+test("let ( @");
+test("let ( x @");
+test("let ( x = @");
+test("let ( x = 1 @");
+test("let ( x = 1, @");
+test("let ( x = 1, y @");
+test("let ( x = 1, y = @");
+test("let ( x = 1, y = 2 @");
+test("let ( x = 1, y = 2 ) @");
+test("let ( x = 1, y = 2 ) { @");
+test("let ( x = 1, y = 2 ) { x @");
+test("let ( x = 1, y = 2 ) { x; @");
+test("let ( x = 1, y = 2 ) { x; } @");
+test_no_strict("let ( x = 1, y = 2 ) x @");
+
+// Expression closures
+
+test("function f() 1 @");
+test("function f() 1; @");
+test("(function () 1 @");
+test("(function () 1); @");
+
+// Legacy generator
+
+test("function f() { (yield @");
+test("function f() { (yield 1 @");
+test("function f() { f(yield @");
+test("function f() { f(yield 1 @");
+
+// for each...in
+
+test("for each @");
+test("for each (@");
+test("for each (x @");
+test("for each (x in @");
+test("for each (x in y @");
+test("for each (x in y) @");
+
+test("for each (var @");
+test("for each (var x @");
+test("for each (var x in @");
+test("for each (var x in y @");
+test("for each (var x in y) @");
+
+test("for each (let @");
+test("for each (let x @");
+test("for each (let x in @");
+test("for each (let x in y @");
+test("for each (let x in y) @");
+
+// let expression
+
+test("(let @");
+test("(let ( @");
+test("(let ( x @");
+test("(let ( x = @");
+test("(let ( x = 1 @");
+test("(let ( x = 1, @");
+test("(let ( x = 1, y @");
+test("(let ( x = 1, y = @");
+test("(let ( x = 1, y = 2 @");
+test("(let ( x = 1, y = 2 ) @");
+test("(let ( x = 1, y = 2 ) x @");
+test("(let ( x = 1, y = 2 ) x) @");
+test("(let ( x = 1, y = 2 ) x); @");
+
+// Legacy array comprehensions
+
+test("[x @");
+test("[x for @");
+test("[x for ( @");
+test("[x for (x @");
+test("[x for (x of @");
+test("[x for (x of y @");
+test("[x for (x of y) @");
+test("[x for (x of y) if @");
+test("[x for (x of y) if ( @");
+test("[x for (x of y) if (x @");
+test("[x for (x of y) if (x == @");
+test("[x for (x of y) if (x == 1 @");
+test("[x for (x of y) if (x == 1) @");
+test("[x for (x of y) if (x == 1)] @");
+
+test("[x for (x in @");
+test("[x for (x in y @");
+test("[x for (x in y) @");
+
+test("[x for each @");
+test("[x for each ( @");
+test("[x for each (x @");
+test("[x for each (x in @");
+test("[x for each (x in y @");
+test("[x for each (x in y) @");
+
+// Generator expressions
+
+test("(x @");
+test("(x for @");
+test("(x for ( @");
+test("(x for (x @");
+test("(x for (x of @");
+test("(x for (x of y @");
+test("(x for (x of y) @");
+test("(x for (x of y) if @");
+test("(x for (x of y) if ( @");
+test("(x for (x of y) if (x @");
+test("(x for (x of y) if (x == @");
+test("(x for (x of y) if (x == 1 @");
+test("(x for (x of y) if (x == 1) @");
+test("(x for (x of y) if (x == 1)) @");
+
+test("(x for (x in @");
+test("(x for (x in y @");
+test("(x for (x in y) @");
+
+test("(x for each @");
+test("(x for each ( @");
+test("(x for each (x @");
+test("(x for each (x in @");
+test("(x for each (x in y @");
+test("(x for each (x in y) @");
--- a/js/src/jit-test/tests/ion/gc-during-bailout.js
+++ b/js/src/jit-test/tests/ion/gc-during-bailout.js
@@ -1,9 +1,11 @@
 setJitCompilerOption("ion.warmup.trigger", 30);
+gcPreserveCode();
+var o = {};
 
 function f(i) {
     var obj_1 = { a: 0 };
     var obj_2 = { a: 0 };
     var obj_3 = { a: 0 };
     var obj_4 = { a: 0 };
     var obj_5 = { a: 0 };
     var obj_6 = { a: 0 };
@@ -65,16 +67,92 @@ function f(i) {
     obj_25.a = 1;
     obj_26.a = 1;
     obj_27.a = 1;
     obj_28.a = 1;
     obj_29.a = 1;
     obj_30.a = 1;
 }
 
-gcPreserveCode();
-
-var o = {};
 for (var i = 0; i < 200; i++) {
     // Do not inline 'f', to keep re-entering 'f' at every loop iteration.
     with (o) { }
     f(i);
 }
+
+// This is the same test except that we do not preserve code under shrinking GC.
+gczeal(14);
+
+function g(i) {
+    var obj_1 = { a: 0 };
+    var obj_2 = { a: 0 };
+    var obj_3 = { a: 0 };
+    var obj_4 = { a: 0 };
+    var obj_5 = { a: 0 };
+    var obj_6 = { a: 0 };
+    var obj_7 = { a: 0 };
+    var obj_8 = { a: 0 };
+    var obj_9 = { a: 0 };
+    var obj_10 = { a: 0 };
+    var obj_11 = { a: 0 };
+    var obj_12 = { a: 0 };
+    var obj_13 = { a: 0 };
+    var obj_14 = { a: 0 };
+    var obj_15 = { a: 0 };
+    var obj_16 = { a: 0 };
+    var obj_17 = { a: 0 };
+    var obj_18 = { a: 0 };
+    var obj_19 = { a: 0 };
+    var obj_20 = { a: 0 };
+    var obj_21 = { a: 0 };
+    var obj_22 = { a: 0 };
+    var obj_23 = { a: 0 };
+    var obj_24 = { a: 0 };
+    var obj_25 = { a: 0 };
+    var obj_26 = { a: 0 };
+    var obj_27 = { a: 0 };
+    var obj_28 = { a: 0 };
+    var obj_29 = { a: 0 };
+    var obj_30 = { a: 0 };
+
+    // Doing a bailout after the return of the function call implies that we
+    // cannot resume before the function call.  Thus, we would have to recover
+    // the 30 objects allocations during the bailout.
+    schedulegc(i % 100);
+    bailout();
+
+    obj_1.a = 1;
+    obj_2.a = 1;
+    obj_3.a = 1;
+    obj_4.a = 1;
+    obj_5.a = 1;
+    obj_6.a = 1;
+    obj_7.a = 1;
+    obj_8.a = 1;
+    obj_9.a = 1;
+    obj_10.a = 1;
+    obj_11.a = 1;
+    obj_12.a = 1;
+    obj_13.a = 1;
+    obj_14.a = 1;
+    obj_15.a = 1;
+    obj_16.a = 1;
+    obj_17.a = 1;
+    obj_18.a = 1;
+    obj_19.a = 1;
+    obj_20.a = 1;
+    obj_21.a = 1;
+    obj_22.a = 1;
+    obj_23.a = 1;
+    obj_24.a = 1;
+    obj_25.a = 1;
+    obj_26.a = 1;
+    obj_27.a = 1;
+    obj_28.a = 1;
+    obj_29.a = 1;
+    obj_30.a = 1;
+}
+
+for (var i = 0; i < 200; i++) {
+    // Do not inline 'g', to keep re-entering 'g' at every loop iteration.
+    with (o) { }
+    g(i);
+}
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -923,16 +923,17 @@ jit::ToggleBaselineSPS(JSRuntime *runtim
 static void
 MarkActiveBaselineScripts(JSRuntime *rt, const JitActivationIterator &activation)
 {
     for (jit::JitFrameIterator iter(activation); !iter.done(); ++iter) {
         switch (iter.type()) {
           case JitFrame_BaselineJS:
             iter.script()->baselineScript()->setActive();
             break;
+          case JitFrame_Bailout:
           case JitFrame_IonJS: {
             // Keep the baseline script around, since bailouts from the ion
             // jitcode might need to re-enter into the baseline jitcode.
             iter.script()->baselineScript()->setActive();
             for (InlineFrameIterator inlineIter(rt, &iter); inlineIter.more(); ++inlineIter)
                 inlineIter.script()->baselineScript()->setActive();
             break;
           }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1931,17 +1931,18 @@ JS_PUBLIC_API(void)
 JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value)
 {
     rt->gc.setParameter(key, value);
 }
 
 JS_PUBLIC_API(uint32_t)
 JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key)
 {
-    return rt->gc.getParameter(key);
+    AutoLockGC lock(rt);
+    return rt->gc.getParameter(key, lock);
 }
 
 JS_PUBLIC_API(void)
 JS_SetGCParameterForThread(JSContext *cx, JSGCParamKey key, uint32_t value)
 {
     MOZ_ASSERT(key == JSGC_MAX_CODE_CACHE_BYTES);
 }
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -931,41 +931,43 @@ js::FindBody(JSContext *cx, HandleFuncti
         return false;
 
     const mozilla::Range<const char16_t> srcChars = stableChars.twoByteRange();
     TokenStream ts(cx, options, srcChars.start().get(), srcChars.length(), nullptr);
     int nest = 0;
     bool onward = true;
     // Skip arguments list.
     do {
-        switch (ts.getToken()) {
+        TokenKind tt;
+        if (!ts.getToken(&tt))
+            return false;
+        switch (tt) {
           case TOK_NAME:
           case TOK_YIELD:
             if (nest == 0)
                 onward = false;
             break;
           case TOK_LP:
             nest++;
             break;
           case TOK_RP:
             if (--nest == 0)
                 onward = false;
             break;
-          case TOK_ERROR:
-            // Must be memory.
-            return false;
           default:
             break;
         }
     } while (onward);
-    TokenKind tt = ts.getToken();
-    if (tt == TOK_ARROW)
-        tt = ts.getToken();
-    if (tt == TOK_ERROR)
+    TokenKind tt;
+    if (!ts.getToken(&tt))
         return false;
+    if (tt == TOK_ARROW) {
+        if (!ts.getToken(&tt))
+            return false;
+    }
     bool braced = tt == TOK_LC;
     MOZ_ASSERT_IF(fun->isExprClosure(), !braced);
     *bodyStart = ts.currentToken().pos.begin;
     if (braced)
         *bodyStart += 1;
     mozilla::RangedPtr<const char16_t> end = srcChars.end();
     if (end[-1] == '}') {
         end--;
@@ -1655,22 +1657,19 @@ js_fun_bind(JSContext *cx, HandleObject 
     return funobj;
 }
 
 /*
  * Report "malformed formal parameter" iff no illegal char or similar scanner
  * error was already reported.
  */
 static bool
-OnBadFormal(JSContext *cx, TokenKind tt)
+OnBadFormal(JSContext *cx)
 {
-    if (tt != TOK_ERROR)
-        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_FORMAL);
-    else
-        MOZ_ASSERT(cx->isExceptionPending());
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_FORMAL);
     return false;
 }
 
 const JSFunctionSpec js::function_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,   fun_toSource,   0,0),
 #endif
     JS_FN(js_toString_str,   fun_toString,   0,0),
@@ -1801,60 +1800,61 @@ FunctionConstructor(JSContext *cx, unsig
          * here (duplicate argument names, etc.) will be detected when we
          * compile the function body.
          */
         TokenStream ts(cx, options, collected_args.get(), args_length,
                        /* strictModeGetter = */ nullptr);
         bool yieldIsValidName = ts.versionNumber() < JSVERSION_1_7 && !isStarGenerator;
 
         /* The argument string may be empty or contain no tokens. */
-        TokenKind tt = ts.getToken();
+        TokenKind tt;
+        if (!ts.getToken(&tt))
+            return false;
         if (tt != TOK_EOF) {
             for (;;) {
-                /*
-                 * Check that it's a name.  This also implicitly guards against
-                 * TOK_ERROR, which was already reported.
-                 */
+                /* Check that it's a name. */
                 if (hasRest) {
                     ts.reportError(JSMSG_PARAMETER_AFTER_REST);
                     return false;
                 }
 
                 if (tt == TOK_YIELD && yieldIsValidName)
                     tt = TOK_NAME;
 
                 if (tt != TOK_NAME) {
                     if (tt == TOK_TRIPLEDOT) {
                         hasRest = true;
-                        tt = ts.getToken();
+                        if (!ts.getToken(&tt))
+                            return false;
                         if (tt == TOK_YIELD && yieldIsValidName)
                             tt = TOK_NAME;
                         if (tt != TOK_NAME) {
-                            if (tt != TOK_ERROR)
-                                ts.reportError(JSMSG_NO_REST_NAME);
+                            ts.reportError(JSMSG_NO_REST_NAME);
                             return false;
                         }
                     } else {
-                        return OnBadFormal(cx, tt);
+                        return OnBadFormal(cx);
                     }
                 }
 
                 if (!formals.append(ts.currentName()))
                     return false;
 
                 /*
                  * Get the next token.  Stop on end of stream.  Otherwise
                  * insist on a comma, get another name, and iterate.
                  */
-                tt = ts.getToken();
+                if (!ts.getToken(&tt))
+                    return false;
                 if (tt == TOK_EOF)
                     break;
                 if (tt != TOK_COMMA)
-                    return OnBadFormal(cx, tt);
-                tt = ts.getToken();
+                    return OnBadFormal(cx);
+                if (!ts.getToken(&tt))
+                    return false;
             }
         }
     }
 
 #ifdef DEBUG
     for (unsigned i = 0; i < formals.length(); ++i) {
         JSString *str = formals[i];
         MOZ_ASSERT(str->asAtom().asPropertyName() == formals[i]);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -703,69 +703,72 @@ ChunkPool::Enum::popFront()
 inline void
 ChunkPool::Enum::removeAndPopFront()
 {
     MOZ_ASSERT(!empty());
     *chunkp = front()->info.next;
     --pool.count_;
 }
 
-/* Must be called either during the GC or with the GC lock taken. */
 Chunk *
-GCRuntime::expireChunkPool(bool shrinkBuffers, bool releaseAll)
+GCRuntime::expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC &lock)
 {
     /*
      * Return old empty chunks to the system while preserving the order of
      * other chunks in the list. This way, if the GC runs several times
      * without emptying the list, the older chunks will stay at the tail
      * and are more likely to reach the max age.
      */
     Chunk *freeList = nullptr;
     unsigned freeChunkCount = 0;
-    for (ChunkPool::Enum e(emptyChunks); !e.empty(); ) {
+    for (ChunkPool::Enum e(emptyChunks(lock)); !e.empty(); ) {
         Chunk *chunk = e.front();
         MOZ_ASSERT(chunk->unused());
         MOZ_ASSERT(!chunkSet.has(chunk));
-        if (releaseAll || freeChunkCount >= tunables.maxEmptyChunkCount() ||
+        if (freeChunkCount >= tunables.maxEmptyChunkCount() ||
             (freeChunkCount >= tunables.minEmptyChunkCount() &&
              (shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
         {
             e.removeAndPopFront();
             prepareToFreeChunk(chunk->info);
             chunk->info.next = freeList;
             freeList = chunk;
         } else {
             /* Keep the chunk but increase its age. */
             ++freeChunkCount;
             ++chunk->info.age;
             e.popFront();
         }
     }
-    MOZ_ASSERT(emptyChunks.count() <= tunables.maxEmptyChunkCount());
-    MOZ_ASSERT_IF(shrinkBuffers, emptyChunks.count() <= tunables.minEmptyChunkCount());
-    MOZ_ASSERT_IF(releaseAll, emptyChunks.count() == 0);
+    MOZ_ASSERT(emptyChunks(lock).count() <= tunables.maxEmptyChunkCount());
+    MOZ_ASSERT_IF(shrinkBuffers, emptyChunks(lock).count() <= tunables.minEmptyChunkCount());
     return freeList;
 }
 
 void
+GCRuntime::freeEmptyChunks(JSRuntime *rt)
+{
+    for (ChunkPool::Enum e(emptyChunks_); !e.empty();) {
+        Chunk *chunk = e.front();
+        e.removeAndPopFront();
+        MOZ_ASSERT(!chunk->info.numArenasFreeCommitted);
+        FreeChunk(rt, chunk);
+    }
+}
+
+void
 GCRuntime::freeChunkList(Chunk *chunkListHead)
 {
     while (Chunk *chunk = chunkListHead) {
         MOZ_ASSERT(!chunk->info.numArenasFreeCommitted);
         chunkListHead = chunk->info.next;
         FreeChunk(rt, chunk);
     }
 }
 
-void
-GCRuntime::expireAndFreeChunkPool(bool releaseAll)
-{
-    freeChunkList(expireChunkPool(true, releaseAll));
-}
-
 /* static */ Chunk *
 Chunk::allocate(JSRuntime *rt)
 {
     Chunk *chunk = AllocChunk(rt);
     if (!chunk)
         return nullptr;
     chunk->init(rt);
     rt->gc.stats.count(gcstats::STAT_NEW_CHUNK);
@@ -1047,40 +1050,42 @@ Chunk::releaseArena(ArenaHeader *aheader
 
     if (info.numArenasFree == 1) {
         MOZ_ASSERT(!info.prevp);
         MOZ_ASSERT(!info.next);
         addToAvailableList(zone);
     } else if (!unused()) {
         MOZ_ASSERT(info.prevp);
     } else {
+        if (maybeLock.isNothing())
+            maybeLock.emplace(rt);
         MOZ_ASSERT(unused());
         removeFromAvailableList();
         decommitAllArenas(rt);
-        rt->gc.moveChunkToFreePool(this);
+        rt->gc.moveChunkToFreePool(this, maybeLock.ref());
     }
 }
 
 void
-GCRuntime::moveChunkToFreePool(Chunk *chunk)
+GCRuntime::moveChunkToFreePool(Chunk *chunk, const AutoLockGC &lock)
 {
     MOZ_ASSERT(chunk->unused());
     MOZ_ASSERT(chunkSet.has(chunk));
     chunkSet.remove(chunk);
-    emptyChunks.put(chunk);
+    emptyChunks(lock).put(chunk);
 }
 
 inline bool
 GCRuntime::wantBackgroundAllocation(const AutoLockGC &lock) const
 {
     // To minimize memory waste, we do not want to run the background chunk
     // allocation if we already have some empty chunks or when the runtime has
     // a small heap size (and therefore likely has a small growth rate).
     return allocTask.enabled() &&
-           emptyChunks.count() < tunables.minEmptyChunkCount() &&
+           emptyChunks(lock).count() < tunables.minEmptyChunkCount() &&
            chunkSet.count() >= 4;
 }
 
 void
 GCRuntime::startBackgroundAllocTaskIfIdle()
 {
     AutoLockHelperThreadState helperLock;
     if (allocTask.isRunning())
@@ -1119,17 +1124,17 @@ Chunk *
 GCRuntime::pickChunk(const AutoLockGC &lock, Zone *zone,
                      AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
 {
     Chunk **listHeadp = getAvailableChunkList(zone);
     Chunk *chunk = *listHeadp;
     if (chunk)
         return chunk;
 
-    chunk = emptyChunks.get(rt);
+    chunk = emptyChunks(lock).get(rt);
     if (!chunk) {
         chunk = Chunk::allocate(rt);
         if (!chunk)
             return nullptr;
         MOZ_ASSERT(chunk->info.numArenasFreeCommitted == 0);
     }
 
     MOZ_ASSERT(chunk->unused());
@@ -1236,17 +1241,17 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     inUnsafeRegion(0),
 #endif
     alwaysPreserveCode(false),
 #ifdef DEBUG
     noGCOrAllocationCheck(0),
 #endif
     lock(nullptr),
     lockOwner(nullptr),
-    allocTask(rt, emptyChunks),
+    allocTask(rt, emptyChunks_),
     helperState(rt)
 {
     setGCMode(JSGC_MODE_GLOBAL);
 }
 
 #ifdef JS_GC_ZEAL
 
 const char *gc::ZealModeHelpText =
@@ -1414,17 +1419,17 @@ GCRuntime::finish()
     systemAvailableChunkListHead = nullptr;
     userAvailableChunkListHead = nullptr;
     if (chunkSet.initialized()) {
         for (GCChunkSet::Range r(chunkSet.all()); !r.empty(); r.popFront())
             releaseChunk(r.front());
         chunkSet.clear();
     }
 
-    expireAndFreeChunkPool(true);
+    freeEmptyChunks(rt);
 
     if (rootsHash.initialized())
         rootsHash.clear();
 
     FinishPersistentRootedChains(rt);
 
     if (lock) {
         PR_DestroyLock(lock);
@@ -1530,31 +1535,31 @@ GCSchedulingTunables::setParameter(JSGCP
         MOZ_ASSERT(maxEmptyChunkCount_ >= minEmptyChunkCount_);
         break;
       default:
         MOZ_CRASH("Unknown GC parameter.");
     }
 }
 
 uint32_t
-GCRuntime::getParameter(JSGCParamKey key)
+GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC &lock)
 {
     switch (key) {
       case JSGC_MAX_BYTES:
         return uint32_t(tunables.gcMaxBytes());
       case JSGC_MAX_MALLOC_BYTES:
         return maxMallocBytes;
       case JSGC_BYTES:
         return uint32_t(usage.gcBytes());
       case JSGC_MODE:
         return uint32_t(mode);
       case JSGC_UNUSED_CHUNKS:
-        return uint32_t(emptyChunks.count());
+        return uint32_t(emptyChunks(lock).count());
       case JSGC_TOTAL_CHUNKS:
-        return uint32_t(chunkSet.count() + emptyChunks.count());
+        return uint32_t(chunkSet.count() + emptyChunks(lock).count());
       case JSGC_SLICE_TIME_BUDGET:
         return uint32_t(sliceBudget > 0 ? sliceBudget / PRMJ_USEC_PER_MSEC : 0);
       case JSGC_MARK_STACK_LIMIT:
         return marker.maxCapacity();
       case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
         return tunables.highFrequencyThresholdUsec();
       case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
         return tunables.highFrequencyLowLimitBytes() / 1024 / 1024;
@@ -2548,17 +2553,17 @@ GCRuntime::releaseRelocatedArenas(ArenaH
                   JS_MOVED_TENURED_PATTERN, Arena::thingsSpan(thingSize));
 #endif
 
         aheader->chunk()->releaseArena(aheader);
         ++count;
     }
 
     AutoLockGC lock(rt);
-    expireChunksAndArenas(true);
+    expireChunksAndArenas(true, lock);
 }
 
 #endif // JSGC_COMPACTING
 
 void
 ArenaLists::finalizeNow(FreeOp *fop, const FinalizePhase& phase)
 {
     gcstats::AutoPhase ap(fop->runtime()->gc.stats, phase.statsPhase);
@@ -3211,25 +3216,24 @@ GCRuntime::decommitArenasFromAvailableLi
 
 void
 GCRuntime::decommitArenas()
 {
     decommitArenasFromAvailableList(&systemAvailableChunkListHead);
     decommitArenasFromAvailableList(&userAvailableChunkListHead);
 }
 
-/* Must be called with the GC lock taken. */
 void
-GCRuntime::expireChunksAndArenas(bool shouldShrink)
+GCRuntime::expireChunksAndArenas(bool shouldShrink, const AutoLockGC &lock)
 {
 #ifdef JSGC_FJGENERATIONAL
     rt->threadPool.pruneChunkCache();
 #endif
 
-    if (Chunk *toFree = expireChunkPool(shouldShrink, false)) {
+    if (Chunk *toFree = expireEmptyChunkPool(shouldShrink, lock)) {
         AutoUnlockGC unlock(rt);
         freeChunkList(toFree);
     }
 
     if (shouldShrink)
         decommitArenas();
 }
 
@@ -3367,17 +3371,17 @@ GCHelperState::work()
     switch (state()) {
 
       case IDLE:
         MOZ_CRASH("GC helper triggered on idle state");
         break;
 
       case SWEEPING: {
         AutoTraceLog logSweeping(logger, TraceLogger::GCSweeping);
-        doSweep();
+        doSweep(lock);
         MOZ_ASSERT(state() == SWEEPING);
         break;
       }
 
     }
 
     setState(IDLE);
     thread = nullptr;
@@ -3449,40 +3453,39 @@ GCHelperState::waitBackgroundSweepEnd()
 {
     AutoLockGC lock(rt);
     while (state() == SWEEPING)
         waitForBackgroundThread();
     if (rt->gc.incrementalState == NO_INCREMENTAL)
         rt->gc.assertBackgroundSweepingFinished();
 }
 
-/* Must be called with the GC lock taken. */
 void
-GCHelperState::doSweep()
+GCHelperState::doSweep(const AutoLockGC &lock)
 {
     if (sweepFlag) {
         sweepFlag = false;
         AutoUnlockGC unlock(rt);
 
         rt->gc.sweepBackgroundThings();
 
         rt->freeLifoAlloc.freeAll();
     }
 
     bool shrinking = shrinkFlag;
-    rt->gc.expireChunksAndArenas(shrinking);
+    rt->gc.expireChunksAndArenas(shrinking, lock);
 
     /*
      * The main thread may have called ShrinkGCBuffers while
      * ExpireChunksAndArenas(rt, false) was running, so we recheck the flag
      * afterwards.
      */
     if (!shrinking && shrinkFlag) {
         shrinkFlag = false;
-        rt->gc.expireChunksAndArenas(true);
+        rt->gc.expireChunksAndArenas(true, lock);
     }
 }
 
 bool
 GCHelperState::onBackgroundThread()
 {
     return PR_GetCurrentThread() == thread;
 }
@@ -5250,17 +5253,17 @@ GCRuntime::endSweepPhase(bool lastGC)
         if (!sweepOnBackgroundThread) {
             /*
              * Destroy arenas after we finished the sweeping so finalizers can
              * safely use IsAboutToBeFinalized(). This is done on the
              * GCHelperState if possible. We acquire the lock only because
              * Expire needs to unlock it for other callers.
              */
             AutoLockGC lock(rt);
-            expireChunksAndArenas(invocationKind == GC_SHRINK);
+            expireChunksAndArenas(invocationKind == GC_SHRINK, lock);
         }
     }
 
     {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_END);
         callFinalizeCallbacks(&fop, JSFINALIZE_COLLECTION_END);
 
         /* If we finished a full GC, then the gray bits are correct. */
@@ -6136,17 +6139,17 @@ GCRuntime::shrinkBuffers()
 {
     AutoLockHelperThreadState helperLock;
     AutoLockGC lock(rt);
     MOZ_ASSERT(!rt->isHeapBusy());
 
     if (CanUseExtraThreads())
         helperState.startBackgroundShrink();
     else
-        expireChunksAndArenas(true);
+        expireChunksAndArenas(true, lock);
 }
 
 void
 GCRuntime::minorGC(JS::gcreason::Reason reason)
 {
 #ifdef JSGC_GENERATIONAL
     minorGCRequested = false;
     TraceLogger *logger = TraceLoggerForMainThread(rt);
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -19,16 +19,18 @@
 #include "js/GCAPI.h"
 #include "js/SliceBudget.h"
 #include "js/Vector.h"
 
 #include "vm/NativeObject.h"
 
 namespace js {
 
+class AutoLockGC;
+
 namespace gc {
 class ForkJoinNursery;
 }
 
 unsigned GetCPUCount();
 
 enum HeapState {
     Idle,             // doing nothing with the GC heap
@@ -1036,17 +1038,17 @@ class GCHelperState
     static void freeElementsAndArray(void **array, void **end) {
         MOZ_ASSERT(array <= end);
         for (void **p = array; p != end; ++p)
             js_free(*p);
         js_free(array);
     }
 
     /* Must be called with the GC lock taken. */
-    void doSweep();
+    void doSweep(const AutoLockGC &lock);
 
   public:
     explicit GCHelperState(JSRuntime *rt)
       : rt(rt),
         done(nullptr),
         state_(IDLE),
         thread(nullptr),
         sweepFlag(false),
--- a/layout/base/SelectionCarets.cpp
+++ b/layout/base/SelectionCarets.cpp
@@ -379,49 +379,52 @@ SelectionCarets::UpdateSelectionCarets()
   }
 
   nsRefPtr<dom::Selection> selection = GetSelection();
   if (!selection) {
     SetVisibility(false);
     return;
   }
 
-  if (selection->GetRangeCount() <= 0) {
+  if (selection->IsCollapsed()) {
     SetVisibility(false);
     return;
   }
 
-  nsRefPtr<nsRange> range = selection->GetRangeAt(0);
-  if (range->Collapsed()) {
-    SetVisibility(false);
-    return;
-  }
+  int32_t rangeCount = selection->GetRangeCount();
+  nsRefPtr<nsRange> firstRange = selection->GetRangeAt(0);
+  nsRefPtr<nsRange> lastRange = selection->GetRangeAt(rangeCount - 1);
 
-  nsLayoutUtils::FirstAndLastRectCollector collector;
-  nsRange::CollectClientRects(&collector, range,
-                              range->GetStartParent(), range->StartOffset(),
-                              range->GetEndParent(), range->EndOffset(), true, true);
+  nsLayoutUtils::FirstAndLastRectCollector collectorStart;
+  nsRange::CollectClientRects(&collectorStart, firstRange,
+                              firstRange->GetStartParent(), firstRange->StartOffset(),
+                              firstRange->GetEndParent(), firstRange->EndOffset(), true, true);
+
+  nsLayoutUtils::FirstAndLastRectCollector collectorEnd;
+  nsRange::CollectClientRects(&collectorEnd, lastRange,
+                              lastRange->GetStartParent(), lastRange->StartOffset(),
+                              lastRange->GetEndParent(), lastRange->EndOffset(), true, true);
 
   nsIFrame* canvasFrame = mPresShell->GetCanvasFrame();
   nsIFrame* rootFrame = mPresShell->GetRootFrame();
 
   if (!canvasFrame || !rootFrame) {
     SetVisibility(false);
     return;
   }
 
   // Check start and end frame is rtl or ltr text
   nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
   int32_t startOffset;
   nsIFrame* startFrame = FindFirstNodeWithFrame(mPresShell->GetDocument(),
-                                                range, fs, false, startOffset);
+                                                firstRange, fs, false, startOffset);
 
   int32_t endOffset;
   nsIFrame* endFrame = FindFirstNodeWithFrame(mPresShell->GetDocument(),
-                                              range, fs, true, endOffset);
+                                              lastRange, fs, true, endOffset);
 
   if (!startFrame || !endFrame) {
     SetVisibility(false);
     return;
   }
 
   // If frame isn't editable and we don't support non-editable fields, bail
   // out.
@@ -437,46 +440,46 @@ SelectionCarets::UpdateSelectionCarets()
     return;
   }
 
   bool startFrameIsRTL = IsRightToLeft(startFrame);
   bool endFrameIsRTL = IsRightToLeft(endFrame);
 
   // If start frame is LTR, then place start caret in first rect's leftmost
   // otherwise put it to first rect's rightmost.
-  ReduceRectToVerticalEdge(collector.mFirstRect, startFrameIsRTL);
+  ReduceRectToVerticalEdge(collectorStart.mFirstRect, startFrameIsRTL);
 
   // Contrary to start frame, if end frame is LTR, put end caret to last
   // rect's rightmost position, otherwise, put it to last rect's leftmost.
-  ReduceRectToVerticalEdge(collector.mLastRect, !endFrameIsRTL);
+  ReduceRectToVerticalEdge(collectorEnd.mLastRect, !endFrameIsRTL);
 
   nsAutoTArray<nsIFrame*, 16> hitFramesInFirstRect;
   nsLayoutUtils::GetFramesForArea(rootFrame,
-    collector.mFirstRect,
+    collectorStart.mFirstRect,
     hitFramesInFirstRect,
     nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
       nsLayoutUtils::IGNORE_CROSS_DOC |
       nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
 
   nsAutoTArray<nsIFrame*, 16> hitFramesInLastRect;
   nsLayoutUtils::GetFramesForArea(rootFrame,
-    collector.mLastRect,
+    collectorEnd.mLastRect,
     hitFramesInLastRect,
     nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
       nsLayoutUtils::IGNORE_CROSS_DOC |
       nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
 
   SetStartFrameVisibility(hitFramesInFirstRect.Contains(startFrame));
   SetEndFrameVisibility(hitFramesInLastRect.Contains(endFrame));
 
-  nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collector.mFirstRect);
-  nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collector.mLastRect);
+  nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collectorStart.mFirstRect);
+  nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collectorEnd.mLastRect);
 
-  SetStartFramePos(collector.mFirstRect.BottomLeft());
-  SetEndFramePos(collector.mLastRect.BottomRight());
+  SetStartFramePos(collectorStart.mFirstRect.BottomLeft());
+  SetEndFramePos(collectorEnd.mLastRect.BottomRight());
   SetVisibility(true);
 
   // If range select only one character, append tilt class name to it.
   bool isTilt = false;
   if (startFrame && endFrame) {
     // In this case <textarea>abc</textarea> and we select 'c' character,
     // EndContent would be HTMLDivElement and mResultContent which get by
     // calling startFrame->PeekOffset() with selecting next cluster would be
@@ -684,21 +687,23 @@ SelectionCarets::DragSelection(const nsP
 
   nsFrame::ContentOffsets offsets =
     newFrame->GetContentOffsetsFromPoint(newPoint);
   if (!offsets.content) {
     return nsEventStatus_eConsumeNoDefault;
   }
 
   nsRefPtr<dom::Selection> selection = GetSelection();
-  if (selection->GetRangeCount() <= 0) {
+  int32_t rangeCount = selection->GetRangeCount();
+  if (rangeCount <= 0) {
     return nsEventStatus_eConsumeNoDefault;
   }
 
-  nsRefPtr<nsRange> range = selection->GetRangeAt(0);
+  nsRefPtr<nsRange> range = mDragMode == START_FRAME ?
+    selection->GetRangeAt(0) : selection->GetRangeAt(rangeCount - 1);
   if (!CompareRangeWithContentOffset(range, fs, offsets, mDragMode)) {
     return nsEventStatus_eConsumeNoDefault;
   }
 
   nsIFrame* anchorFrame;
   selection->GetPrimaryFrameForAnchorNode(&anchorFrame);
   if (!anchorFrame) {
     return nsEventStatus_eConsumeNoDefault;
@@ -732,30 +737,32 @@ SelectionCarets::GetCaretYCenterPosition
 {
   nsIFrame* canvasFrame = mPresShell->GetCanvasFrame();
 
   if (!canvasFrame) {
     return 0;
   }
 
   nsRefPtr<dom::Selection> selection = GetSelection();
-  if (selection->GetRangeCount() <= 0) {
+  int32_t rangeCount = selection->GetRangeCount();
+  if (rangeCount <= 0) {
     return 0;
   }
 
-  nsRefPtr<nsRange> range = selection->GetRangeAt(0);
   nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
 
   MOZ_ASSERT(mDragMode != NONE);
   nsCOMPtr<nsIContent> node;
   uint32_t nodeOffset;
   if (mDragMode == START_FRAME) {
+    nsRefPtr<nsRange> range = selection->GetRangeAt(0);
     node = do_QueryInterface(range->GetStartParent());
     nodeOffset = range->StartOffset();
   } else {
+    nsRefPtr<nsRange> range = selection->GetRangeAt(rangeCount - 1);
     node = do_QueryInterface(range->GetEndParent());
     nodeOffset = range->EndOffset();
   }
 
   int32_t offset;
   CaretAssociationHint hint =
     mDragMode == START_FRAME ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE;
   nsIFrame* theFrame =
@@ -880,22 +887,16 @@ SelectionCarets::GetFrameSelection()
   }
 }
 
 nsresult
 SelectionCarets::NotifySelectionChanged(nsIDOMDocument* aDoc,
                                        nsISelection* aSel,
                                        int16_t aReason)
 {
-  bool isCollapsed;
-  aSel->GetIsCollapsed(&isCollapsed);
-  if (isCollapsed) {
-    SetVisibility(false);
-    return NS_OK;
-  }
   if (!aReason || (aReason & (nsISelectionListener::DRAG_REASON |
                                nsISelectionListener::KEYPRESS_REASON |
                                nsISelectionListener::MOUSEDOWN_REASON))) {
     SetVisibility(false);
   } else {
     UpdateSelectionCarets();
   }
   return NS_OK;
--- a/layout/base/tests/marionette/manifest.ini
+++ b/layout/base/tests/marionette/manifest.ini
@@ -8,8 +8,9 @@ browser = true
 ; true if the test is compatible with b2g, otherwise false
 b2g = true
 
 ; true if the test should be skipped
 skip = false
 
 [test_touchcaret.py]
 [test_selectioncarets.py]
+[test_selectioncarets_multiplerange.py]
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/marionette/test_selectioncarets_multiplerange.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+# 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/.
+
+from by import By
+from marionette import Actions
+from marionette_test import MarionetteTestCase
+from selection import SelectionManager
+
+
+class SelectionCaretsMultipleRangeTest(MarionetteTestCase):
+    _long_press_time = 1        # 1 second
+
+    def setUp(self):
+        # Code to execute before a tests are run.
+        MarionetteTestCase.setUp(self)
+        self.actions = Actions(self.marionette)
+
+    def openTestHtml(self, enabled=True):
+        # Open html for testing and enable selectioncaret and
+        # non-editable support
+        self.marionette.execute_script(
+            'SpecialPowers.setBoolPref("selectioncaret.enabled", %s);' %
+            ('true' if enabled else 'false'))
+        self.marionette.execute_script(
+            'SpecialPowers.setBoolPref("selectioncaret.noneditable", %s);' %
+            ('true' if enabled else 'false'))
+
+        test_html = self.marionette.absolute_url('test_selectioncarets_multiplerange.html')
+        self.marionette.navigate(test_html)
+
+        self._body = self.marionette.find_element(By.ID, 'bd')
+        self._sel3 = self.marionette.find_element(By.ID, 'sel3')
+        self._sel4 = self.marionette.find_element(By.ID, 'sel4')
+        self._sel6 = self.marionette.find_element(By.ID, 'sel6')
+        self._nonsel1 = self.marionette.find_element(By.ID, 'nonsel1')
+
+    def _long_press_without_contextmenu(self, el, x, y):
+        return self.actions.press(el, x, y).move_by_offset(0, 0).\
+            wait(self._long_press_time).release()
+
+    def _long_press_to_select_word(self, el, wordOrdinal):
+        sel = SelectionManager(el)
+        original_content = sel.content
+        words = original_content.split()
+        self.assertTrue(wordOrdinal < len(words),
+            'Expect at least %d words in the content.' % wordOrdinal)
+
+        # Calc offset
+        offset = 0
+        for i in range(wordOrdinal):
+            offset += (len(words[i]) + 1)
+
+        # Move caret inside the word.
+        el.tap()
+        sel.move_caret_to_front()
+        sel.move_caret_by_offset(offset)
+        x, y = sel.caret_location()
+
+        # Long press the caret position. Selection carets should appear, and the
+        # word will be selected. On Windows, those spaces after the word
+        # will also be selected.
+        self._long_press_without_contextmenu(el, x, y).perform()
+
+    def _to_unix_line_ending(self, s):
+        """Changes all Windows/Mac line endings in s to UNIX line endings."""
+
+        return s.replace('\r\n', '\n').replace('\r', '\n')
+
+    def test_long_press_to_select_non_selectable_word(self):
+        '''Testing long press on non selectable field.
+        We should not select anything when long press on non selectable fields.'''
+
+        self.openTestHtml(enabled=True)
+        halfY = self._nonsel1.size['height'] / 2
+        self._long_press_without_contextmenu(self._nonsel1, 0, halfY).perform()
+        sel = SelectionManager(self._nonsel1)
+        range_count = sel.range_count()
+        self.assertEqual(range_count, 0)
+
+    def test_drag_caret_over_non_selectable_field(self):
+        '''Testing drag caret over non selectable field.
+        So that the selected content should exclude non selectable field and
+        end selection caret should appear in last range's position.'''
+        self.openTestHtml(enabled=True)
+
+        # Select target element and get target caret location
+        self._long_press_to_select_word(self._sel4, 3)
+        sel = SelectionManager(self._body)
+        (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location()
+
+        self._long_press_to_select_word(self._sel6, 0)
+        (_, _), (end_caret2_x, end_caret2_y) = sel.selection_carets_location()
+
+        # Select start element
+        self._long_press_to_select_word(self._sel3, 3)
+
+        # Drag end caret to target location
+        (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
+        self.actions.flick(self._body, caret2_x, caret2_y, end_caret_x, end_caret_y, 1).perform()
+        self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()),
+            'this 3\nuser can select this')
+
+        (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
+        self.actions.flick(self._body, caret2_x, caret2_y, end_caret2_x, end_caret2_y, 1).perform()
+        self.assertEqual(self._to_unix_line_ending(sel.selected_content.strip()),
+            'this 3\nuser can select this 4\nuser can select this 5\nuser')
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -346,16 +346,23 @@ nsTextControlFrame::CreateAnonymousConte
       PresContext()->StyleSet()->ResolvePseudoElementStyle(
           mContent->AsElement(), pseudoType, StyleContext(),
           placeholderNode->AsElement());
 
     if (!aElements.AppendElement(ContentInfo(placeholderNode,
                                  placeholderStyleContext))) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
+
+    if (!IsSingleLineTextControl()) {
+      // For textareas, UpdateValueDisplay doesn't initialize the visibility
+      // status of the placeholder because it returns early, so we have to
+      // do that manually here.
+      txtCtrl->UpdatePlaceholderVisibility(true);
+    }
   }
 
   rv = UpdateValueDisplay(false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // textareas are eagerly initialized
   bool initEagerly = !IsSingleLineTextControl();
   if (!initEagerly) {
--- a/media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
@@ -220,17 +220,17 @@ typedef struct fsmdef_media_t_ {
      * port number used in m= data channel line
      */
     uint16_t       local_datachannel_port;
     uint16_t       remote_datachannel_port;
 
     /*
      * Data Channel properties
      */
-#define WEBRTC_DATACHANNEL_STREAMS_DEFAULT 16
+#define WEBRTC_DATACHANNEL_STREAMS_DEFAULT 256
     uint32         datachannel_streams;
     char           datachannel_protocol[SDP_MAX_STRING_LEN + 1];
 
     /*
      * This field contains the number of elements in the payloads field.
      */
     int32_t num_payloads;
 
--- a/netwerk/base/public/nsIStreamingProtocolController.idl
+++ b/netwerk/base/public/nsIStreamingProtocolController.idl
@@ -40,17 +40,17 @@ interface nsIStreamingProtocolMetaData :
     attribute unsigned long width;
 
     /**
      * The height of the resolution.
      */
     attribute unsigned long height;
 
     /**
-     * The duration of the media stream.
+     * The duration of the media stream in units of microseconds.
      */
     attribute unsigned long long duration;
 
     /**
      * The sample rate of the media stream.
      */
     attribute unsigned long sampleRate;
 
--- a/netwerk/cache2/CacheIndex.cpp
+++ b/netwerk/cache2/CacheIndex.cpp
@@ -48,30 +48,30 @@ public:
     , mOldFrecency(0)
     , mOldExpirationTime(nsICacheEntry::NO_EXPIRATION_TIME)
     , mDoNotSearchInIndex(false)
     , mDoNotSearchInUpdates(false)
   {
     mIndex->AssertOwnsLock();
 
     mHash = aHash;
-    CacheIndexEntry *entry = FindEntry();
+    const CacheIndexEntry *entry = FindEntry();
     mIndex->mIndexStats.BeforeChange(entry);
     if (entry && entry->IsInitialized() && !entry->IsRemoved()) {
       mOldRecord = entry->mRec;
       mOldFrecency = entry->mRec->mFrecency;
       mOldExpirationTime = entry->mRec->mExpirationTime;
     }
   }
 
   ~CacheIndexEntryAutoManage()
   {
     mIndex->AssertOwnsLock();
 
-    CacheIndexEntry *entry = FindEntry();
+    const CacheIndexEntry *entry = FindEntry();
     mIndex->mIndexStats.AfterChange(entry);
     if (!entry || !entry->IsInitialized() || entry->IsRemoved()) {
       entry = nullptr;
     }
 
     if (entry && !mOldRecord) {
       mIndex->InsertRecordToFrecencyArray(entry->mRec);
       mIndex->InsertRecordToExpirationArray(entry->mRec);
@@ -120,19 +120,19 @@ public:
   // We cannot rely on nsTHashtable::GetEntry() in case we are enumerating the
   // entries and returning PL_DHASH_REMOVE. Destructor is called before the
   // entry is removed. Caller must call one of following methods to skip
   // lookup in the hashtable.
   void DoNotSearchInIndex()   { mDoNotSearchInIndex = true; }
   void DoNotSearchInUpdates() { mDoNotSearchInUpdates = true; }
 
 private:
-  CacheIndexEntry * FindEntry()
+  const CacheIndexEntry * FindEntry()
   {
-    CacheIndexEntry *entry = nullptr;
+    const CacheIndexEntry *entry = nullptr;
 
     switch (mIndex->mState) {
       case CacheIndex::READING:
       case CacheIndex::WRITING:
         if (!mDoNotSearchInUpdates) {
           entry = mIndex->mPendingUpdates.GetEntry(*mHash);
         }
         // no break
@@ -515,16 +515,17 @@ CacheIndex::AddEntry(const SHA1Sum::Hash
   // non-fresh entries exists outside the scope of CacheIndexEntryAutoManage.
   bool updateIfNonFreshEntriesExist = false;
 
   {
     CacheIndexEntryAutoManage entryMng(aHash, index);
 
     CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
     bool entryRemoved = entry && entry->IsRemoved();
+    CacheIndexEntryUpdate *updated = nullptr;
 
     if (index->mState == READY || index->mState == UPDATING ||
         index->mState == BUILDING) {
       MOZ_ASSERT(index->mPendingUpdates.Count() == 0);
 
       if (entry && !entryRemoved) {
         // Found entry in index that shouldn't exist.
 
@@ -553,17 +554,17 @@ CacheIndex::AddEntry(const SHA1Sum::Hash
           MOZ_ASSERT(index->mState == UPDATING);
         }
       }
 
       if (!entry) {
         entry = index->mIndex.PutEntry(*aHash);
       }
     } else { // WRITING, READING
-      CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
+      updated = index->mPendingUpdates.GetEntry(*aHash);
       bool updatedRemoved = updated && updated->IsRemoved();
 
       if ((updated && !updatedRemoved) ||
           (!updated && entry && !entryRemoved && entry->IsFresh())) {
         // Fresh entry found, so the file was removed outside FF
         LOG(("CacheIndex::AddEntry() - Cache file was removed outside FF "
              "process!"));
 
@@ -573,22 +574,27 @@ CacheIndex::AddEntry(const SHA1Sum::Hash
           LOG(("CacheIndex::AddEntry() - Found entry that shouldn't exist, "
                "update is needed"));
           index->mIndexNeedsUpdate = true;
         }
         // Ignore if state is READING since the index information is partial
       }
 
       updated = index->mPendingUpdates.PutEntry(*aHash);
-      entry = updated;
     }
 
-    entry->InitNew();
-    entry->MarkDirty();
-    entry->MarkFresh();
+    if (updated) {
+      updated->InitNew();
+      updated->MarkDirty();
+      updated->MarkFresh();
+    } else {
+      entry->InitNew();
+      entry->MarkDirty();
+      entry->MarkFresh();
+    }
   }
 
   if (updateIfNonFreshEntriesExist &&
       index->mIndexStats.Count() != index->mIndexStats.Fresh()) {
     index->mIndexNeedsUpdate = true;
   }
 
   index->StartUpdatingIndexIfNeeded();
@@ -647,17 +653,17 @@ CacheIndex::EnsureEntryExists(const SHA1
         if (!entry) {
           entry = index->mIndex.PutEntry(*aHash);
         }
         entry->InitNew();
         entry->MarkDirty();
       }
       entry->MarkFresh();
     } else { // WRITING, READING
-      CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
+      CacheIndexEntryUpdate *updated = index->mPendingUpdates.GetEntry(*aHash);
       bool updatedRemoved = updated && updated->IsRemoved();
 
       if (updatedRemoved ||
           (!updated && entryRemoved && entry->IsFresh())) {
         // Fresh information about missing entry found. This could happen only
         // if somebody copies files to the entries directory while FF is running.
         LOG(("CacheIndex::EnsureEntryExists() - Cache file was added outside "
              "FF process! Update is needed."));
@@ -727,16 +733,17 @@ CacheIndex::InitEntry(const SHA1Sum::Has
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   {
     CacheIndexEntryAutoManage entryMng(aHash, index);
 
     CacheIndexEntry *entry = index->mIndex.GetEntry(*aHash);
+    CacheIndexEntryUpdate *updated = nullptr;
     bool reinitEntry = false;
 
     if (entry && entry->IsRemoved()) {
       entry = nullptr;
     }
 
     if (index->mState == READY || index->mState == UPDATING ||
         index->mState == BUILDING) {
@@ -748,61 +755,70 @@ CacheIndex::InitEntry(const SHA1Sum::Has
         index->mIndexNeedsUpdate = true; // TODO Does this really help in case of collision?
         reinitEntry = true;
       } else {
         if (entry->IsInitialized()) {
           return NS_OK;
         }
       }
     } else {
-      CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
+      updated = index->mPendingUpdates.GetEntry(*aHash);
       DebugOnly<bool> removed = updated && updated->IsRemoved();
 
       MOZ_ASSERT(updated || !removed);
       MOZ_ASSERT(updated || entry);
 
       if (updated) {
         MOZ_ASSERT(updated->IsFresh());
 
         if (IsCollision(updated, aAppId, aAnonymous, aInBrowser)) {
           index->mIndexNeedsUpdate = true;
           reinitEntry = true;
         } else {
           if (updated->IsInitialized()) {
             return NS_OK;
           }
         }
-        entry = updated;
       } else {
         MOZ_ASSERT(entry->IsFresh());
 
         if (IsCollision(entry, aAppId, aAnonymous, aInBrowser)) {
           index->mIndexNeedsUpdate = true;
           reinitEntry = true;
         } else {
           if (entry->IsInitialized()) {
             return NS_OK;
           }
         }
 
         // make a copy of a read-only entry
         updated = index->mPendingUpdates.PutEntry(*aHash);
         *updated = *entry;
-        entry = updated;
       }
     }
 
     if (reinitEntry) {
       // There is a collision and we are going to rewrite this entry. Initialize
       // it as a new entry.
-      entry->InitNew();
-      entry->MarkFresh();
+      if (updated) {
+        updated->InitNew();
+        updated->MarkFresh();
+      } else {
+        entry->InitNew();
+        entry->MarkFresh();
+      }
     }
-    entry->Init(aAppId, aAnonymous, aInBrowser);
-    entry->MarkDirty();
+
+    if (updated) {
+      updated->Init(aAppId, aAnonymous, aInBrowser);
+      updated->MarkDirty();
+    } else {
+      entry->Init(aAppId, aAnonymous, aInBrowser);
+      entry->MarkDirty();
+    }
   }
 
   index->StartUpdatingIndexIfNeeded();
   index->WriteIndexToDiskIfNeeded();
 
   return NS_OK;
 }
 
@@ -860,17 +876,17 @@ CacheIndex::RemoveEntry(const SHA1Sum::H
           } else {
             entry->MarkRemoved();
             entry->MarkDirty();
             entry->MarkFresh();
           }
         }
       }
     } else { // WRITING, READING
-      CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
+      CacheIndexEntryUpdate *updated = index->mPendingUpdates.GetEntry(*aHash);
       bool updatedRemoved = updated && updated->IsRemoved();
 
       if (updatedRemoved ||
           (!updated && entryRemoved && entry->IsFresh())) {
         // Fresh information about missing entry found. This could happen only
         // if somebody copies files to the entries directory while FF is running.
         LOG(("CacheIndex::RemoveEntry() - Cache file was added outside FF "
              "process! Update is needed."));
@@ -940,52 +956,68 @@ CacheIndex::UpdateEntry(const SHA1Sum::H
     if (index->mState == READY || index->mState == UPDATING ||
         index->mState == BUILDING) {
       MOZ_ASSERT(index->mPendingUpdates.Count() == 0);
       MOZ_ASSERT(entry);
 
       if (!HasEntryChanged(entry, aFrecency, aExpirationTime, aSize)) {
         return NS_OK;
       }
+
+      MOZ_ASSERT(entry->IsFresh());
+      MOZ_ASSERT(entry->IsInitialized());
+      entry->MarkDirty();
+
+      if (aFrecency) {
+        entry->SetFrecency(*aFrecency);
+      }
+
+      if (aExpirationTime) {
+        entry->SetExpirationTime(*aExpirationTime);
+      }
+
+      if (aSize) {
+        entry->SetFileSize(*aSize);
+      }
     } else {
-      CacheIndexEntry *updated = index->mPendingUpdates.GetEntry(*aHash);
+      CacheIndexEntryUpdate *updated = index->mPendingUpdates.GetEntry(*aHash);
       DebugOnly<bool> removed = updated && updated->IsRemoved();
 
       MOZ_ASSERT(updated || !removed);
       MOZ_ASSERT(updated || entry);
 
       if (!updated) {
-        if (entry &&
-            HasEntryChanged(entry, aFrecency, aExpirationTime, aSize)) {
-          // make a copy of a read-only entry
-          updated = index->mPendingUpdates.PutEntry(*aHash);
-          *updated = *entry;
-          entry = updated;
-        } else {
+        if (!entry) {
+          LOG(("CacheIndex::UpdateEntry() - Entry was found neither in mIndex "
+               "nor in mPendingUpdates!"));
+          NS_WARNING(("CacheIndex::UpdateEntry() - Entry was found neither in "
+                      "mIndex nor in mPendingUpdates!"));
           return NS_ERROR_NOT_AVAILABLE;
         }
-      } else {
-        entry = updated;
+
+        // make a copy of a read-only entry
+        updated = index->mPendingUpdates.PutEntry(*aHash);
+        *updated = *entry;
       }
-    }
-
-    MOZ_ASSERT(entry->IsFresh());
-    MOZ_ASSERT(entry->IsInitialized());
-    entry->MarkDirty();
-
-    if (aFrecency) {
-      entry->SetFrecency(*aFrecency);
-    }
-
-    if (aExpirationTime) {
-      entry->SetExpirationTime(*aExpirationTime);
-    }
-
-    if (aSize) {
-      entry->SetFileSize(*aSize);
+
+      MOZ_ASSERT(updated->IsFresh());
+      MOZ_ASSERT(updated->IsInitialized());
+      updated->MarkDirty();
+
+      if (aFrecency) {
+        updated->SetFrecency(*aFrecency);
+      }
+
+      if (aExpirationTime) {
+        updated->SetExpirationTime(*aExpirationTime);
+      }
+
+      if (aSize) {
+        updated->SetFileSize(*aSize);
+      }
     }
   }
 
   index->WriteIndexToDiskIfNeeded();
 
   return NS_OK;
 }
 
@@ -1091,17 +1123,17 @@ CacheIndex::HasEntry(const nsACString &a
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   SHA1Sum sum;
   SHA1Sum::Hash hash;
   sum.update(aKey.BeginReading(), aKey.Length());
   sum.finish(hash);
 
-  CacheIndexEntry *entry = nullptr;
+  const CacheIndexEntry *entry = nullptr;
 
   switch (index->mState) {
     case READING:
     case WRITING:
       entry = index->mPendingUpdates.GetEntry(hash);
       // no break
     case BUILDING:
     case UPDATING:
@@ -1476,17 +1508,17 @@ CacheIndex::ProcessPendingOperations()
 
   MOZ_ASSERT(mPendingUpdates.Count() == 0);
 
   EnsureCorrectStats();
 }
 
 // static
 PLDHashOperator
-CacheIndex::UpdateEntryInIndex(CacheIndexEntry *aEntry, void* aClosure)
+CacheIndex::UpdateEntryInIndex(CacheIndexEntryUpdate *aEntry, void* aClosure)
 {
   CacheIndex *index = static_cast<CacheIndex *>(aClosure);
 
   LOG(("CacheFile::UpdateEntryInIndex() [hash=%08x%08x%08x%08x%08x]",
        LOGSHA1(aEntry->Hash())));
 
   MOZ_ASSERT(aEntry->IsFresh());
 
@@ -1511,18 +1543,26 @@ CacheIndex::UpdateEntryInIndex(CacheInde
         entry->MarkDirty();
         entry->MarkFresh();
       }
     }
 
     return PL_DHASH_REMOVE;
   }
 
-  entry = index->mIndex.PutEntry(*aEntry->Hash());
-  *entry = *aEntry;
+  if (entry) {
+    // Some information in mIndex can be newer than in mPendingUpdates (see bug
+    // 1074832). This will copy just those values that were really updated.
+    aEntry->ApplyUpdate(entry);
+  } else {
+    // There is no entry in mIndex, copy all information from mPendingUpdates
+    // to mIndex.
+    entry = index->mIndex.PutEntry(*aEntry->Hash());
+    *entry = *aEntry;
+  }
 
   return PL_DHASH_REMOVE;
 }
 
 bool
 CacheIndex::WriteIndexToDiskIfNeeded()
 {
   if (mState != READY || mShuttingDown) {
--- a/netwerk/cache2/CacheIndex.h
+++ b/netwerk/cache2/CacheIndex.h
@@ -161,61 +161,61 @@ public:
     if (aAnonymous) {
       mRec->mFlags |= kAnonymousMask;
     }
     if (aInBrowser) {
       mRec->mFlags |= kInBrowserMask;
     }
   }
 
-  const SHA1Sum::Hash * Hash() { return &mRec->mHash; }
+  const SHA1Sum::Hash * Hash() const { return &mRec->mHash; }
 
-  bool IsInitialized() { return !!(mRec->mFlags & kInitializedMask); }
+  bool IsInitialized() const { return !!(mRec->mFlags & kInitializedMask); }
 
-  uint32_t AppId() { return mRec->mAppId; }
-  bool     Anonymous() { return !!(mRec->mFlags & kAnonymousMask); }
-  bool     InBrowser() { return !!(mRec->mFlags & kInBrowserMask); }
+  uint32_t AppId() const { return mRec->mAppId; }
+  bool     Anonymous() const { return !!(mRec->mFlags & kAnonymousMask); }
+  bool     InBrowser() const { return !!(mRec->mFlags & kInBrowserMask); }
 
-  bool IsRemoved() { return !!(mRec->mFlags & kRemovedMask); }
+  bool IsRemoved() const { return !!(mRec->mFlags & kRemovedMask); }
   void MarkRemoved() { mRec->mFlags |= kRemovedMask; }
 
-  bool IsDirty() { return !!(mRec->mFlags & kDirtyMask); }
+  bool IsDirty() const { return !!(mRec->mFlags & kDirtyMask); }
   void MarkDirty() { mRec->mFlags |= kDirtyMask; }
   void ClearDirty() { mRec->mFlags &= ~kDirtyMask; }
 
-  bool IsFresh() { return !!(mRec->mFlags & kFreshMask); }
+  bool IsFresh() const { return !!(mRec->mFlags & kFreshMask); }
   void MarkFresh() { mRec->mFlags |= kFreshMask; }
 
   void     SetFrecency(uint32_t aFrecency) { mRec->mFrecency = aFrecency; }
-  uint32_t GetFrecency() { return mRec->mFrecency; }
+  uint32_t GetFrecency() const { return mRec->mFrecency; }
 
   void     SetExpirationTime(uint32_t aExpirationTime)
   {
     mRec->mExpirationTime = aExpirationTime;
   }
-  uint32_t GetExpirationTime() { return mRec->mExpirationTime; }
+  uint32_t GetExpirationTime() const { return mRec->mExpirationTime; }
 
   // Sets filesize in kilobytes.
   void     SetFileSize(uint32_t aFileSize)
   {
     if (aFileSize > kFileSizeMask) {
       LOG(("CacheIndexEntry::SetFileSize() - FileSize is too large, "
            "truncating to %u", kFileSizeMask));
       aFileSize = kFileSizeMask;
     }
     mRec->mFlags &= ~kFileSizeMask;
     mRec->mFlags |= aFileSize;
   }
   // Returns filesize in kilobytes.
-  uint32_t GetFileSize() { return GetFileSize(mRec); }
+  uint32_t GetFileSize() const { return GetFileSize(mRec); }
   static uint32_t GetFileSize(CacheIndexRecord *aRec)
   {
     return aRec->mFlags & kFileSizeMask;
   }
-  bool     IsFileEmpty() { return GetFileSize() == 0; }
+  bool     IsFileEmpty() const { return GetFileSize() == 0; }
 
   void WriteToBuf(void *aBuf)
   {
     CacheIndexRecord *dst = reinterpret_cast<CacheIndexRecord *>(aBuf);
 
     // Copy the whole record to the buffer.
     memcpy(aBuf, mRec, sizeof(CacheIndexRecord));
 
@@ -241,17 +241,17 @@ public:
                sizeof(SHA1Sum::Hash)) == 0);
 
     mRec->mFrecency = NetworkEndian::readUint32(&src->mFrecency);
     mRec->mExpirationTime = NetworkEndian::readUint32(&src->mExpirationTime);
     mRec->mAppId = NetworkEndian::readUint32(&src->mAppId);
     mRec->mFlags = NetworkEndian::readUint32(&src->mFlags);
   }
 
-  void Log() {
+  void Log() const {
     LOG(("CacheIndexEntry::Log() [this=%p, hash=%08x%08x%08x%08x%08x, fresh=%u,"
          " initialized=%u, removed=%u, dirty=%u, anonymous=%u, inBrowser=%u, "
          "appId=%u, frecency=%u, expirationTime=%u, size=%u]",
          this, LOGSHA1(mRec->mHash), IsFresh(), IsInitialized(), IsRemoved(),
          IsDirty(), Anonymous(), InBrowser(), AppId(), GetFrecency(),
          GetExpirationTime(), GetFileSize()));
   }
 
@@ -275,16 +275,17 @@ public:
   }
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   {
     return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
   }
 
 private:
+  friend class CacheIndexEntryUpdate;
   friend class CacheIndex;
   friend class CacheIndexEntryAutoManage;
 
   static const uint32_t kInitializedMask = 0x80000000;
   static const uint32_t kAnonymousMask   = 0x40000000;
   static const uint32_t kInBrowserMask   = 0x20000000;
 
   // This flag is set when the entry was removed. We need to keep this
@@ -303,16 +304,93 @@ private:
   static const uint32_t kReservedMask    = 0x03000000;
 
   // FileSize in kilobytes
   static const uint32_t kFileSizeMask    = 0x00FFFFFF;
 
   nsAutoPtr<CacheIndexRecord> mRec;
 };
 
+class CacheIndexEntryUpdate : public CacheIndexEntry
+{
+public:
+  explicit CacheIndexEntryUpdate(CacheIndexEntry::KeyTypePointer aKey)
+    : CacheIndexEntry(aKey)
+    , mUpdateFlags(0)
+  {
+    MOZ_COUNT_CTOR(CacheIndexEntryUpdate);
+    LOG(("CacheIndexEntryUpdate::CacheIndexEntryUpdate()"));
+  }
+  ~CacheIndexEntryUpdate()
+  {
+    MOZ_COUNT_DTOR(CacheIndexEntryUpdate);
+    LOG(("CacheIndexEntryUpdate::~CacheIndexEntryUpdate()"));
+  }
+
+  CacheIndexEntryUpdate& operator=(const CacheIndexEntry& aOther)
+  {
+    MOZ_ASSERT(memcmp(&mRec->mHash, &aOther.mRec->mHash,
+               sizeof(SHA1Sum::Hash)) == 0);
+    mUpdateFlags = 0;
+    *(static_cast<CacheIndexEntry *>(this)) = aOther;
+    return *this;
+  }
+
+  void InitNew()
+  {
+    mUpdateFlags = kFrecencyUpdatedMask | kExpirationUpdatedMask |
+                   kFileSizeUpdatedMask;
+    CacheIndexEntry::InitNew();
+  }
+
+  void SetFrecency(uint32_t aFrecency)
+  {
+    mUpdateFlags |= kFrecencyUpdatedMask;
+    CacheIndexEntry::SetFrecency(aFrecency);
+  }
+
+  void SetExpirationTime(uint32_t aExpirationTime)
+  {
+    mUpdateFlags |= kExpirationUpdatedMask;
+    CacheIndexEntry::SetExpirationTime(aExpirationTime);
+  }
+
+  void SetFileSize(uint32_t aFileSize)
+  {
+    mUpdateFlags |= kFileSizeUpdatedMask;
+    CacheIndexEntry::SetFileSize(aFileSize);
+  }
+
+  void ApplyUpdate(CacheIndexEntry *aDst) {
+    MOZ_ASSERT(memcmp(&mRec->mHash, &aDst->mRec->mHash,
+               sizeof(SHA1Sum::Hash)) == 0);
+    if (mUpdateFlags & kFrecencyUpdatedMask) {
+      aDst->mRec->mFrecency = mRec->mFrecency;
+    }
+    if (mUpdateFlags & kExpirationUpdatedMask) {
+      aDst->mRec->mExpirationTime = mRec->mExpirationTime;
+    }
+    aDst->mRec->mAppId = mRec->mAppId;
+    if (mUpdateFlags & kFileSizeUpdatedMask) {
+      aDst->mRec->mFlags = mRec->mFlags;
+    } else {
+      // Copy all flags except file size.
+      aDst->mRec->mFlags &= kFileSizeMask;
+      aDst->mRec->mFlags |= (mRec->mFlags & ~kFileSizeMask);
+    }
+  }
+
+private:
+  static const uint32_t kFrecencyUpdatedMask = 0x00000001;
+  static const uint32_t kExpirationUpdatedMask = 0x00000002;
+  static const uint32_t kFileSizeUpdatedMask = 0x00000004;
+
+  uint32_t mUpdateFlags;
+};
+
 class CacheIndexStats
 {
 public:
   CacheIndexStats()
     : mCount(0)
     , mNotInitialized(0)
     , mRemoved(0)
     , mDirty(0)
@@ -392,17 +470,17 @@ public:
     return mCount - mRemoved - mNotInitialized - mEmpty;
   }
 
   uint32_t Size() {
     MOZ_ASSERT(!mStateLogged, "CacheIndexStats::Size() - state logged!");
     return mSize;
   }
 
-  void BeforeChange(CacheIndexEntry *aEntry) {
+  void BeforeChange(const CacheIndexEntry *aEntry) {
 #ifdef DEBUG_STATS
     if (!mDisableLogging) {
       LOG(("CacheIndexStats::BeforeChange()"));
       Log();
     }
 #endif
 
     MOZ_ASSERT(!mStateLogged, "CacheIndexStats::BeforeChange() - state "
@@ -436,17 +514,17 @@ public:
             MOZ_ASSERT(mSize >= aEntry->GetFileSize());
             mSize -= aEntry->GetFileSize();
           }
         }
       }
     }
   }
 
-  void AfterChange(CacheIndexEntry *aEntry) {
+  void AfterChange(const CacheIndexEntry *aEntry) {
     MOZ_ASSERT(mStateLogged, "CacheIndexStats::AfterChange() - state not "
                "logged!");
 #ifdef DEBUG
     mStateLogged = false;
 #endif
     if (aEntry) {
       ++mCount;
       if (aEntry->IsDirty()) {
@@ -637,17 +715,17 @@ private:
   // Checks whether any of the information about the entry has changed.
   static bool HasEntryChanged(CacheIndexEntry *aEntry,
                               const uint32_t  *aFrecency,
                               const uint32_t  *aExpirationTime,
                               const uint32_t  *aSize);
 
   // Merge all pending operations from mPendingUpdates into mIndex.
   void ProcessPendingOperations();
-  static PLDHashOperator UpdateEntryInIndex(CacheIndexEntry *aEntry,
+  static PLDHashOperator UpdateEntryInIndex(CacheIndexEntryUpdate *aEntry,
                                             void* aClosure);
 
   // Following methods perform writing of the index file.
   //
   // The index is written periodically, but not earlier than once in
   // kMinDumpInterval and there must be at least kMinUnwrittenChanges
   // differences between index on disk and in memory. Index is always first
   // written to a temporary file and the old index file is replaced when the
@@ -924,17 +1002,17 @@ private:
   // Directory enumerator used when building and updating index.
   nsCOMPtr<nsIDirectoryEnumerator> mDirEnumerator;
 
   // Main index hashtable.
   nsTHashtable<CacheIndexEntry> mIndex;
 
   // We cannot add, remove or change any entry in mIndex in states READING and
   // WRITING. We track all changes in mPendingUpdates during these states.
-  nsTHashtable<CacheIndexEntry> mPendingUpdates;
+  nsTHashtable<CacheIndexEntryUpdate> mPendingUpdates;
 
   // Contains information statistics for mIndex + mPendingUpdates.
   CacheIndexStats               mIndexStats;
 
   // When reading journal, we must first parse the whole file and apply the
   // changes iff the journal was read successfully. mTmpJournal is used to store
   // entries from the journal file. We throw away all these entries if parsing
   // of the journal fails or the hash does not match.
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -1031,26 +1031,16 @@ WebSocketChannel::WebSocketChannel() :
   mFramePtr = mBuffer = static_cast<uint8_t *>(moz_xmalloc(mBufferSize));
 
   nsresult rv;
   mConnectionLogService = do_GetService("@mozilla.org/network/dashboard;1",&rv);
   if (NS_FAILED(rv))
     LOG(("Failed to initiate dashboard service."));
 
   mSerial = sSerialSeed++;
-
-  // Register for prefs change notifications
-  nsCOMPtr<nsIObserverService> observerService =
-    mozilla::services::GetObserverService();
-  if (observerService) {
-    observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
-  } else {
-    NS_WARNING("failed to get observer service");
-  }
-
 }
 
 WebSocketChannel::~WebSocketChannel()
 {
   LOG(("WebSocketChannel::~WebSocketChannel() %p\n", this));
 
   if (mWasOpened) {
     MOZ_ASSERT(mCalledOnStop, "WebSocket was opened but OnStop was not called");
@@ -1967,16 +1957,43 @@ WebSocketChannel::EnsureHdrOut(uint32_t 
     mDynamicOutputSize = size;
     mDynamicOutput =
       (uint8_t *) moz_xrealloc(mDynamicOutput, mDynamicOutputSize);
   }
 
   mHdrOut = mDynamicOutput;
 }
 
+namespace {
+
+class RemoveObserverRunnable : public nsRunnable
+{
+  nsRefPtr<WebSocketChannel> mChannel;
+
+public:
+  RemoveObserverRunnable(WebSocketChannel* aChannel)
+    : mChannel(aChannel)
+  {}
+
+  NS_IMETHOD Run()
+  {
+    nsCOMPtr<nsIObserverService> observerService =
+      mozilla::services::GetObserverService();
+    if (!observerService) {
+      NS_WARNING("failed to get observer service");
+      return NS_OK;
+    }
+
+    observerService->RemoveObserver(mChannel, NS_NETWORK_LINK_TOPIC);
+    return NS_OK;
+  }
+};
+
+} // anonymous namespace
+
 void
 WebSocketChannel::CleanupConnection()
 {
   LOG(("WebSocketChannel::CleanupConnection() %p", this));
 
   if (mLingeringCloseTimer) {
     mLingeringCloseTimer->Cancel();
     mLingeringCloseTimer = nullptr;
@@ -1998,16 +2015,20 @@ WebSocketChannel::CleanupConnection()
     mTransport->Close(NS_BASE_STREAM_CLOSED);
     mTransport = nullptr;
   }
 
   if (mConnectionLogService && !mPrivateBrowsing) {
     mConnectionLogService->RemoveHost(mHost, mSerial);
   }
 
+  // This method can run in any thread, but the observer has to be removed on
+  // the main-thread.
+  NS_DispatchToMainThread(new RemoveObserverRunnable(this));
+
   DecrementSessionCount();
 }
 
 void
 WebSocketChannel::StopSession(nsresult reason)
 {
   LOG(("WebSocketChannel::StopSession() %p [%x]\n", this, reason));
 
@@ -2105,18 +2126,16 @@ WebSocketChannel::StopSession(nsresult r
   delete mCompressor;
   mCompressor = nullptr;
 
   if (!mCalledOnStop) {
     mCalledOnStop = 1;
     mTargetThread->Dispatch(new CallOnStop(this, reason),
                             NS_DISPATCH_NORMAL);
   }
-
-  return;
 }
 
 void
 WebSocketChannel::AbortSession(nsresult reason)
 {
   LOG(("WebSocketChannel::AbortSession() %p [reason %x] stopped = %d\n",
        this, reason, mStopped));
 
@@ -2914,16 +2933,29 @@ WebSocketChannel::AsyncOpen(nsIURI *aURI
     mConnectionLogService->AddHost(mHost, mSerial,
                                    BaseWebSocketChannel::mEncrypted);
   }
 
   rv = ApplyForAdmission();
   if (NS_FAILED(rv))
     return rv;
 
+  // Register for prefs change notifications
+  nsCOMPtr<nsIObserverService> observerService =
+    mozilla::services::GetObserverService();
+  if (!observerService) {
+    NS_WARNING("failed to get observer service");
+    return NS_ERROR_FAILURE;
+  }
+
+  rv = observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   // Only set these if the open was successful:
   //
   mWasOpened = 1;
   mListener = aListener;
   mContext = aContext;
   IncrementSessionCount();
 
   return rv;
@@ -3223,18 +3255,18 @@ WebSocketChannel::OnStartRequest(nsIRequ
   if (mRecvdHttpUpgradeTransport)
     return StartWebsocketData();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketChannel::OnStopRequest(nsIRequest *aRequest,
-                                  nsISupports *aContext,
-                                  nsresult aStatusCode)
+                                nsISupports *aContext,
+                                nsresult aStatusCode)
 {
   LOG(("WebSocketChannel::OnStopRequest() %p [%p %p %x]\n",
        this, aRequest, aContext, aStatusCode));
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread");
 
   ReportConnectionTelemetry();
 
   // This is the end of the HTTP upgrade transaction, the
--- a/netwerk/sctp/datachannel/DataChannel.cpp
+++ b/netwerk/sctp/datachannel/DataChannel.cpp
@@ -909,41 +909,49 @@ DataChannelConnection::FindFreeStream()
 bool
 DataChannelConnection::RequestMoreStreams(int32_t aNeeded)
 {
   struct sctp_status status;
   struct sctp_add_streams sas;
   uint32_t outStreamsNeeded;
   socklen_t len;
 
-  if (aNeeded + mStreams.Length() > MAX_NUM_STREAMS)
+  if (aNeeded + mStreams.Length() > MAX_NUM_STREAMS) {
     aNeeded = MAX_NUM_STREAMS - mStreams.Length();
-  if (aNeeded <= 0)
+  }
+  if (aNeeded <= 0) {
     return false;
+  }
 
   len = (socklen_t)sizeof(struct sctp_status);
   if (usrsctp_getsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_STATUS, &status, &len) < 0) {
     LOG(("***failed: getsockopt SCTP_STATUS"));
     return false;
   }
   outStreamsNeeded = aNeeded; // number to add
 
-  memset(&sas, 0, sizeof(struct sctp_add_streams));
+  // Note: if multiple channel opens happen when we don't have enough space,
+  // we'll call RequestMoreStreams() multiple times
+  memset(&sas, 0, sizeof(sas));
   sas.sas_instrms = 0;
   sas.sas_outstrms = (uint16_t)outStreamsNeeded; /* XXX error handling */
   // Doesn't block, we get an event when it succeeds or fails
   if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_ADD_STREAMS, &sas,
                          (socklen_t) sizeof(struct sctp_add_streams)) < 0) {
-    if (errno == EALREADY)
+    if (errno == EALREADY) {
+      LOG(("Already have %u output streams", outStreamsNeeded));
       return true;
+    }
 
     LOG(("***failed: setsockopt ADD errno=%d", errno));
     return false;
   }
   LOG(("Requested %u more streams", outStreamsNeeded));
+  // We add to mStreams when we get a SCTP_STREAM_CHANGE_EVENT and the
+  // values are larger than mStreams.Length()
   return true;
 }
 
 int32_t
 DataChannelConnection::SendControlMessage(void *msg, uint32_t len, uint16_t stream)
 {
   struct sctp_sndinfo sndinfo;
 
@@ -1049,16 +1057,23 @@ DataChannelConnection::SendDeferredMessa
 
     // Only one of these should be set....
     if (channel->mFlags & DATA_CHANNEL_FLAGS_SEND_REQ) {
       if (SendOpenRequestMessage(channel->mLabel, channel->mProtocol,
                                  channel->mStream,
                                  channel->mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED,
                                  channel->mPrPolicy, channel->mPrValue)) {
         channel->mFlags &= ~DATA_CHANNEL_FLAGS_SEND_REQ;
+
+        channel->mState = OPEN;
+        channel->mReady = true;
+        LOG(("%s: sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel.get()));
+        NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
+                                  DataChannelOnMessageAvailable::ON_CHANNEL_OPEN, this,
+                                  channel));
         sent = true;
       } else {
         if (errno == EAGAIN || errno == EWOULDBLOCK) {
           still_blocked = true;
         } else {
           // Close the channel, inform the user
           mStreams[channel->mStream] = nullptr;
           channel->mState = CLOSED;
@@ -1176,16 +1191,17 @@ DataChannelConnection::HandleOpenRequest
     case DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT_UNORDERED:
       prPolicy = SCTP_PR_SCTP_RTX;
       break;
     case DATA_CHANNEL_PARTIAL_RELIABLE_TIMED:
     case DATA_CHANNEL_PARTIAL_RELIABLE_TIMED_UNORDERED:
       prPolicy = SCTP_PR_SCTP_TTL;
       break;
     default:
+      LOG(("Unknown channel type", req->channel_type));
       /* XXX error handling */
       return;
   }
   prValue = ntohl(req->reliability_param);
   flags = (req->channel_type & 0x80) ? DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED : 0;
 
   if ((channel = FindChannelByStream(stream))) {
     if (!(channel->mFlags & DATA_CHANNEL_FLAGS_EXTERNAL_NEGOTIATED)) {
@@ -1202,16 +1218,20 @@ DataChannelConnection::HandleOpenRequest
         LOG(("WARNING: external negotiation mismatch with OpenRequest:"
              "channel %u, policy %u/%u, value %u/%u, flags %x/%x",
              stream, prPolicy, channel->mPrPolicy,
              prValue, channel->mPrValue, flags, channel->mFlags));
       }
     }
     return;
   }
+  if (stream >= mStreams.Length()) {
+    LOG(("%s: stream %u out of bounds (%u)", __FUNCTION__, stream, mStreams.Length()));
+    return;
+  }
 
   nsCString label(nsDependentCSubstring(&req->label[0], ntohs(req->label_length)));
   nsCString protocol(nsDependentCSubstring(&req->label[ntohs(req->label_length)],
                                            ntohs(req->protocol_length)));
 
   channel = new DataChannel(this,
                             stream,
                             DataChannel::CONNECTING,
@@ -1219,18 +1239,18 @@ DataChannelConnection::HandleOpenRequest
                             protocol,
                             prPolicy, prValue,
                             flags,
                             nullptr, nullptr);
   mStreams[stream] = channel;
 
   channel->mState = DataChannel::WAITING_TO_OPEN;
 
-  LOG(("%s: sending ON_CHANNEL_CREATED for %s/%s: %u", __FUNCTION__,
-       channel->mLabel.get(), channel->mProtocol.get(), stream));
+  LOG(("%s: sending ON_CHANNEL_CREATED for %s/%s: %u (state %u)", __FUNCTION__,
+       channel->mLabel.get(), channel->mProtocol.get(), stream, channel->mState));
   NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
                             DataChannelOnMessageAvailable::ON_CHANNEL_CREATED,
                             this, channel));
 
   LOG(("%s: deferring sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel.get()));
 
   if (!SendOpenAckMessage(stream)) {
     // XXX Only on EAGAIN!?  And if not, then close the channel??
@@ -1728,23 +1748,24 @@ DataChannelConnection::HandleStreamReset
           // The other side closed the channel
           // We could be in three states:
           // 1. Normal state (input and output streams (OPEN)
           //    Notify application, send a RESET in response on our
           //    outbound channel.  Go to CLOSED
           // 2. We sent our own reset (CLOSING); either they crossed on the
           //    wire, or this is a response to our Reset.
           //    Go to CLOSED
-          // 3. We've sent a open but haven't gotten a response yet (OPENING)
+          // 3. We've sent a open but haven't gotten a response yet (CONNECTING)
           //    I believe this is impossible, as we don't have an input stream yet.
 
           LOG(("Incoming: Channel %u  closed, state %d",
                channel->mStream, channel->mState));
           ASSERT_WEBRTC(channel->mState == DataChannel::OPEN ||
                         channel->mState == DataChannel::CLOSING ||
+                        channel->mState == DataChannel::CONNECTING ||
                         channel->mState == DataChannel::WAITING_TO_OPEN);
           if (channel->mState == DataChannel::OPEN ||
               channel->mState == DataChannel::WAITING_TO_OPEN) {
             ResetOutgoingStream(channel->mStream);
             SendOutgoingStreamReset();
             NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
                                       DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
                                       channel));
@@ -1780,47 +1801,52 @@ DataChannelConnection::HandleStreamChang
     LOG(("*** Failed increasing number of streams from %u (%u/%u)",
          mStreams.Length(),
          strchg->strchange_instrms,
          strchg->strchange_outstrms));
     // XXX FIX! notify pending opens of failure
     return;
   } else {
     if (strchg->strchange_instrms > mStreams.Length()) {
-      LOG(("Other side increased streamds from %u to %u",
+      LOG(("Other side increased streams from %u to %u",
            mStreams.Length(), strchg->strchange_instrms));
     }
-    if (strchg->strchange_outstrms > mStreams.Length()) {
+    if (strchg->strchange_outstrms > mStreams.Length() ||
+        strchg->strchange_instrms > mStreams.Length()) {
       uint16_t old_len = mStreams.Length();
+      uint16_t new_len = std::max(strchg->strchange_outstrms,
+                                  strchg->strchange_instrms);
       LOG(("Increasing number of streams from %u to %u - adding %u (in: %u)",
-           old_len,
-           strchg->strchange_outstrms,
-           strchg->strchange_outstrms - old_len,
+           old_len, new_len, new_len - old_len,
            strchg->strchange_instrms));
       // make sure both are the same length
-      mStreams.AppendElements(strchg->strchange_outstrms - old_len);
+      mStreams.AppendElements(new_len - old_len);
       LOG(("New length = %d (was %d)", mStreams.Length(), old_len));
-      for (uint32_t i = old_len; i < mStreams.Length(); ++i) {
+      for (size_t i = old_len; i < mStreams.Length(); ++i) {
         mStreams[i] = nullptr;
       }
       // Re-process any channels waiting for streams.
       // Linear search, but we don't increase channels often and
       // the array would only get long in case of an app error normally
 
       // Make sure we request enough streams if there's a big jump in streams
       // Could make a more complex API for OpenXxxFinish() and avoid this loop
       int32_t num_needed = mPending.GetSize();
       LOG(("%d of %d new streams already needed", num_needed,
-           strchg->strchange_outstrms - old_len));
-      num_needed -= (strchg->strchange_outstrms - old_len); // number we added
+           new_len - old_len));
+      num_needed -= (new_len - old_len); // number we added
       if (num_needed > 0) {
         if (num_needed < 16)
           num_needed = 16;
         LOG(("Not enough new streams, asking for %d more", num_needed));
         RequestMoreStreams(num_needed);
+      } else if (strchg->strchange_outstrms < strchg->strchange_instrms) {
+        LOG(("Requesting %d output streams to match partner",
+             strchg->strchange_instrms - strchg->strchange_outstrms));
+        RequestMoreStreams(strchg->strchange_instrms - strchg->strchange_outstrms);
       }
 
       ProcessQueuedOpens();
     }
     // else probably not a change in # of streams
   }
 
   for (i = 0; i < mStreams.Length(); ++i) {
--- a/netwerk/sctp/datachannel/DataChannelProtocol.h
+++ b/netwerk/sctp/datachannel/DataChannelProtocol.h
@@ -12,17 +12,17 @@
 #elif defined(_MSC_VER)
 #pragma pack (push, 1)
 #define SCTP_PACKED
 #else
 #error "Unsupported compiler"
 #endif
 
 // Duplicated in fsm.def
-#define WEBRTC_DATACHANNEL_STREAMS_DEFAULT 16
+#define WEBRTC_DATACHANNEL_STREAMS_DEFAULT 256
 
 #define DATA_CHANNEL_PPID_CONTROL        50
 #define DATA_CHANNEL_PPID_BINARY         52
 #define DATA_CHANNEL_PPID_BINARY_LAST    53
 #define DATA_CHANNEL_PPID_DOMSTRING      54
 #define DATA_CHANNEL_PPID_DOMSTRING_LAST 51
 
 #define DATA_CHANNEL_MAX_BINARY_FRAGMENT 0x4000
--- a/testing/marionette/client/marionette/marionette_test.py
+++ b/testing/marionette/client/marionette/marionette_test.py
@@ -377,19 +377,20 @@ permissions.forEach(function (perm) {
         if self.marionette.session is None:
             self.marionette.start_session()
         if self.marionette.timeout is not None:
             self.marionette.timeouts(self.marionette.TIMEOUT_SEARCH, self.marionette.timeout)
             self.marionette.timeouts(self.marionette.TIMEOUT_SCRIPT, self.marionette.timeout)
             self.marionette.timeouts(self.marionette.TIMEOUT_PAGE, self.marionette.timeout)
         else:
             self.marionette.timeouts(self.marionette.TIMEOUT_PAGE, 30000)
+
         if hasattr(self, 'test_container') and self.test_container:
             self.switch_into_test_container()
-        else:
+        elif hasattr(self, 'test_container') and self.test_container is False:
             if self.marionette.session_capabilities.has_key('b2g') \
             and self.marionette.session_capabilities['b2g'] == True:
                 self.close_test_container()
 
     def tearDown(self):
         pass
 
     def cleanTest(self):
@@ -411,66 +412,28 @@ permissions.forEach(function (perm) {
                     self.marionette.session = None
                     try:
                         self.marionette.client.close()
                     except socket.error:
                         pass
         self.marionette = None
 
     def switch_into_test_container(self):
-        self.marionette.set_context("content")
-        frame = None
-        try:
-            frame = self.marionette.find_element(
-                'css selector',
-                'iframe[src*="app://test-container.gaiamobile.org/index.html"]'
-            )
-        except NoSuchElementException:
-            result = self.marionette.execute_async_script("""
-if((navigator.mozSettings == undefined) || (navigator.mozSettings == null) || (navigator.mozApps == undefined) || (navigator.mozApps == null)) {
-    marionetteScriptFinished(false);
-    return;
-}
-let setReq = navigator.mozSettings.createLock().set({'lockscreen.enabled': false});
-setReq.onsuccess = function() {
-    let appsReq = navigator.mozApps.mgmt.getAll();
-    appsReq.onsuccess = function() {
-        let apps = appsReq.result;
-        for (let i = 0; i < apps.length; i++) {
-            let app = apps[i];
-            if (app.manifest.name === 'Test Container') {
-                app.launch();
-                window.addEventListener('apploadtime', function apploadtime(){
-                    window.removeEventListener('apploadtime', apploadtime);
-                    marionetteScriptFinished(true);
-                });
-                return;
-            }
-        }
-        marionetteScriptFinished(false);
-    }
-    appsReq.onerror = function() {
-        marionetteScriptFinished(false);
-    }
-}
-setReq.onerror = function() {
-    marionetteScriptFinished(false);
-}""", script_timeout=60000)
+        self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
 
-            self.assertTrue(result)
-            frame = Wait(self.marionette, timeout=10, interval=0.2).until(element_present(
-                'css selector',
-                'iframe[src*="app://test-container.gaiamobile.org/index.html"]'
-            ))
+        frame = Wait(self.marionette, timeout=10, interval=0.2).until(element_present(
+            'css selector',
+            'iframe[src*="app://test-container.gaiamobile.org/index.html"]'
+        ))
 
         self.marionette.switch_to_frame(frame)
 
     def close_test_container(self):
-        self.marionette.set_context("content")
-        self.marionette.switch_to_frame()
+        self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
+
         result = self.marionette.execute_async_script("""
 if((navigator.mozSettings == undefined) || (navigator.mozSettings == null) || (navigator.mozApps == undefined) || (navigator.mozApps == null)) {
     marionetteScriptFinished(false);
     return;
 }
 let setReq = navigator.mozSettings.createLock().set({'lockscreen.enabled': false});
 setReq.onsuccess = function() {
     let appsReq = navigator.mozApps.mgmt.getAll();
@@ -494,17 +457,20 @@ setReq.onsuccess = function() {
     appsReq.onerror = function() {
         marionetteScriptFinished(false);
     }
 }
 setReq.onerror = function() {
     marionetteScriptFinished(false);
 }""", script_timeout=60000)
 
-        frame = Wait(self.marionette, timeout=10, interval=0.2).until(element_not_present(
+        if not result:
+            raise Exception('Failed to close Test Container app')
+
+        Wait(self.marionette, timeout=10, interval=0.2).until(element_not_present(
             'css selector',
             'iframe[src*="app://test-container.gaiamobile.org/index.html"]'
         ))
 
 
 class MarionetteTestCase(CommonTestCase):
 
     match_re = re.compile(r"test_(.*)\.py$")
@@ -512,17 +478,17 @@ class MarionetteTestCase(CommonTestCase)
     def __init__(self, marionette_weakref, methodName='runTest',
                  filepath='', **kwargs):
         self._marionette_weakref = marionette_weakref
         self.marionette = None
         self.extra_emulator_index = -1
         self.methodName = methodName
         self.filepath = filepath
         self.testvars = kwargs.pop('testvars', None)
-        self.test_container = kwargs.pop('test_container', False)
+        self.test_container = kwargs.pop('test_container', None)
         CommonTestCase.__init__(self, methodName, **kwargs)
 
     @classmethod
     def add_tests_to_suite(cls, mod_name, filepath, suite, testloader, marionette, testvars, **kwargs):
         test_mod = imp.load_source(mod_name, filepath)
 
         for name in dir(test_mod):
             obj = getattr(test_mod, name)
@@ -583,17 +549,17 @@ class MarionetteJSTestCase(CommonTestCas
     inactivity_timeout_re = re.compile(r"MARIONETTE_INACTIVITY_TIMEOUT(\s*)=(\s*)(\d+);")
     match_re = re.compile(r"test_(.*)\.js$")
 
     def __init__(self, marionette_weakref, methodName='runTest', jsFile=None, **kwargs):
         assert(jsFile)
         self.jsFile = jsFile
         self._marionette_weakref = marionette_weakref
         self.marionette = None
-        self.test_container = kwargs.pop('test_container', False)
+        self.test_container = kwargs.pop('test_container', None)
         CommonTestCase.__init__(self, methodName)
 
     @classmethod
     def add_tests_to_suite(cls, mod_name, filepath, suite, testloader, marionette, testvars, **kwargs):
         suite.addTest(cls(weakref.ref(marionette), jsFile=filepath, **kwargs))
 
     def runTest(self):
         if self.marionette.session is None:
--- a/testing/marionette/client/marionette/runner/base.py
+++ b/testing/marionette/client/marionette/runner/base.py
@@ -633,16 +633,63 @@ class BaseMarionetteTestRunner(object):
                 'no_window': self.no_window,
                 'sdcard': self.sdcard,
             })
         return kwargs
 
     def start_marionette(self):
         self.marionette = Marionette(**self._build_kwargs())
 
+    def launch_test_container(self):
+        if self.marionette.session is None:
+            self.marionette.start_session()
+        self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
+
+        result = self.marionette.execute_async_script("""
+if((navigator.mozSettings == undefined) || (navigator.mozSettings == null) || (navigator.mozApps == undefined) || (navigator.mozApps == null)) {
+    marionetteScriptFinished(false);
+    return;
+}
+let setReq = navigator.mozSettings.createLock().set({'lockscreen.enabled': false});
+setReq.onsuccess = function() {
+    let appName = 'Test Container';
+    let activeApp = window.wrappedJSObject.System.currentApp;
+
+    // if the Test Container is already open then do nothing
+    if(activeApp.name === appName){
+        marionetteScriptFinished(true);
+    }
+
+    let appsReq = navigator.mozApps.mgmt.getAll();
+    appsReq.onsuccess = function() {
+        let apps = appsReq.result;
+        for (let i = 0; i < apps.length; i++) {
+            let app = apps[i];
+            if (app.manifest.name === appName) {
+                app.launch();
+                window.addEventListener('appopen', function apploadtime(){
+                    window.removeEventListener('appopen', apploadtime);
+                    marionetteScriptFinished(true);
+                });
+                return;
+            }
+        }
+        marionetteScriptFinished(false);
+    }
+    appsReq.onerror = function() {
+        marionetteScriptFinished(false);
+    }
+}
+setReq.onerror = function() {
+    marionetteScriptFinished(false);
+}""", script_timeout=60000)
+
+        if not result:
+            raise Exception("Could not launch test container app")
+
     def run_tests(self, tests):
         self.reset_test_stats()
         self.start_time = time.time()
 
         need_external_ip = True
         if not self.marionette:
             self.start_marionette()
             if self.emulator:
@@ -719,17 +766,17 @@ class BaseMarionetteTestRunner(object):
 
         for run_tests in self.mixin_run_tests:
             run_tests(tests)
         if self.shuffle:
             self.logger.info("Using seed where seed is:%d" % self.shuffle_seed)
 
         self.logger.suite_end()
 
-    def add_test(self, test, expected='pass', test_container=False):
+    def add_test(self, test, expected='pass', test_container=None):
         filepath = os.path.abspath(test)
 
         if os.path.isdir(filepath):
             for root, dirs, files in os.walk(filepath):
                 for filename in files:
                     if (filename.startswith('test_') and
                         (filename.endswith('.py') or filename.endswith('.js'))):
                         filepath = os.path.join(root, filename)
@@ -773,19 +820,23 @@ class BaseMarionetteTestRunner(object):
                     test.setdefault('disabled', 'filtered by type (%s)' % self.type)
                     self.manifest_skipped_tests.append(test)
 
             for i in target_tests:
                 if not os.path.exists(i["path"]):
                     raise IOError("test file: %s does not exist" % i["path"])
 
                 file_ext = os.path.splitext(os.path.split(i['path'])[-1])[-1]
-                test_container = False
-                if i.get('test_container') and i.get('test_container') == 'true' and testarg_b2g:
-                    test_container = True
+                test_container = None
+                if i.get('test_container') and testarg_b2g:
+                    if i.get('test_container') == "true":
+                        test_container = True
+                    elif i.get('test_container') == "false":
+                        test_container = False
+
                 self.add_test(i["path"], i["expected"], test_container)
             return
 
         self.tests.append({'filepath': filepath, 'expected': expected, 'test_container': test_container})
 
     def run_test(self, filepath, expected, test_container):
 
         testloader = unittest.TestLoader()
@@ -806,16 +857,19 @@ class BaseMarionetteTestRunner(object):
 
         if suite.countTestCases():
             runner = self.textrunnerclass(logger=self.logger,
                                           marionette=self.marionette,
                                           capabilities=self.capabilities,
                                           logcat_stdout=self.logcat_stdout,
                                           result_callbacks=self.result_callbacks)
 
+            if test_container:
+                self.launch_test_container()
+
             results = runner.run(suite)
             self.results.append(results)
 
             self.failed += len(results.failures) + len(results.errors)
             if hasattr(results, 'skipped'):
                 self.skipped += len(results.skipped)
                 self.todo += len(results.skipped)
             self.passed += results.passed
--- a/testing/marionette/client/marionette/selection.py
+++ b/testing/marionette/client/marionette/selection.py
@@ -68,40 +68,48 @@ class SelectionManager(object):
             cmd = '''var len = arguments[0].value.length;
                   arguments[0].setSelectionRange(len, len);'''
         else:
             cmd = '''var sel = window.getSelection();
                   sel.collapse(arguments[0].lastChild, arguments[0].lastChild.length);'''
 
         self.element.marionette.execute_script(cmd, script_args=[self.element])
 
-    def selection_rect_list(self):
-        '''Return the selection's DOMRectList object.
+    def selection_rect_list(self, idx):
+        '''Return the selection's DOMRectList object for the range at given idx.
 
-        If the element is either <input> or <textarea>, return the selection's
-        DOMRectList within the element. Otherwise, return the DOMRectList of the
-        current selection.
+        If the element is either <input> or <textarea>, return the DOMRectList of
+        the range at given idx of the selection within the element. Otherwise,
+        return the DOMRectList of the of the range at given idx of current selection.
 
         '''
         cmd = self.js_selection_cmd() +\
-            '''return sel.getRangeAt(0).getClientRects();'''
+            '''return sel.getRangeAt(%d).getClientRects();''' % idx
+        return self.element.marionette.execute_script(cmd, script_args=[self.element])
+
+    def range_count(self):
+        '''Get selection's range count'''
+        cmd = self.js_selection_cmd() +\
+            '''return sel.rangeCount;'''
         return self.element.marionette.execute_script(cmd, script_args=[self.element])
 
     def _selection_location_helper(self, location_type):
         '''Return the start and end location of the selection in the element.
 
         Return a tuple containing two pairs of (x, y) coordinates of the start
         and end locations in the element. The coordinates are relative to the
         top left-hand corner of the element. Both ltr and rtl directions are
         considered.
 
         '''
-        rect_list = self.selection_rect_list()
-        list_length = rect_list['length']
-        first_rect, last_rect = rect_list['0'], rect_list[str(list_length - 1)]
+        range_count = self.range_count();
+        first_rect_list = self.selection_rect_list(0)
+        last_rect_list = self.selection_rect_list(range_count - 1)
+        last_list_length = last_rect_list['length']
+        first_rect, last_rect = first_rect_list['0'], last_rect_list[str(last_list_length - 1)]
         origin_x, origin_y = self.element.location['x'], self.element.location['y']
 
         if self.element.get_attribute('dir') == 'rtl':  # such as Arabic
             start_pos, end_pos = 'right', 'left'
         else:
             start_pos, end_pos = 'left', 'right'
 
         # Calculate y offset according to different needs.
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/www/test_selectioncarets_multiplerange.html
@@ -0,0 +1,19 @@
+<html>
+<style>
+h4 {
+  -moz-user-select: none;
+}
+</style>
+<body id=bd>
+<h3 id=sel1>user can select this 1</h3>
+<h3 id=sel2>user can select this 2</h3>
+<h3 id=sel3>user can select this 3</h3>
+<h4 id=nonsel1>user cannot select this 1</h4>
+<h4 id=nonsel2>user cannot select this 2</h4>
+<h3 id=sel4>user can select this 4</h3>
+<h3 id=sel5>user can select this 5</h3>
+<h4 id=nonsel3>user cannot select this 3</h4>
+<h3 id=sel6>user can select this 6</h3>
+<h3 id=sel7>user can select this 7</h3>
+</body>
+</html>
--- a/testing/mozbase/mozdebug/mozdebug/mozdebug.py
+++ b/testing/mozbase/mozdebug/mozdebug/mozdebug.py
@@ -79,19 +79,19 @@ def get_debugger_info(debugger, debugger
     Get the information about the requested debugger.
 
     Returns a dictionary containing the |path| of the debugger executable,
     if it will run in |interactive| mode, its arguments and whether it needs
     to escape arguments it passes to the debugged program (|requiresEscapedArgs|).
     If the debugger cannot be found in the system, returns |None|.
 
     :param debugger: The name of the debugger.
-    :param debuggerArgs: If specified, it's the list of arguments to pass to the
-     debugger. A debugger specific separator arguments is appended at the end of
-     that list.
+    :param debuggerArgs: If specified, it's the arguments to pass to the debugger,
+    as a string. Any debugger-specific separator arguments are appended after these
+    arguments.
     :param debuggerInteractive: If specified, forces the debugger to be interactive.
     '''
 
     debuggerPath = None
 
     if debugger:
         # Append '.exe' to the debugger on Windows if it's not present,
         # so things like '--debugger=devenv' work.
@@ -113,23 +113,24 @@ def get_debugger_info(debugger, debugger
         return default
 
     # Define a namedtuple to access the debugger information from the outside world.
     DebuggerInfo = namedtuple(
         'DebuggerInfo',
         ['path', 'interactive', 'args', 'requiresEscapedArgs']
     )
 
-    debugger_arguments = get_debugger_info('args', [])
+    debugger_arguments = []
 
-    # Extend the default arguments for the chosen debugger with the ones passed in, if any.
     if debuggerArgs:
         # Append the provided debugger arguments at the end of the arguments list.
         debugger_arguments += debuggerArgs.split()
 
+    debugger_arguments += get_debugger_info('args', [])
+
     # Override the default debugger interactive mode if needed.
     debugger_interactive = get_debugger_info('interactive', False)
     if debuggerInteractive:
         debugger_interactive = debuggerInteractive
 
     d = DebuggerInfo(
         debuggerPath,
         debugger_interactive,
--- a/testing/mozbase/mozrunner/mozrunner/base/runner.py
+++ b/testing/mozbase/mozrunner/mozrunner/base/runner.py
@@ -22,17 +22,17 @@ class BaseRunner(object):
     """
     __metaclass__ = ABCMeta
     last_test = 'mozrunner-startup'
     process_handler = None
     timeout = None
     output_timeout = None
 
     def __init__(self, app_ctx=None, profile=None, clean_profile=True, env=None,
-                 process_class=None, process_args=None, symbols_path=None):
+                 process_class=None, process_args=None, symbols_path=None, dump_save_path=None):
         self.app_ctx = app_ctx or DefaultContext()
 
         if isinstance(profile, basestring):
             self.profile = self.app_ctx.profile_class(profile=profile)
         else:
             self.profile = profile or self.app_ctx.profile_class(**getattr(self.app_ctx, 'profile_args', {}))
 
         # process environment
@@ -40,16 +40,17 @@ class BaseRunner(object):
             self.env = os.environ.copy()
         else:
             self.env = env.copy()
 
         self.clean_profile = clean_profile
         self.process_class = process_class or ProcessHandler
         self.process_args = process_args or {}
         self.symbols_path = symbols_path
+        self.dump_save_path = dump_save_path
 
         self.crashed = 0
 
     def __del__(self):
         self.cleanup()
 
     @abstractproperty
     def command(self):
@@ -176,16 +177,19 @@ class BaseRunner(object):
         :param dump_save_path: Directory to save the minidump files to
         :param test_name: Name to use in the crash output
         :param quiet: If `True` don't print the PROCESS-CRASH message to stdout
         :returns: True if a crash was detected, otherwise False
         """
         if not dump_directory:
             dump_directory = os.path.join(self.profile.profile, 'minidumps')
 
+        if not dump_save_path:
+            dump_save_path = self.dump_save_path
+
         try:
             logger = get_default_logger()
             if logger is not None:
                 if test_name is None:
                     test_name = "runner.py"
                 self.crashed += mozcrash.log_crashes(
                     logger,
                     dump_directory,
--- a/testing/mozbase/mozrunner/setup.py
+++ b/testing/mozbase/mozrunner/setup.py
@@ -1,17 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import sys
 from setuptools import setup, find_packages
 
 PACKAGE_NAME = 'mozrunner'
-PACKAGE_VERSION = '6.4'
+PACKAGE_VERSION = '6.5'
 
 desc = """Reliable start/stop/configuration of Mozilla Applications (Firefox, Thunderbird, etc.)"""
 
 deps = ['mozcrash >= 0.14',
         'mozdevice >= 0.37',
         'mozfile >= 1.0',
         'mozinfo >= 0.7',
         'mozlog >= 1.5',
--- a/testing/talos/talos.json
+++ b/testing/talos/talos.json
@@ -1,16 +1,16 @@
 {
     "talos.zip": {
-        "url": "http://talos-bundles.pvt.build.mozilla.org/zips/talos.9dfad0333c76.zip",
+        "url": "http://talos-bundles.pvt.build.mozilla.org/zips/talos.e4a77270a43a.zip",
         "path": ""
     },
     "global": {
         "talos_repo": "https://hg.mozilla.org/build/talos",
-        "talos_revision": "9dfad0333c76"
+        "talos_revision": "e4a77270a43a"
     },
     "extra_options": {
         "android": [ "--apkPath=%(apk_path)s" ]
     },
     "suites": {
         "chromez": {
             "tests": ["tresize", "tcanvasmark"]
         },
--- a/testing/testsuite-targets.mk
+++ b/testing/testsuite-targets.mk
@@ -306,18 +306,16 @@ xpcshell-tests:
 	  -I$(DEPTH)/_tests/mozbase/mozinfo \
 	  $(topsrcdir)/testing/xpcshell/runxpcshelltests.py \
 	  --manifest=$(DEPTH)/_tests/xpcshell/xpcshell.ini \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
 	  --no-logfiles \
 	  --test-plugin-path='$(DIST)/plugins' \
 	  --tests-root-dir=$(abspath _tests/xpcshell) \
 	  --testing-modules-dir=$(abspath _tests/modules) \
-	  --xunit-file=$(abspath _tests/xpcshell/results.xml) \
-	  --xunit-suite-name=xpcshell \
           $(SYMBOLS_PATH) \
 	  $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS) \
 	  $(xpcshell_path)
 
 B2G_XPCSHELL = \
 	rm -f ./@.log && \
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
 	  -I$(DEPTH)/build \
--- a/testing/xpcshell/runxpcshelltests.py
+++ b/testing/xpcshell/runxpcshelltests.py
@@ -1069,21 +1069,16 @@ class XPCShellTests(object):
                 testingModulesDir = os.path.abspath(testingModulesDir)
 
             if not testingModulesDir.endswith(os.path.sep):
                 testingModulesDir += os.path.sep
 
         self.debuggerInfo = None
 
         if debugger:
-            # We need a list of arguments, not a string, to feed into
-            # the debugger
-            if debuggerArgs:
-                debuggerArgs = debuggerArgs.split();
-
             self.debuggerInfo = mozdebug.get_debugger_info(debugger, debuggerArgs, debuggerInteractive)
 
         self.xpcshell = xpcshell
         self.xrePath = xrePath
         self.appPath = appPath
         self.symbolsPath = symbolsPath
         self.manifest = manifest
         self.testdirs = testdirs
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -1689,18 +1689,20 @@ static bool DoFindInReadable(const nsACS
 
   return FindInReadable(value, start, end);
 }
 
 static PLDHashOperator EnumerateEntries(const nsACString& key,
                                         nsCString entry,
                                         void* userData)
 {
-  crashReporterAPIData->Append(key + NS_LITERAL_CSTRING("=") + entry +
-                               NS_LITERAL_CSTRING("\n"));
+  if (!entry.IsEmpty()) {
+    crashReporterAPIData->Append(key + NS_LITERAL_CSTRING("=") + entry +
+                                 NS_LITERAL_CSTRING("\n"));
+  }
   return PL_DHASH_NEXT;
 }
 
 // This function is miscompiled with MSVC 2005/2008 when PGO is on.
 #ifdef _MSC_VER
 #pragma optimize("", off)
 #endif
 static nsresult
@@ -1792,16 +1794,21 @@ nsresult AnnotateCrashReport(const nsACS
   // now rebuild the file contents
   crashReporterAPIData->Truncate(0);
   crashReporterAPIData_Hash->EnumerateRead(EnumerateEntries,
                                            crashReporterAPIData);
 
   return NS_OK;
 }
 
+nsresult RemoveCrashReportAnnotation(const nsACString& key)
+{
+  return AnnotateCrashReport(key, NS_LITERAL_CSTRING(""));
+}
+
 nsresult SetGarbageCollecting(bool collecting)
 {
   if (!GetEnabled())
     return NS_ERROR_NOT_INITIALIZED;
 
   isGarbageCollecting = collecting;
 
   return NS_OK;
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -59,20 +59,21 @@ bool     GetCrashEventsDir(nsAString& aP
 
 bool     GetEnabled();
 bool     GetServerURL(nsACString& aServerURL);
 nsresult SetServerURL(const nsACString& aServerURL);
 bool     GetMinidumpPath(nsAString& aPath);
 nsresult SetMinidumpPath(const nsAString& aPath);
 
 
-// AnnotateCrashReport and AppendAppNotesToCrashReport may be called from any
-// thread in a chrome process, but may only be called from the main thread in
-// a content process.
+// AnnotateCrashReport, RemoveCrashReportAnnotation and
+// AppendAppNotesToCrashReport may be called from any thread in a chrome
+// process, but may only be called from the main thread in a content process.
 nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data);
+nsresult RemoveCrashReportAnnotation(const nsACString& key);
 nsresult AppendAppNotesToCrashReport(const nsACString& data);
 
 void AnnotateOOMAllocationSize(size_t size);
 nsresult SetGarbageCollecting(bool collecting);
 void SetEventloopNestingLevel(uint32_t level);
 
 nsresult SetRestartArgs(int argc, char** argv);
 nsresult SetupExtraData(nsIFile* aAppDataDirectory,