Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 10 Aug 2015 09:18:21 -0400
changeset 284151 c07833808ff1098d9eec76397d4f17881e6c2222
parent 284132 cd45a38ded04f57eb0d02829787b470eaf2f4802 (current diff)
parent 284150 6c5f55b6ed6f5f3b62bc602fef9b9b63981ffab4 (diff)
child 284152 86b3b49cae3712b44f0474d9acd8be0ecd85a9b5
child 284163 6b0ba29e866dcf63a3054eb955ece082cbbeda13
child 284166 7a19194812eb767bee7cdf8fc36ba9a383c1bead
child 284197 0fb7ec3c7810400281ca468131a7003a8b2df586
child 284231 8202cb84b06328e0aeab9354e66550d65609140f
push id4238
push usernalexander@mozilla.com
push dateMon, 10 Aug 2015 18:07:50 +0000
reviewersmerge
milestone42.0a1
Merge inbound to m-c. a=merge
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -61,15 +61,8 @@ MOZ_WEBGL_CONFORMANT=1
 MOZ_PAY=1
 # Enable activities. These are used for FxOS developers currently.
 MOZ_ACTIVITIES=1
 MOZ_JSDOWNLOADS=1
 MOZ_WEBM_ENCODER=1
 
 # Enable checking that add-ons are signed by the trusted root
 MOZ_ADDON_SIGNING=1
-if test "$MOZ_OFFICIAL_BRANDING"; then
-  if test "$MOZ_UPDATE_CHANNEL" = "beta" -o \
-          "$MOZ_UPDATE_CHANNEL" = "release" -o \
-          "$MOZ_UPDATE_CHANNEL" = "esr"; then
-    MOZ_REQUIRE_SIGNING=1
-  fi
-fi
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -45,29 +45,16 @@ public:
 
   ASTConsumerPtr makeASTConsumer() {
     return astMatcher.newASTConsumer();
   }
 
 private:
   class ScopeChecker : public MatchFinder::MatchCallback {
   public:
-    enum Scope {
-      eLocal,
-      eGlobal
-    };
-    ScopeChecker(Scope scope_) :
-      scope(scope_) {}
-    virtual void run(const MatchFinder::MatchResult &Result);
-  private:
-    Scope scope;
-  };
-
-  class NonHeapClassChecker : public MatchFinder::MatchCallback {
-  public:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
   class ArithmeticArgChecker : public MatchFinder::MatchCallback {
   public:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
@@ -111,19 +98,17 @@ private:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
   class ExplicitImplicitChecker : public MatchFinder::MatchCallback {
   public:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
-  ScopeChecker stackClassChecker;
-  ScopeChecker globalClassChecker;
-  NonHeapClassChecker nonheapClassChecker;
+  ScopeChecker scopeChecker;
   ArithmeticArgChecker arithmeticArgChecker;
   TrivialCtorDtorChecker trivialCtorDtorChecker;
   NaNExprChecker nanExprChecker;
   NoAddRefReleaseOnReturnChecker noAddRefReleaseOnReturnChecker;
   RefCountedInsideLambdaChecker refCountedInsideLambdaChecker;
   ExplicitOperatorBoolChecker explicitOperatorBoolChecker;
   NoDuplicateRefCntMemberChecker noDuplicateRefCntMemberChecker;
   NeedsNoVTableTypeChecker needsNoVTableTypeChecker;
@@ -272,16 +257,18 @@ private:
 };
 
 static CustomTypeAnnotation StackClass =
   CustomTypeAnnotation("moz_stack_class", "stack");
 static CustomTypeAnnotation GlobalClass =
   CustomTypeAnnotation("moz_global_class", "global");
 static CustomTypeAnnotation NonHeapClass =
   CustomTypeAnnotation("moz_nonheap_class", "non-heap");
+static CustomTypeAnnotation HeapClass =
+  CustomTypeAnnotation("moz_heap_class", "heap");
 static CustomTypeAnnotation MustUse =
   CustomTypeAnnotation("moz_must_use", "must-use");
 
 class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> {
   DiagnosticsEngine &Diag;
   const CompilerInstance &CI;
   DiagnosticsMatcher matcher;
 public:
@@ -609,34 +596,16 @@ bool typeHasVTable(QualType T) {
   return offender && offender->hasDefinition() && offender->isDynamicClass();
 }
 
 }
 
 namespace clang {
 namespace ast_matchers {
 
-/// This matcher will match any class with the stack class assertion or an
-/// array of such classes.
-AST_MATCHER(QualType, stackClassAggregate) {
-  return StackClass.hasEffectiveAnnotation(Node);
-}
-
-/// This matcher will match any class with the global class assertion or an
-/// array of such classes.
-AST_MATCHER(QualType, globalClassAggregate) {
-  return GlobalClass.hasEffectiveAnnotation(Node);
-}
-
-/// This matcher will match any class with the stack class assertion or an
-/// array of such classes.
-AST_MATCHER(QualType, nonheapClassAggregate) {
-  return NonHeapClass.hasEffectiveAnnotation(Node);
-}
-
 /// This matcher will match any function declaration that is declared as a heap
 /// allocator.
 AST_MATCHER(FunctionDecl, heapAllocator) {
   return MozChecker::hasCustomAnnotation(&Node, "moz_heap_allocator");
 }
 
 /// This matcher will match any declaration that is marked as not accepting
 /// arithmetic expressions in its arguments.
@@ -941,64 +910,33 @@ CustomTypeAnnotation::AnnotationReason C
     }
   }
 
   AnnotationReason Reason = { QualType(), RK_None, nullptr };
   Cache[Key] = Reason;
   return Reason;
 }
 
-bool isPlacementNew(const CXXNewExpr *expr) {
+bool isPlacementNew(const CXXNewExpr *Expr) {
   // Regular new expressions aren't placement new
-  if (expr->getNumPlacementArgs() == 0)
+  if (Expr->getNumPlacementArgs() == 0)
     return false;
-  if (MozChecker::hasCustomAnnotation(expr->getOperatorNew(),
-      "moz_heap_allocator"))
+  const FunctionDecl *Decl = Expr->getOperatorNew();
+  if (Decl && MozChecker::hasCustomAnnotation(Decl, "moz_heap_allocator")) {
     return false;
+  }
   return true;
 }
 
-DiagnosticsMatcher::DiagnosticsMatcher()
-  : stackClassChecker(ScopeChecker::eLocal),
-    globalClassChecker(ScopeChecker::eGlobal)
-{
-  // Stack class assertion: non-local variables of a stack class are forbidden
-  // (non-localness checked in the callback)
-  astMatcher.addMatcher(varDecl(hasType(stackClassAggregate())).bind("node"),
-    &stackClassChecker);
-  // Stack class assertion: new stack class is forbidden (unless placement new)
-  astMatcher.addMatcher(newExpr(hasType(pointerType(
-      pointee(stackClassAggregate())
-    ))).bind("node"), &stackClassChecker);
-  // Global class assertion: non-global variables of a global class are forbidden
-  // (globalness checked in the callback)
-  astMatcher.addMatcher(varDecl(hasType(globalClassAggregate())).bind("node"),
-    &globalClassChecker);
-  // Global class assertion: new global class is forbidden
-  astMatcher.addMatcher(newExpr(hasType(pointerType(
-      pointee(globalClassAggregate())
-    ))).bind("node"), &globalClassChecker);
-  // Non-heap class assertion: new non-heap class is forbidden (unless placement
-  // new)
-  astMatcher.addMatcher(newExpr(hasType(pointerType(
-      pointee(nonheapClassAggregate())
-    ))).bind("node"), &nonheapClassChecker);
-
-  // Any heap allocation function that returns a non-heap or a stack class or
-  // a global class is definitely doing something wrong
-  astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(),
-      returns(pointerType(pointee(nonheapClassAggregate()))))))).bind("node"),
-    &nonheapClassChecker);
-  astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(),
-      returns(pointerType(pointee(stackClassAggregate()))))))).bind("node"),
-    &stackClassChecker);
-
-  astMatcher.addMatcher(callExpr(callee(functionDecl(allOf(heapAllocator(),
-      returns(pointerType(pointee(globalClassAggregate()))))))).bind("node"),
-    &globalClassChecker);
+DiagnosticsMatcher::DiagnosticsMatcher() {
+  astMatcher.addMatcher(varDecl().bind("node"), &scopeChecker);
+  astMatcher.addMatcher(newExpr().bind("node"), &scopeChecker);
+  astMatcher.addMatcher(materializeTemporaryExpr().bind("node"), &scopeChecker);
+  astMatcher.addMatcher(callExpr(callee(functionDecl(heapAllocator()))).bind("node"),
+                        &scopeChecker);
 
   astMatcher.addMatcher(callExpr(allOf(hasDeclaration(noArithmeticExprInArgs()),
           anyOf(
               hasDescendant(binaryOperator(allOf(binaryArithmeticOperator(),
                   hasLHS(hasDescendant(declRefExpr())),
                   hasRHS(hasDescendant(declRefExpr()))
               )).bind("node")),
               hasDescendant(unaryOperator(allOf(unaryArithmeticOperator(),
@@ -1082,86 +1020,147 @@ DiagnosticsMatcher::DiagnosticsMatcher()
 
   astMatcher.addMatcher(
       constructorDecl(isInterestingImplicitCtor(),
                       ofClass(allOf(isConcreteClass(), decl().bind("class"))),
                       unless(isMarkedImplicit())).bind("ctor"),
       &explicitImplicitChecker);
 }
 
+// These enum variants determine whether an allocation has occured in the code.
+enum AllocationVariety {
+  AV_None,
+  AV_Global,
+  AV_Automatic,
+  AV_Temporary,
+  AV_Heap,
+};
+
 void DiagnosticsMatcher::ScopeChecker::run(
     const MatchFinder::MatchResult &Result) {
   DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
-    DiagnosticIDs::Error, "variable of type %0 only valid on the stack");
-  unsigned globalID = Diag.getDiagnosticIDs()->getCustomDiagID(
-    DiagnosticIDs::Error, "variable of type %0 only valid as global");
 
+  // There are a variety of different reasons why something could be allocated
+  AllocationVariety Variety = AV_None;
   SourceLocation Loc;
   QualType T;
-  if (const VarDecl *d = Result.Nodes.getNodeAs<VarDecl>("node")) {
-    if (scope == eLocal) {
-      // Ignore the match if it's a local variable.
-      if (d->hasLocalStorage())
-        return;
-    } else if (scope == eGlobal) {
-      // Ignore the match if it's a global variable or a static member of a
-      // class.  The latter is technically not in the global scope, but for the
-      // use case of classes that intend to avoid introducing static
-      // initializers that is fine.
-      if (d->hasGlobalStorage() && !d->isStaticLocal())
-        return;
+
+  // Determine the type of allocation which we detected
+  if (const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("node")) {
+    if (D->hasGlobalStorage()) {
+      Variety = AV_Global;
+    } else {
+      Variety = AV_Automatic;
     }
-
-    Loc = d->getLocation();
-    T = d->getType();
-  } else if (const CXXNewExpr *expr =
-      Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
-    // If it's placement new, then this match doesn't count.
-    if (scope == eLocal && isPlacementNew(expr))
-      return;
-
-    Loc = expr->getStartLoc();
-    T = expr->getAllocatedType();
-  } else if (const CallExpr *expr =
-      Result.Nodes.getNodeAs<CallExpr>("node")) {
-    Loc = expr->getLocStart();
-    T = GetCallReturnType(expr)->getPointeeType();
+    T = D->getType();
+    Loc = D->getLocStart();
+  } else if (const CXXNewExpr *E = Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
+    // New allocates things on the heap.
+    // We don't consider placement new to do anything, as it doesn't actually
+    // allocate the storage, and thus gives us no useful information.
+    if (!isPlacementNew(E)) {
+      Variety = AV_Heap;
+      T = E->getAllocatedType();
+      Loc = E->getLocStart();
+    }
+  } else if (const Expr *E = Result.Nodes.getNodeAs<MaterializeTemporaryExpr>("node")) {
+    Variety = AV_Temporary;
+    T = E->getType().getUnqualifiedType();
+    Loc = E->getLocStart();
+  } else if (const CallExpr *E = Result.Nodes.getNodeAs<CallExpr>("node")) {
+    T = E->getType()->getPointeeType();
+    if (!T.isNull()) {
+      // This will always allocate on the heap, as the heapAllocator() check
+      // was made in the matcher
+      Variety = AV_Heap;
+      Loc = E->getLocStart();
+    }
   }
 
-  if (scope == eLocal) {
-    Diag.Report(Loc, stackID) << T;
-    StackClass.dumpAnnotationReason(Diag, T, Loc);
-  } else if (scope == eGlobal) {
-    Diag.Report(Loc, globalID) << T;
-    GlobalClass.dumpAnnotationReason(Diag, T, Loc);
-  }
-}
+  // Error messages for incorrect allocations.
+  unsigned StackID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Error, "variable of type %0 only valid on the stack");
+  unsigned GlobalID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Error, "variable of type %0 only valid as global");
+  unsigned HeapID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Error, "variable of type %0 only valid on the heap");
+  unsigned NonHeapID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Error, "variable of type %0 is not valid on the heap");
 
-void DiagnosticsMatcher::NonHeapClassChecker::run(
-    const MatchFinder::MatchResult &Result) {
-  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
-  unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
-    DiagnosticIDs::Error, "variable of type %0 is not valid on the heap");
+  unsigned StackNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Note, "value incorrectly allocated in an automatic variable");
+  unsigned GlobalNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Note, "value incorrectly allocated in a global variable");
+  unsigned HeapNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Note, "value incorrectly allocated on the heap");
+  unsigned TemporaryNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Note, "value incorrectly allocated in a temporary");
+
+  // Report errors depending on the annotations on the input types.
+  switch (Variety) {
+  case AV_None:
+    return;
+
+  case AV_Global:
+    if (StackClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, StackID) << T;
+      Diag.Report(Loc, GlobalNoteID);
+      StackClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    if (HeapClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, HeapID) << T;
+      Diag.Report(Loc, GlobalNoteID);
+      HeapClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    break;
 
-  SourceLocation Loc;
-  QualType T;
-  if (const CXXNewExpr *expr = Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
-    // If it's placement new, then this match doesn't count.
-    if (isPlacementNew(expr))
-      return;
-    Loc = expr->getLocStart();
-    T = expr->getAllocatedType();
-  } else if (const CallExpr *expr = Result.Nodes.getNodeAs<CallExpr>("node")) {
-    Loc = expr->getLocStart();
-    T = GetCallReturnType(expr)->getPointeeType();
+  case AV_Automatic:
+    if (GlobalClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, GlobalID) << T;
+      Diag.Report(Loc, StackNoteID);
+      GlobalClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    if (HeapClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, HeapID) << T;
+      Diag.Report(Loc, StackNoteID);
+      HeapClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    break;
+
+  case AV_Temporary:
+    if (GlobalClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, GlobalID) << T;
+      Diag.Report(Loc, TemporaryNoteID);
+      GlobalClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    if (HeapClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, HeapID) << T;
+      Diag.Report(Loc, TemporaryNoteID);
+      HeapClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    break;
+
+  case AV_Heap:
+    if (GlobalClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, GlobalID) << T;
+      Diag.Report(Loc, HeapNoteID);
+      GlobalClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    if (StackClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, StackID) << T;
+      Diag.Report(Loc, HeapNoteID);
+      StackClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    if (NonHeapClass.hasEffectiveAnnotation(T)) {
+      Diag.Report(Loc, NonHeapID) << T;
+      Diag.Report(Loc, HeapNoteID);
+      NonHeapClass.dumpAnnotationReason(Diag, T, Loc);
+    }
+    break;
   }
-
-  Diag.Report(Loc, stackID) << T;
-  NonHeapClass.dumpAnnotationReason(Diag, T, Loc);
 }
 
 void DiagnosticsMatcher::ArithmeticArgChecker::run(
     const MatchFinder::MatchResult &Result) {
   DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
   unsigned errorID = Diag.getDiagnosticIDs()->getCustomDiagID(
       DiagnosticIDs::Error, "cannot pass an arithmetic expression of built-in types to %0");
   const Expr *expr = Result.Nodes.getNodeAs<Expr>("node");
--- a/build/clang-plugin/tests/TestCustomHeap.cpp
+++ b/build/clang-plugin/tests/TestCustomHeap.cpp
@@ -18,11 +18,11 @@ void *operator new(size_t x, int qual) M
 template <typename T>
 T *customAlloc() MOZ_HEAP_ALLOCATOR {
   T *arg =  static_cast<T*>(malloc(sizeof(T)));
   return new (arg) T();
 }
 
 template <typename T>
 void misuseX(T q) {
-  X *foo = customAlloc<X>(); // expected-error {{variable of type 'X' is not valid on the heap}}
-  X *foo2 = new (100) X(); // expected-error {{variable of type 'X' is not valid on the heap}}
+  X *foo = customAlloc<X>(); // expected-error {{variable of type 'X' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
+  X *foo2 = new (100) X(); // expected-error {{variable of type 'X' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
 }
--- a/build/clang-plugin/tests/TestGlobalClass.cpp
+++ b/build/clang-plugin/tests/TestGlobalClass.cpp
@@ -10,43 +10,43 @@ struct MOZ_GLOBAL_CLASS Global {
 template <class T>
 struct MOZ_GLOBAL_CLASS TemplateClass {
   T i;
 };
 
 void gobble(void *) { }
 
 void misuseGlobalClass(int len) {
-  Global notValid; // expected-error {{variable of type 'Global' only valid as global}}
-  Global alsoNotValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}} expected-note {{'Global [2]' is a global type because it is an array of global type 'Global'}}
-  static Global notValid2; // expected-error {{variable of type 'Global' only valid as global}}
-  static Global alsoNotValid2[2]; // expected-error {{variable of type 'Global [2]' only valid as global}} expected-note {{'Global [2]' is a global type because it is an array of global type 'Global'}}
+  Global notValid; // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated in an automatic variable}}
+  Global alsoNotValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}} expected-note {{'Global [2]' is a global type because it is an array of global type 'Global'}} expected-note {{value incorrectly allocated in an automatic variable}}
+  static Global valid;
+  static Global alsoValid[2];
 
-  gobble(&notValid2);
   gobble(&notValid);
-  gobble(&alsoNotValid2[0]);
+  gobble(&valid);
+  gobble(&alsoValid[0]);
 
-  gobble(new Global); // expected-error {{variable of type 'Global' only valid as global}}
-  gobble(new Global[10]); // expected-error {{variable of type 'Global' only valid as global}}
-  gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid as global}}
-  gobble(len <= 5 ? &notValid2 : new Global); // expected-error {{variable of type 'Global' only valid as global}}
+  gobble(new Global); // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(new Global[10]); // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid as global}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(len <= 5 ? &valid : new Global); // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated on the heap}}
 
   char buffer[sizeof(Global)];
-  gobble(new (buffer) Global); // expected-error {{variable of type 'Global' only valid as global}}
+  gobble(new (buffer) Global);
 }
 
 Global valid;
 struct RandomClass {
   Global nonstaticMember; // expected-note {{'RandomClass' is a global type because member 'nonstaticMember' is a global type 'Global'}}
   static Global staticMember;
 };
 struct MOZ_GLOBAL_CLASS RandomGlobalClass {
   Global nonstaticMember;
   static Global staticMember;
 };
 
 struct BadInherit : Global {}; // expected-note {{'BadInherit' is a global type because it inherits from a global type 'Global'}}
 struct MOZ_GLOBAL_CLASS GoodInherit : Global {};
 
 void misuseGlobalClassEvenMore(int len) {
-  BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid as global}}
-  RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid as global}}
+  BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid as global}} expected-note {{value incorrectly allocated in an automatic variable}}
+  RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid as global}} expected-note {{value incorrectly allocated in an automatic variable}}
 }
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/tests/TestHeapClass.cpp
@@ -0,0 +1,64 @@
+#define MOZ_HEAP_CLASS __attribute__((annotate("moz_heap_class")))
+#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
+
+#include <stddef.h>
+
+struct MOZ_HEAP_CLASS Heap {
+  int i;
+  Heap() {}
+  MOZ_IMPLICIT Heap(int i) {}
+  Heap(int i, int j) {}
+  void *operator new(size_t x) throw() { return 0; }
+  void *operator new(size_t blah, char *buffer) { return buffer; }
+};
+
+template <class T>
+struct MOZ_HEAP_CLASS TemplateClass {
+  T i;
+};
+
+void gobble(void *) { }
+
+void gobbleref(const Heap&) { }
+
+void misuseHeapClass(int len) {
+  Heap invalid; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}}
+  Heap alsoInvalid[2]; // expected-error {{variable of type 'Heap [2]' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}} expected-note {{'Heap [2]' is a heap type because it is an array of heap type 'Heap'}}
+  static Heap invalidStatic; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}}
+  static Heap alsoInvalidStatic[2]; // expected-error {{variable of type 'Heap [2]' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}} expected-note {{'Heap [2]' is a heap type because it is an array of heap type 'Heap'}}
+
+  gobble(&invalid);
+  gobble(&invalidStatic);
+  gobble(&alsoInvalid[0]);
+
+  gobbleref(Heap()); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}}
+  gobbleref(Heap(10, 20)); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}}
+  gobbleref(Heap(10)); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}}
+  gobbleref(10); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}}
+
+  gobble(new Heap);
+  gobble(new Heap[10]);
+  gobble(new TemplateClass<int>);
+  gobble(len <= 5 ? &invalid : new Heap);
+
+  char buffer[sizeof(Heap)];
+  gobble(new (buffer) Heap);
+}
+
+Heap invalidStatic; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}}
+struct RandomClass {
+  Heap nonstaticMember; // expected-note {{'RandomClass' is a heap type because member 'nonstaticMember' is a heap type 'Heap'}}
+  static Heap staticMember; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}}
+};
+struct MOZ_HEAP_CLASS RandomHeapClass {
+  Heap nonstaticMember;
+  static Heap staticMember; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}}
+};
+
+struct BadInherit : Heap {}; // expected-note {{'BadInherit' is a heap type because it inherits from a heap type 'Heap'}}
+struct MOZ_HEAP_CLASS GoodInherit : Heap {};
+
+void useStuffWrongly() {
+  BadInherit i; // expected-error {{variable of type 'BadInherit' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}}
+  RandomClass r; // expected-error {{variable of type 'RandomClass' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}}
+}
--- a/build/clang-plugin/tests/TestInheritTypeAnnotationsFromTemplateArgs.cpp
+++ b/build/clang-plugin/tests/TestInheritTypeAnnotationsFromTemplateArgs.cpp
@@ -5,13 +5,13 @@
 class Normal {};
 class MOZ_STACK_CLASS Stack {};
 class IndirectStack : Stack {}; // expected-note {{'IndirectStack' is a stack type because it inherits from a stack type 'Stack'}}
 
 template<class T>
 class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Template {}; // expected-note 2 {{'Template<Stack>' is a stack type because it has a template argument stack type 'Stack'}} expected-note {{'Template<IndirectStack>' is a stack type because it has a template argument stack type 'IndirectStack'}}
 class IndirectTemplate : Template<Stack> {}; // expected-note {{'IndirectTemplate' is a stack type because it inherits from a stack type 'Template<Stack>'}}
 
-static Template<Stack> a; // expected-error {{variable of type 'Template<Stack>' only valid on the stack}}
-static Template<IndirectStack> b; // expected-error {{variable of type 'Template<IndirectStack>' only valid on the stack}}
+static Template<Stack> a; // expected-error {{variable of type 'Template<Stack>' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
+static Template<IndirectStack> b; // expected-error {{variable of type 'Template<IndirectStack>' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
 static Template<Normal> c;
-static IndirectTemplate d; // expected-error {{variable of type 'IndirectTemplate' only valid on the stack}}
+static IndirectTemplate d; // expected-error {{variable of type 'IndirectTemplate' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
 
--- a/build/clang-plugin/tests/TestMultipleAnnotations.cpp
+++ b/build/clang-plugin/tests/TestMultipleAnnotations.cpp
@@ -1,14 +1,14 @@
 #define MOZ_MUST_USE __attribute__((annotate("moz_must_use")))
 #define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
 
 class MOZ_MUST_USE MOZ_STACK_CLASS TestClass {};
 
-TestClass foo; // expected-error {{variable of type 'TestClass' only valid on the stack}}
+TestClass foo; // expected-error {{variable of type 'TestClass' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
 
 TestClass f()
 {
   TestClass bar;
   return bar;
 }
 
 void g()
--- a/build/clang-plugin/tests/TestNonHeapClass.cpp
+++ b/build/clang-plugin/tests/TestNonHeapClass.cpp
@@ -20,20 +20,20 @@ void misuseNonHeapClass(int len) {
   NonHeap alsoValid[2];
   static NonHeap validStatic;
   static NonHeap alsoValidStatic[2];
 
   gobble(&valid);
   gobble(&validStatic);
   gobble(&alsoValid[0]);
 
-  gobble(new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}}
-  gobble(new NonHeap[10]); // expected-error {{variable of type 'NonHeap' is not valid on the heap}}
-  gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' is not valid on the heap}}
-  gobble(len <= 5 ? &valid : new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}}
+  gobble(new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(new NonHeap[10]); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(len <= 5 ? &valid : new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
 
   char buffer[sizeof(NonHeap)];
   gobble(new (buffer) NonHeap);
 }
 
 NonHeap validStatic;
 struct RandomClass {
   NonHeap nonstaticMember; // expected-note {{'RandomClass' is a non-heap type because member 'nonstaticMember' is a non-heap type 'NonHeap'}}
@@ -43,20 +43,20 @@ struct MOZ_NONHEAP_CLASS RandomNonHeapCl
   NonHeap nonstaticMember;
   static NonHeap staticMember;
 };
 
 struct BadInherit : NonHeap {}; // expected-note {{'BadInherit' is a non-heap type because it inherits from a non-heap type 'NonHeap'}}
 struct MOZ_NONHEAP_CLASS GoodInherit : NonHeap {};
 
 void useStuffWrongly() {
-  gobble(new BadInherit); // expected-error {{variable of type 'BadInherit' is not valid on the heap}}
-  gobble(new RandomClass); // expected-error {{variable of type 'RandomClass' is not valid on the heap}}
+  gobble(new BadInherit); // expected-error {{variable of type 'BadInherit' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(new RandomClass); // expected-error {{variable of type 'RandomClass' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}}
 }
 
 // Stack class overrides non-heap typees.
 struct MOZ_STACK_CLASS StackClass {};
 struct MOZ_NONHEAP_CLASS InferredStackClass : GoodInherit {
   NonHeap nonstaticMember;
   StackClass stackClass; // expected-note {{'InferredStackClass' is a stack type because member 'stackClass' is a stack type 'StackClass'}}
 };
 
-InferredStackClass global; // expected-error {{variable of type 'InferredStackClass' only valid on the stack}}
+InferredStackClass global; // expected-error {{variable of type 'InferredStackClass' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
--- a/build/clang-plugin/tests/TestStackClass.cpp
+++ b/build/clang-plugin/tests/TestStackClass.cpp
@@ -12,39 +12,39 @@ struct MOZ_STACK_CLASS TemplateClass {
   T i;
 };
 
 void gobble(void *) { }
 
 void misuseStackClass(int len) {
   Stack valid;
   Stack alsoValid[2];
-  static Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
-  static Stack alsoNotValid[2]; // expected-error {{variable of type 'Stack [2]' only valid on the stack}} expected-note {{'Stack [2]' is a stack type because it is an array of stack type 'Stack'}}
+  static Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
+  static Stack alsoNotValid[2]; // expected-error {{variable of type 'Stack [2]' only valid on the stack}} expected-note {{'Stack [2]' is a stack type because it is an array of stack type 'Stack'}} expected-note {{value incorrectly allocated in a global variable}}
 
   gobble(&valid);
   gobble(&notValid);
   gobble(&alsoValid[0]);
 
-  gobble(new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}}
-  gobble(new Stack[10]); // expected-error {{variable of type 'Stack' only valid on the stack}}
-  gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid on the stack}}
-  gobble(len <= 5 ? &valid : new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}}
+  gobble(new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(new Stack[10]); // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}}
+  gobble(len <= 5 ? &valid : new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}}
 
   char buffer[sizeof(Stack)];
   gobble(new (buffer) Stack);
 }
 
-Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
+Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
 struct RandomClass {
   Stack nonstaticMember; // expected-note {{'RandomClass' is a stack type because member 'nonstaticMember' is a stack type 'Stack'}}
-  static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
+  static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
 };
 struct MOZ_STACK_CLASS RandomStackClass {
   Stack nonstaticMember;
-  static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
+  static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
 };
 
 struct BadInherit : Stack {}; // expected-note {{'BadInherit' is a stack type because it inherits from a stack type 'Stack'}}
 struct MOZ_STACK_CLASS GoodInherit : Stack {};
 
-BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid on the stack}}
-RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid on the stack}}
+BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
+RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}}
--- a/build/clang-plugin/tests/moz.build
+++ b/build/clang-plugin/tests/moz.build
@@ -4,16 +4,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 SOURCES += [
     'TestBadImplicitConversionCtor.cpp',
     'TestCustomHeap.cpp',
     'TestExplicitOperatorBool.cpp',
     'TestGlobalClass.cpp',
+    'TestHeapClass.cpp',
     'TestInheritTypeAnnotationsFromTemplateArgs.cpp',
     'TestMultipleAnnotations.cpp',
     'TestMustOverride.cpp',
     'TestMustUse.cpp',
     'TestNANTestingExpr.cpp',
     'TestNANTestingExprC.c',
     'TestNeedsNoVTableType.cpp',
     'TestNoAddRefReleaseOnReturn.cpp',
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -164,17 +164,17 @@ GetWindowURI(nsIDOMWindow *aWindow)
 
 static void
 AppendWindowURI(nsGlobalWindow *aWindow, nsACString& aStr, bool aAnonymize)
 {
   nsCOMPtr<nsIURI> uri = GetWindowURI(aWindow);
 
   if (uri) {
     if (aAnonymize && !aWindow->IsChromeWindow()) {
-      aStr.AppendPrintf("<anonymized-%d>", aWindow->WindowID());
+      aStr.AppendPrintf("<anonymized-%llu>", aWindow->WindowID());
     } else {
       nsCString spec;
       uri->GetSpec(spec);
 
       // A hack: replace forward slashes with '\\' so they aren't
       // treated as path separators.  Users of the reporters
       // (such as about:memory) have to undo this change.
       spec.ReplaceChar('/', '\\');
@@ -273,19 +273,17 @@ CollectWindowReports(nsGlobalWindow *aWi
     }
   }
 
   windowPath += NS_LITERAL_CSTRING("window-objects/");
 
   if (top) {
     windowPath += NS_LITERAL_CSTRING("top(");
     AppendWindowURI(top, windowPath, aAnonymize);
-    windowPath += NS_LITERAL_CSTRING(", id=");
-    windowPath.AppendInt(top->WindowID());
-    windowPath += NS_LITERAL_CSTRING(")");
+    windowPath.AppendPrintf(", id=%llu)", top->WindowID());
 
     aTopWindowPaths->Put(aWindow->WindowID(), windowPath);
 
     windowPath += aWindow->IsFrozen() ? NS_LITERAL_CSTRING("/cached/")
                                       : NS_LITERAL_CSTRING("/active/");
   } else {
     if (aGhostWindowIDs->Contains(aWindow->WindowID())) {
       windowPath += NS_LITERAL_CSTRING("top(none)/ghost/");
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1008,35 +1008,32 @@ void MediaDecoder::ChangeState(PlayState
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
   if (mNextState == aState) {
     mNextState = PLAY_STATE_PAUSED;
   }
 
   if (mPlayState == PLAY_STATE_SHUTDOWN) {
-    GetReentrantMonitor().NotifyAll();
     return;
   }
 
   DECODER_LOG("ChangeState %s => %s",
               ToPlayStateStr(mPlayState), ToPlayStateStr(aState));
   mPlayState = aState;
 
   if (mPlayState == PLAY_STATE_PLAYING) {
     ConstructMediaTracks();
   } else if (IsEnded()) {
     RemoveMediaTracks();
   }
 
   CancelDormantTimer();
   // Start dormant timer if necessary
   StartDormantTimer();
-
-  GetReentrantMonitor().NotifyAll();
 }
 
 void MediaDecoder::UpdateLogicalPosition(MediaDecoderEventVisibility aEventVisibility)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mShuttingDown)
     return;
 
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -1093,25 +1093,21 @@ protected:
   // Media duration set explicitly by JS. At present, this is only ever present
   // for MSE.
   Canonical<Maybe<double>> mExplicitDuration;
 
   // Set to one of the valid play states.
   // This can only be changed on the main thread while holding the decoder
   // monitor. Thus, it can be safely read while holding the decoder monitor
   // OR on the main thread.
-  // Any change to the state on the main thread must call NotifyAll on the
-  // monitor so the decode thread can wake up.
   Canonical<PlayState> mPlayState;
 
   // This can only be changed on the main thread while holding the decoder
   // monitor. Thus, it can be safely read while holding the decoder monitor
   // OR on the main thread.
-  // Any change to the state must call NotifyAll on the monitor.
-  // This can only be PLAY_STATE_PAUSED or PLAY_STATE_PLAYING.
   Canonical<PlayState> mNextState;
 
   // True if the decoder is seeking.
   Canonical<bool> mLogicallySeeking;
 
   // True if the media is same-origin with the element. Data can only be
   // passed to MediaStreams when this is true.
   Canonical<bool> mSameOriginMedia;
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -655,19 +655,16 @@ MediaDecoderStateMachine::Push(VideoData
   MOZ_ASSERT(aSample);
   // TODO: Send aSample to MSG and recalculate readystate before pushing,
   // otherwise AdvanceFrame may pop the sample before we have a chance
   // to reach playing.
   aSample->mFrameID = ++mCurrentFrameID;
   VideoQueue().Push(aSample);
   UpdateNextFrameStatus();
   DispatchDecodeTasksIfNeeded();
-
-  // XXXbholley - Is this still necessary?
-  mDecoder->GetReentrantMonitor().NotifyAll();
 }
 
 void
 MediaDecoderStateMachine::PushFront(VideoData* aSample)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(aSample);
 
@@ -689,19 +686,16 @@ MediaDecoderStateMachine::OnAudioPopped(
 void
 MediaDecoderStateMachine::OnVideoPopped(const MediaData* aSample)
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mDecoder->UpdatePlaybackOffset(aSample->mOffset);
   UpdateNextFrameStatus();
   DispatchVideoDecodeTaskIfNeeded();
-  // Notify the decode thread that the video queue's buffers may have
-  // free'd up space for more frames.
-  mDecoder->GetReentrantMonitor().NotifyAll();
 }
 
 void
 MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
                                        MediaDecoderReader::NotDecodedReason aReason)
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
@@ -773,17 +767,16 @@ MediaDecoderStateMachine::OnNotDecoded(M
   }
   switch (mState) {
     case DECODER_STATE_BUFFERING:
     case DECODER_STATE_DECODING: {
       if (MaybeFinishDecodeFirstFrame()) {
         return;
       }
       CheckIfDecodeComplete();
-      mDecoder->GetReentrantMonitor().NotifyAll();
       // Schedule the state machine to notify track ended as soon as possible.
       if (mAudioCaptured) {
         ScheduleStateMachine();
       }
       return;
     }
     case DECODER_STATE_SEEKING: {
       if (!mCurrentSeek.Exists()) {
@@ -1088,17 +1081,16 @@ void MediaDecoderStateMachine::MaybeStar
   StartAudioThread();
 
   // Tell DecodedStream to start playback with specified start time and media
   // info. This is consistent with how we create AudioSink in StartAudioThread().
   if (mAudioCaptured) {
     mDecodedStream->StartPlayback(GetMediaTime(), mInfo);
   }
 
-  mDecoder->GetReentrantMonitor().NotifyAll();
   DispatchDecodeTasksIfNeeded();
 }
 
 void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   SAMPLE_LOG("UpdatePlaybackPositionInternal(%lld)", aTime);
   AssertCurrentThreadInMonitor();
@@ -1255,23 +1247,21 @@ void MediaDecoderStateMachine::SetDorman
     // Note that we do not wait for the decode task queue to go idle before
     // queuing the ReleaseMediaResources task - instead, we disconnect promises,
     // reset state, and put a ResetDecode in the decode task queue. Any tasks
     // that run after ResetDecode are supposed to run with a clean slate. We rely
     // on that in other places (i.e. seeking), so it seems reasonable to rely on
     // it here as well.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(mReader, &MediaDecoderReader::ReleaseMediaResources);
     DecodeTaskQueue()->Dispatch(r.forget());
-    mDecoder->GetReentrantMonitor().NotifyAll();
   } else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) {
     mDecodingFrozenAtStateDecoding = true;
     ScheduleStateMachine();
     mDecodingFirstFrame = true;
     SetState(DECODER_STATE_DECODING_NONE);
-    mDecoder->GetReentrantMonitor().NotifyAll();
   }
 }
 
 void MediaDecoderStateMachine::Shutdown()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   // Once we've entered the shutdown state here there's no going back.
@@ -1904,20 +1894,16 @@ MediaDecoderStateMachine::DecodeError()
   }
 
   // Change state to error, which will cause the state machine to wait until
   // the MediaDecoder shuts it down.
   SetState(DECODER_STATE_ERROR);
   ScheduleStateMachine();
   DECODER_WARN("Decode error, changed state to ERROR");
 
-  // XXXbholley - Is anybody actually waiting on this monitor, or is it just
-  // a leftover from when we used to do sync dispatch for the below?
-  mDecoder->GetReentrantMonitor().NotifyAll();
-
   // MediaDecoder::DecodeError notifies the owner, and then shuts down the state
   // machine.
   nsCOMPtr<nsIRunnable> event =
     NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
   AbstractThread::MainThread()->Dispatch(event.forget());
 }
 
 void
@@ -2351,18 +2337,16 @@ nsresult MediaDecoderStateMachine::RunSt
                     OutOfDecodedVideo(), VideoRequestStatus());
         return NS_OK;
       }
 
       DECODER_LOG("Changed state from BUFFERING to DECODING");
       DECODER_LOG("Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
       StartDecoding();
 
-      // Notify to allow blocked decoder thread to continue
-      mDecoder->GetReentrantMonitor().NotifyAll();
       NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled");
       return NS_OK;
     }
 
     case DECODER_STATE_SEEKING: {
       if (mPendingSeek.Exists()) {
         InitiateSeek();
       }
@@ -3071,19 +3055,16 @@ void MediaDecoderStateMachine::OnAudioSi
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   MOZ_ASSERT(!mAudioCaptured, "Should be disconnected when capturing audio.");
 
   mAudioSinkPromise.Complete();
   ResyncAudioClock();
   mAudioCompleted = true;
-
-  // Kick the decode thread; it may be sleeping waiting for this to finish.
-  mDecoder->GetReentrantMonitor().NotifyAll();
 }
 
 void MediaDecoderStateMachine::OnAudioSinkError()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   MOZ_ASSERT(!mAudioCaptured, "Should be disconnected when capturing audio.");
 
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -107,19 +107,18 @@ extern PRLogModuleInfo* gMediaDecoderLog
 extern PRLogModuleInfo* gMediaSampleLog;
 
 /*
   The state machine class. This manages the decoding and seeking in the
   MediaDecoderReader on the decode task queue, and A/V sync on the shared
   state machine thread, and controls the audio "push" thread.
 
   All internal state is synchronised via the decoder monitor. State changes
-  are either propagated by NotifyAll on the monitor (typically when state
-  changes need to be propagated to non-state machine threads) or by scheduling
-  the state machine to run another cycle on the shared state machine thread.
+  are propagated by scheduling the state machine to run another cycle on the
+  shared state machine thread.
 
   See MediaDecoder.h for more details.
 */
 class MediaDecoderStateMachine
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderStateMachine)
 public:
   typedef MediaDecoderReader::AudioDataPromise AudioDataPromise;
@@ -881,18 +880,16 @@ private:
   // Queue of audio frames. This queue is threadsafe, and is accessed from
   // the audio, decoder, state machine, and main threads.
   MediaQueue<MediaData> mAudioQueue;
   // Queue of video frames. This queue is threadsafe, and is accessed from
   // the decoder, state machine, and main threads.
   MediaQueue<MediaData> mVideoQueue;
 
   // The decoder monitor must be obtained before modifying this state.
-  // NotifyAll on the monitor must be called when the state is changed so
-  // that interested threads can wake up and alter behaviour if appropriate
   // Accessed on state machine, audio, main, and AV thread.
   Watchable<State> mState;
 
   // The task queue in which we run decode tasks. This is referred to as
   // the "decode thread", though in practise tasks can run on a different
   // thread every time they're called.
   TaskQueue* DecodeTaskQueue() const { return mReader->OwnerThread(); }
 
--- a/gfx/harfbuzz/src/hb-ot-shape-fallback.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-fallback.cc
@@ -413,23 +413,22 @@ position_cluster (const hb_ot_shape_plan
 void
 _hb_ot_shape_fallback_position (const hb_ot_shape_plan_t *plan,
 				hb_font_t *font,
 				hb_buffer_t  *buffer)
 {
   _hb_buffer_assert_gsubgpos_vars (buffer);
 
   unsigned int start = 0;
-  unsigned int last_cluster = buffer->info[0].cluster;
   unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 1; i < count; i++)
-    if (buffer->info[i].cluster != last_cluster) {
+    if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i])))) {
       position_cluster (plan, font, buffer, start, i);
       start = i;
-      last_cluster = buffer->info[i].cluster;
     }
   position_cluster (plan, font, buffer, start, count);
 }
 
 
 /* Performs old-style TrueType kerning. */
 void
 _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan,
--- a/gfx/harfbuzz/src/hb-ot-shape-normalize.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-normalize.cc
@@ -318,17 +318,17 @@ void
   /* First round, decompose */
 
   buffer->clear_output ();
   count = buffer->len;
   for (buffer->idx = 0; buffer->idx < count;)
   {
     unsigned int end;
     for (end = buffer->idx + 1; end < count; end++)
-      if (buffer->cur().cluster != buffer->info[end].cluster)
+      if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end]))))
         break;
 
     decompose_cluster (&c, end, might_short_circuit, always_short_circuit);
   }
   buffer->swap_buffers ();
 
 
   /* Second round, reorder (inplace) */
--- a/gfx/tests/crashtests/crashtests.list
+++ b/gfx/tests/crashtests/crashtests.list
@@ -97,17 +97,17 @@ load 633322-1.html
 load 665218.html
 load 686190-1.html
 load 693143-1.html
 load 768079-1.html
 load 783041-1.html
 load 783041-2.html
 load 783041-3.html
 load 783041-4.html
-load 798853.html # bug 868792
+asserts-if(gtkWidget,0-1) load 798853.html # bug 868792
 asserts-if(winWidget,0-1) skip-if(B2G) load 815489.html
 load 836225-1.html
 load 839745-1.html
 load 856784-1.html
 load 893572-1.html
 load 893572-2.html
 load 893572-3.html
 load 893572-4.html
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -1563,16 +1563,18 @@ gfxHarfBuzzShaper::ShapeText(gfxContext 
     }
     hb_buffer_set_language(buffer, language);
 
     uint32_t length = aLength;
     hb_buffer_add_utf16(buffer,
                         reinterpret_cast<const uint16_t*>(aText),
                         length, 0, length);
 
+    hb_buffer_set_cluster_level(buffer, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS);
+
     hb_shape(mHBFont, buffer, features.Elements(), features.Length());
 
     if (isRightToLeft) {
         hb_buffer_reverse(buffer);
     }
 
     nsresult rv = SetGlyphsFromRun(aContext, aShapedText, aOffset, aLength,
                                    aText, buffer, aVertical);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -4760,16 +4760,20 @@ CanOptimizeDenseOrUnboxedArraySetElem(JS
 
     *isAddingCaseOut = false;
     *protoDepthOut = 0;
 
     // Some initial sanity checks.
     if (initLength < oldInitLength || capacity < oldCapacity)
         return false;
 
+    // Unboxed arrays need to be able to emit floating point code.
+    if (obj->is<UnboxedArrayObject>() && !obj->runtimeFromMainThread()->jitSupportsFloatingPoint)
+        return false;
+
     Shape* shape = obj->maybeShape();
 
     // Cannot optimize if the shape changed.
     if (oldShape != shape)
         return false;
 
     // Cannot optimize if the capacity changed.
     if (oldCapacity != capacity)
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -3118,22 +3118,17 @@ GenerateSetUnboxed(JSContext* cx, MacroA
         if (unboxedType == JSVAL_TYPE_OBJECT)
             masm.callPreBarrier(address, MIRType_Object);
         else if (unboxedType == JSVAL_TYPE_STRING)
             masm.callPreBarrier(address, MIRType_String);
         else
             MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(unboxedType));
     }
 
-    // If unboxed objects in this group have have never been converted to
-    // native objects then the type set check performed above ensures the value
-    // being written can be stored in the unboxed object.
-    Label* storeFailure = obj->group()->unboxedLayout().nativeGroup() ? &failure : nullptr;
-
-    masm.storeUnboxedProperty(address, unboxedType, value, storeFailure);
+    masm.storeUnboxedProperty(address, unboxedType, value, &failure);
 
     attacher.jumpRejoin(masm);
 
     masm.bind(&failurePopObject);
     masm.pop(object);
     masm.bind(&failure);
 
     attacher.jumpNextStub(masm);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -769,24 +769,23 @@ IonBuilder::inlineArrayPush(CallInfo& ca
     MDefinition* obj = convertUnboxedObjects(callInfo.thisArg());
     MDefinition* value = callInfo.getArg(0);
     if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
                                       &obj, nullptr, &value, /* canModify = */ false))
     {
         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
         return InliningStatus_NotInlined;
     }
-    MOZ_ASSERT(obj == callInfo.thisArg() && value == callInfo.getArg(0));
 
     if (getInlineReturnType() != MIRType_Int32)
         return InliningStatus_NotInlined;
-    if (callInfo.thisArg()->type() != MIRType_Object)
+    if (obj->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
-    TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
+    TemporaryTypeSet* thisTypes = obj->resultTypeSet();
     if (!thisTypes)
         return InliningStatus_NotInlined;
     const Class* clasp = thisTypes->getKnownClass(constraints());
     if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
         return InliningStatus_NotInlined;
     if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
                                   OBJECT_FLAG_LENGTH_OVERFLOW))
     {
@@ -803,23 +802,22 @@ IonBuilder::inlineArrayPush(CallInfo& ca
         thisTypes->convertDoubleElements(constraints());
     if (conversion == TemporaryTypeSet::AmbiguousDoubleConversion) {
         trackOptimizationOutcome(TrackedOutcome::ArrayDoubleConversion);
         return InliningStatus_NotInlined;
     }
 
     JSValueType unboxedType = JSVAL_TYPE_MAGIC;
     if (clasp == &UnboxedArrayObject::class_) {
-        unboxedType = UnboxedArrayElementType(constraints(), callInfo.thisArg(), nullptr);
+        unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
         if (unboxedType == JSVAL_TYPE_MAGIC)
             return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
-    value = callInfo.getArg(0);
 
     if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles ||
         conversion == TemporaryTypeSet::MaybeConvertToDoubles)
     {
         MInstruction* valueDouble = MToDouble::New(alloc(), value);
         current->add(valueDouble);
         value = valueDouble;
     }
@@ -934,16 +932,23 @@ IonBuilder::inlineArrayConcat(CallInfo& 
             continue;
 
         if (key->unknownProperties())
             return InliningStatus_NotInlined;
 
         HeapTypeSetKey elemTypes = key->property(JSID_VOID);
         if (!elemTypes.knownSubset(constraints(), thisElemTypes))
             return InliningStatus_NotInlined;
+
+        if (thisGroup->clasp() == &UnboxedArrayObject::class_ &&
+            !CanStoreUnboxedType(alloc(), thisGroup->unboxedLayout().elementType(),
+                                 MIRType_Value, elemTypes.maybeTypes()))
+        {
+            return InliningStatus_NotInlined;
+        }
     }
 
     // Inline the call.
     JSObject* templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
     if (!templateObj || templateObj->group() != thisGroup)
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1851,16 +1851,50 @@ jit::EqualTypes(MIRType type1, Temporary
         return TypeSetIncludes(typeset1, type2, nullptr);
     if (!typeset1 && typeset2)
         return TypeSetIncludes(typeset2, type1, nullptr);
 
     // Typesets should equal.
     return typeset1->equals(typeset2);
 }
 
+// Tests whether input/inputTypes can always be stored to an unboxed
+// object/array property with the given unboxed type.
+bool
+jit::CanStoreUnboxedType(TempAllocator& alloc,
+                         JSValueType unboxedType, MIRType input, TypeSet* inputTypes)
+{
+    TemporaryTypeSet types;
+
+    switch (unboxedType) {
+      case JSVAL_TYPE_BOOLEAN:
+      case JSVAL_TYPE_INT32:
+      case JSVAL_TYPE_DOUBLE:
+      case JSVAL_TYPE_STRING:
+        types.addType(TypeSet::PrimitiveType(unboxedType), alloc.lifoAlloc());
+        break;
+
+      case JSVAL_TYPE_OBJECT:
+        types.addType(TypeSet::AnyObjectType(), alloc.lifoAlloc());
+        types.addType(TypeSet::NullType(), alloc.lifoAlloc());
+        break;
+
+      default:
+        MOZ_CRASH("Bad unboxed type");
+    }
+
+    return TypeSetIncludes(&types, input, inputTypes);
+}
+
+static bool
+CanStoreUnboxedType(TempAllocator& alloc, JSValueType unboxedType, MDefinition* value)
+{
+    return CanStoreUnboxedType(alloc, unboxedType, value->type(), value->resultTypeSet());
+}
+
 bool
 MPhi::specializeType()
 {
 #ifdef DEBUG
     MOZ_ASSERT(!specialized_);
     specialized_ = true;
 #endif
 
@@ -5409,16 +5443,30 @@ jit::PropertyWriteNeedsTypeBarrier(TempA
             // or a VM call is required. A VM call is always required if pobj
             // and pvalue cannot be modified.
             if (!canModify)
                 return true;
             success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue,
                                                 implicitType);
             break;
         }
+
+        // Perform additional filtering to make sure that any unboxed property
+        // being written can accommodate the value.
+        if (key->isGroup() && key->group()->maybeUnboxedLayout()) {
+            const UnboxedLayout& layout = key->group()->unboxedLayout();
+            if (name) {
+                const UnboxedLayout::Property* property = layout.lookup(name);
+                if (property && !CanStoreUnboxedType(alloc, property->type, *pvalue))
+                    return true;
+            } else {
+                if (layout.isArray() && !CanStoreUnboxedType(alloc, layout.elementType(), *pvalue))
+                    return true;
+            }
+        }
     }
 
     if (success)
         return false;
 
     // If all of the objects except one have property types which reflect the
     // value, and the remaining object has no types at all for the property,
     // add a guard that the object does not have that remaining object's type.
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2836,16 +2836,20 @@ MergeTypes(MIRType* ptype, TemporaryType
 
 bool
 TypeSetIncludes(TypeSet* types, MIRType input, TypeSet* inputTypes);
 
 bool
 EqualTypes(MIRType type1, TemporaryTypeSet* typeset1,
            MIRType type2, TemporaryTypeSet* typeset2);
 
+bool
+CanStoreUnboxedType(TempAllocator& alloc,
+                    JSValueType unboxedType, MIRType input, TypeSet* inputTypes);
+
 #ifdef DEBUG
 bool
 IonCompilationCanUseNurseryPointers();
 #endif
 
 // Helper class to check that GC pointers embedded in MIR instructions are in
 // in the nursery only when the store buffer has been marked as needing to
 // cancel all ion compilations. Otherwise, off-thread Ion compilation and
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -124,17 +124,17 @@ class UnboxedLayout : public mozilla::Li
         js_free(traceList_);
 
         nativeGroup_.init(nullptr);
         nativeShape_.init(nullptr);
         replacementGroup_.init(nullptr);
         constructorCode_.init(nullptr);
     }
 
-    bool isArray() {
+    bool isArray() const {
         return elementType_ != JSVAL_TYPE_MAGIC;
     }
 
     void detachFromCompartment();
 
     const PropertyVector& properties() const {
         return properties_;
     }
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -392,20 +392,25 @@
  *   an array of such objects, as a global or static variable, or as the type of
  *   a new expression (unless placement new is being used). If a member of
  *   another class uses this class, or if another class inherits from this
  *   class, then it is considered to be a stack class as well, although this
  *   attribute need not be provided in such cases.
  * MOZ_NONHEAP_CLASS: Applies to all classes. Any class with this annotation is
  *   expected to live on the stack or in static storage, so it is a compile-time
  *   error to use it, or an array of such objects, as the type of a new
- *   expression (unless placement new is being used). If a member of another
- *   class uses this class, or if another class inherits from this class, then
- *   it is considered to be a non-heap class as well, although this attribute
- *   need not be provided in such cases.
+ *   expression. If a member of another class uses this class, or if another
+ *   class inherits from this class, then it is considered to be a non-heap class
+ *   as well, although this attribute need not be provided in such cases.
+ * MOZ_HEAP_CLASS: Applies to all classes. Any class with this annotation is
+ *   expected to live on the heap, so it is a compile-time error to use it, or
+ *   an array of such objects, as the type of a variable declaration, or as a
+ *   temporary object. If a member of another class uses this class, or if
+ *   another class inherits from this class, then it is considered to be a heap
+ *   class as well, although this attribute need not be provided in such cases.
  * MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS: Applies to all classes that are
  *   intended to prevent introducing static initializers.  This attribute
  *   currently makes it a compile-time error to instantiate these classes
  *   anywhere other than at the global scope, or as a static member of a class.
  * MOZ_TRIVIAL_CTOR_DTOR: Applies to all classes that must have both a trivial
  *   constructor and a trivial destructor.  Setting this attribute on a class
  *   makes it a compile-time error for that class to get a non-trivial
  *   constructor or destructor for any reason.
@@ -470,16 +475,17 @@
  *   declarations where an instance of the template should be considered, for
  *   static analysis purposes, to inherit any type annotations (such as
  *   MOZ_MUST_USE and MOZ_STACK_CLASS) from its template arguments.
  */
 #ifdef MOZ_CLANG_PLUGIN
 #  define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override")))
 #  define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
 #  define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class")))
+#  define MOZ_HEAP_CLASS __attribute__((annotate("moz_heap_class")))
 #  define MOZ_TRIVIAL_CTOR_DTOR __attribute__((annotate("moz_trivial_ctor_dtor")))
 #  ifdef DEBUG
      /* in debug builds, these classes do have non-trivial constructors. */
 #    define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS __attribute__((annotate("moz_global_class")))
 #  else
 #    define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS __attribute__((annotate("moz_global_class"))) \
             MOZ_TRIVIAL_CTOR_DTOR
 #  endif
@@ -504,16 +510,17 @@
     _Pragma("clang diagnostic push") \
     _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
     __attribute__((annotate("moz_heap_allocator"))) \
     _Pragma("clang diagnostic pop")
 #else
 #  define MOZ_MUST_OVERRIDE /* nothing */
 #  define MOZ_STACK_CLASS /* nothing */
 #  define MOZ_NONHEAP_CLASS /* nothing */
+#  define MOZ_HEAP_CLASS /* nothing */
 #  define MOZ_TRIVIAL_CTOR_DTOR /* nothing */
 #  define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS /* nothing */
 #  define MOZ_IMPLICIT /* nothing */
 #  define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT /* nothing */
 #  define MOZ_HEAP_ALLOCATOR /* nothing */
 #  define MOZ_OWNING_REF /* nothing */
 #  define MOZ_NON_OWNING_REF /* nothing */
 #  define MOZ_UNSAFE_REF(reason) /* nothing */
--- a/mobile/android/confvars.sh
+++ b/mobile/android/confvars.sh
@@ -107,14 +107,8 @@ export JS_GC_SMALL_CHUNK_SIZE=1
 
 # Enable FxAccount Avatar
 if test "$NIGHTLY_BUILD"; then
   MOZ_ANDROID_FIREFOX_ACCOUNT_PROFILES=1
 fi
 
 # Enable checking that add-ons are signed by the trusted root
 MOZ_ADDON_SIGNING=1
-if test "$MOZ_OFFICIAL_BRANDING"; then
-  if test "$MOZ_UPDATE_CHANNEL" = "beta" -o \
-          "$MOZ_UPDATE_CHANNEL" = "release"; then
-    MOZ_REQUIRE_SIGNING=1
-  fi
-fi
--- a/netwerk/cache2/CacheIndex.cpp
+++ b/netwerk/cache2/CacheIndex.cpp
@@ -1483,74 +1483,68 @@ CacheIndex::HasEntryChanged(CacheIndexEn
 
 void
 CacheIndex::ProcessPendingOperations()
 {
   LOG(("CacheIndex::ProcessPendingOperations()"));
 
   AssertOwnsLock();
 
-  mPendingUpdates.EnumerateEntries(&CacheIndex::UpdateEntryInIndex, this);
+  for (auto iter = mPendingUpdates.Iter(); !iter.Done(); iter.Next()) {
+    CacheIndexEntryUpdate* update = iter.Get();
+
+    LOG(("CacheIndex::ProcessPendingOperations() [hash=%08x%08x%08x%08x%08x]",
+         LOGSHA1(update->Hash())));
+
+    MOZ_ASSERT(update->IsFresh());
+
+    CacheIndexEntry* entry = mIndex.GetEntry(*update->Hash());
+
+    {
+      CacheIndexEntryAutoManage emng(update->Hash(), this);
+      emng.DoNotSearchInUpdates();
+
+      if (update->IsRemoved()) {
+        if (entry) {
+          if (entry->IsRemoved()) {
+            MOZ_ASSERT(entry->IsFresh());
+            MOZ_ASSERT(entry->IsDirty());
+          } else if (!entry->IsDirty() && entry->IsFileEmpty()) {
+            // Entries with empty file are not stored in index on disk. Just
+            // remove the entry, but only in case the entry is not dirty, i.e.
+            // the entry file was empty when we wrote the index.
+            mIndex.RemoveEntry(*update->Hash());
+            entry = nullptr;
+          } else {
+            entry->MarkRemoved();
+            entry->MarkDirty();
+            entry->MarkFresh();
+          }
+        }
+      } else 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.
+        update->ApplyUpdate(entry);
+      } else {
+        // There is no entry in mIndex, copy all information from
+        // mPendingUpdates to mIndex.
+        entry = mIndex.PutEntry(*update->Hash());
+        *entry = *update;
+      }
+    }
+
+    iter.Remove();
+  }
 
   MOZ_ASSERT(mPendingUpdates.Count() == 0);
 
   EnsureCorrectStats();
 }
 
-// static
-PLDHashOperator
-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());
-
-  CacheIndexEntry *entry = index->mIndex.GetEntry(*aEntry->Hash());
-
-  CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
-  emng.DoNotSearchInUpdates();
-
-  if (aEntry->IsRemoved()) {
-    if (entry) {
-      if (entry->IsRemoved()) {
-        MOZ_ASSERT(entry->IsFresh());
-        MOZ_ASSERT(entry->IsDirty());
-      } else if (!entry->IsDirty() && entry->IsFileEmpty()) {
-        // Entries with empty file are not stored in index on disk. Just remove
-        // the entry, but only in case the entry is not dirty, i.e. the entry
-        // file was empty when we wrote the index.
-        index->mIndex.RemoveEntry(*aEntry->Hash());
-        entry = nullptr;
-      } else {
-        entry->MarkRemoved();
-        entry->MarkDirty();
-        entry->MarkFresh();
-      }
-    }
-
-    return PL_DHASH_REMOVE;
-  }
-
-  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) {
     return false;
   }
 
   if (!mLastDumpTime.IsNull() &&
@@ -1705,17 +1699,35 @@ CacheIndex::FinishWrite(bool aSucceeded)
   mIndexHandle = nullptr;
   mRWHash = nullptr;
   ReleaseBuffer();
 
   if (aSucceeded) {
     // Opening of the file must not be in progress if writing succeeded.
     MOZ_ASSERT(!mIndexFileOpener);
 
-    mIndex.EnumerateEntries(&CacheIndex::ApplyIndexChanges, this);
+    for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
+      CacheIndexEntry* entry = iter.Get();
+
+      bool remove = false;
+      {
+        CacheIndexEntryAutoManage emng(entry->Hash(), this);
+
+        if (entry->IsRemoved()) {
+          emng.DoNotSearchInIndex();
+          remove = true;
+        } else if (entry->IsDirty()) {
+          entry->ClearDirty();
+        }
+      }
+      if (remove) {
+        iter.Remove();
+      }
+    }
+
     mIndexOnDiskIsValid = true;
   } else {
     if (mIndexFileOpener) {
       // If opening of the file is still in progress (e.g. WRITE process was
       // canceled by RemoveAll()) then we need to cancel the opener to make sure
       // that OnFileOpenedInternal() won't be called.
       mIndexFileOpener->Cancel();
       mIndexFileOpener = nullptr;
@@ -1762,36 +1774,16 @@ CacheIndex::CopyRecordsToRWBuf(CacheInde
 
   aEntry->WriteToBuf(data->mBuf);
   data->mBuf += sizeof(CacheIndexRecord);
   data->mProcessed++;
 
   return PL_DHASH_NEXT;
 }
 
-// static
-PLDHashOperator
-CacheIndex::ApplyIndexChanges(CacheIndexEntry *aEntry, void* aClosure)
-{
-  CacheIndex *index = static_cast<CacheIndex *>(aClosure);
-
-  CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
-
-  if (aEntry->IsRemoved()) {
-    emng.DoNotSearchInIndex();
-    return PL_DHASH_REMOVE;
-  }
-
-  if (aEntry->IsDirty()) {
-    aEntry->ClearDirty();
-  }
-
-  return PL_DHASH_NEXT;
-}
-
 nsresult
 CacheIndex::GetFile(const nsACString &aName, nsIFile **_retval)
 {
   nsresult rv;
 
   nsCOMPtr<nsIFile> file;
   rv = mCacheDirectory->Clone(getter_AddRefs(file));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1966,17 +1958,23 @@ CacheIndex::WriteLogToDisk()
   mIndexStats.Log();
 
   PRFileDesc *fd = nullptr;
   rv = logFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
                                  0600, &fd);
   NS_ENSURE_SUCCESS(rv, rv);
 
   WriteLogHelper wlh(fd);
-  mIndex.EnumerateEntries(&CacheIndex::WriteEntryToLog, &wlh);
+  for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
+    CacheIndexEntry* entry = iter.Get();
+    if (entry->IsRemoved() || entry->IsDirty()) {
+      wlh.AddEntry(entry);
+    }
+    iter.Remove();
+  }
 
   rv = wlh.Finish();
   PR_Close(fd);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = indexFile->OpenNSPRFileDesc(PR_RDWR, 0600, &fd);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -1999,29 +1997,16 @@ CacheIndex::WriteLogToDisk()
   PR_Close(fd);
   if (bytesWritten != sizeof(CacheIndexHeader)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
-// static
-PLDHashOperator
-CacheIndex::WriteEntryToLog(CacheIndexEntry *aEntry, void* aClosure)
-{
-  WriteLogHelper *wlh = static_cast<WriteLogHelper *>(aClosure);
-
-  if (aEntry->IsRemoved() || aEntry->IsDirty()) {
-    wlh->AddEntry(aEntry);
-  }
-
-  return PL_DHASH_REMOVE;
-}
-
 void
 CacheIndex::ReadIndexFromDisk()
 {
   LOG(("CacheIndex::ReadIndexFromDisk()"));
 
   nsresult rv;
 
   AssertOwnsLock();
@@ -2389,43 +2374,39 @@ CacheIndex::ProcessJournalEntry(CacheInd
 }
 
 void
 CacheIndex::EnsureNoFreshEntry()
 {
 #ifdef DEBUG_STATS
   CacheIndexStats debugStats;
   debugStats.DisableLogging();
-  mIndex.EnumerateEntries(&CacheIndex::SumIndexStats, &debugStats);
+  for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
+    debugStats.BeforeChange(nullptr);
+    debugStats.AfterChange(iter.Get());
+  }
   MOZ_ASSERT(debugStats.Fresh() == 0);
 #endif
 }
 
 void
 CacheIndex::EnsureCorrectStats()
 {
 #ifdef DEBUG_STATS
   MOZ_ASSERT(mPendingUpdates.Count() == 0);
   CacheIndexStats debugStats;
   debugStats.DisableLogging();
-  mIndex.EnumerateEntries(&CacheIndex::SumIndexStats, &debugStats);
+  for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
+    debugStats.BeforeChange(nullptr);
+    debugStats.AfterChange(iter.Get());
+  }
   MOZ_ASSERT(debugStats == mIndexStats);
 #endif
 }
 
-// static
-PLDHashOperator
-CacheIndex::SumIndexStats(CacheIndexEntry *aEntry, void* aClosure)
-{
-  CacheIndexStats *stats = static_cast<CacheIndexStats *>(aClosure);
-  stats->BeforeChange(nullptr);
-  stats->AfterChange(aEntry);
-  return PL_DHASH_NEXT;
-}
-
 void
 CacheIndex::FinishRead(bool aSucceeded)
 {
   LOG(("CacheIndex::FinishRead() [succeeded=%d]", aSucceeded));
   AssertOwnsLock();
 
   MOZ_ASSERT((!aSucceeded && mState == SHUTDOWN) || mState == READING);
 
@@ -2472,17 +2453,17 @@ CacheIndex::FinishRead(bool aSucceeded)
     return;
   }
 
   if (!mIndexOnDiskIsValid) {
     MOZ_ASSERT(mTmpJournal.Count() == 0);
     EnsureNoFreshEntry();
     ProcessPendingOperations();
     // Remove all entries that we haven't seen during this session
-    mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this);
+    RemoveNonFreshEntries();
     StartUpdatingIndex(true);
     return;
   }
 
   if (!mJournalReadSuccessfully) {
     mTmpJournal.Clear();
     EnsureNoFreshEntry();
     ProcessPendingOperations();
@@ -3060,44 +3041,46 @@ CacheIndex::FinishUpdate(bool aSucceeded
   if (mState == SHUTDOWN) {
     return;
   }
 
   if (mState == UPDATING && aSucceeded) {
     // If we've iterated over all entries successfully then all entries that
     // really exist on the disk are now marked as fresh. All non-fresh entries
     // don't exist anymore and must be removed from the index.
-    mIndex.EnumerateEntries(&CacheIndex::RemoveNonFreshEntries, this);
+    RemoveNonFreshEntries();
   }
 
   // Make sure we won't start update. If the build or update failed, there is no
   // reason to believe that it will succeed next time.
   mIndexNeedsUpdate = false;
 
   ChangeState(READY);
   mLastDumpTime = TimeStamp::NowLoRes(); // Do not dump new index immediately
 }
 
-// static
-PLDHashOperator
-CacheIndex::RemoveNonFreshEntries(CacheIndexEntry *aEntry, void* aClosure)
+void
+CacheIndex::RemoveNonFreshEntries()
 {
-  if (aEntry->IsFresh()) {
-    return PL_DHASH_NEXT;
+  for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
+    CacheIndexEntry* entry = iter.Get();
+    if (entry->IsFresh()) {
+      continue;
+    }
+
+    LOG(("CacheIndex::RemoveNonFreshEntries() - Removing entry. "
+         "[hash=%08x%08x%08x%08x%08x]", LOGSHA1(entry->Hash())));
+
+    {
+      CacheIndexEntryAutoManage emng(entry->Hash(), this);
+      emng.DoNotSearchInIndex();
+    }
+
+    iter.Remove();
   }
-
-  LOG(("CacheFile::RemoveNonFreshEntries() - Removing entry. "
-       "[hash=%08x%08x%08x%08x%08x]", LOGSHA1(aEntry->Hash())));
-
-  CacheIndex *index = static_cast<CacheIndex *>(aClosure);
-
-  CacheIndexEntryAutoManage emng(aEntry->Hash(), index);
-  emng.DoNotSearchInIndex();
-
-  return PL_DHASH_REMOVE;
 }
 
 // static
 char const *
 CacheIndex::StateString(EState aState)
 {
   switch (aState) {
     case INITIAL:  return "INITIAL";
--- a/netwerk/cache2/CacheIndex.h
+++ b/netwerk/cache2/CacheIndex.h
@@ -718,18 +718,16 @@ 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(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
   // writing process succeeds.
@@ -742,35 +740,30 @@ private:
   // Serializes part of mIndex hashtable to the write buffer a writes the buffer
   // to the file.
   void WriteRecords();
   // Finalizes writing process.
   void FinishWrite(bool aSucceeded);
 
   static PLDHashOperator CopyRecordsToRWBuf(CacheIndexEntry *aEntry,
                                             void* aClosure);
-  static PLDHashOperator ApplyIndexChanges(CacheIndexEntry *aEntry,
-                                           void* aClosure);
 
   // Following methods perform writing of the journal during shutdown. All these
   // methods must be called only during shutdown since they write/delete files
   // directly on the main thread instead of using CacheFileIOManager that does
   // it asynchronously on IO thread. Journal contains only entries that are
   // dirty, i.e. changes that are not present in the index file on the disk.
   // When the log is written successfully, the dirty flag in index file is
   // cleared.
   nsresult GetFile(const nsACString &aName, nsIFile **_retval);
   nsresult RemoveFile(const nsACString &aName);
   void     RemoveIndexFromDisk();
   // Writes journal to the disk and clears dirty flag in index header.
   nsresult WriteLogToDisk();
 
-  static PLDHashOperator WriteEntryToLog(CacheIndexEntry *aEntry,
-                                         void* aClosure);
-
   // Following methods perform reading of the index from the disk.
   //
   // Index is read at startup just after initializing the CacheIndex. There are
   // 3 files used when manipulating with index: index file, journal file and
   // a temporary file. All files contain the hash of the data, so we can check
   // whether the content is valid and complete. Index file contains also a dirty
   // flag in the index header which is unset on a clean shutdown. During opening
   // and reading of the files we determine the status of the whole index from
@@ -815,17 +808,16 @@ private:
   // Merges entries from journal into mIndex.
   void MergeJournal();
   // In debug build this method checks that we have no fresh entry in mIndex
   // after we finish reading index and before we process pending operations.
   void EnsureNoFreshEntry();
   // In debug build this method is called after processing pending operations
   // to make sure mIndexStats contains correct information.
   void EnsureCorrectStats();
-  static PLDHashOperator SumIndexStats(CacheIndexEntry *aEntry, void* aClosure);
   // Finalizes reading process.
   void FinishRead(bool aSucceeded);
 
   static PLDHashOperator ProcessJournalEntry(CacheIndexEntry *aEntry,
                                              void* aClosure);
 
   // Following methods perform updating and building of the index.
   // Timer callback that starts update or build process.
@@ -848,18 +840,17 @@ private:
   void StartUpdatingIndex(bool aRebuild);
   // Iterates through all files in entries directory that we didn't create/open
   // during this session and theirs last modified time is newer than timestamp
   // in the index header. Parses the files and adds the entries to the index.
   void UpdateIndex();
   // Finalizes update or build process.
   void FinishUpdate(bool aSucceeded);
 
-  static PLDHashOperator RemoveNonFreshEntries(CacheIndexEntry *aEntry,
-                                               void* aClosure);
+  void RemoveNonFreshEntries();
 
   enum EState {
     // Initial state in which the index is not usable
     // Possible transitions:
     //  -> READING
     INITIAL  = 0,
 
     // Index is being read from the disk.
--- a/widget/windows/WinIMEHandler.cpp
+++ b/widget/windows/WinIMEHandler.cpp
@@ -700,17 +700,17 @@ IMEHandler::ShowOnScreenKeyboard()
     wchar_t path[MAX_PATH];
     // The path to TabTip.exe is defined at the following registry key.
     // This is pulled out of the 64-bit registry hive directly.
     const wchar_t kRegKeyName[] =
       L"Software\\Classes\\CLSID\\"
       L"{054AAE20-4BEA-4347-8A35-64A533254A9D}\\LocalServer32";
     if (!WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE,
                                   kRegKeyName,
-                                  0,
+                                  nullptr,
                                   path,
                                   sizeof path)) {
       return;
     }
 
     std::wstring wstrpath(path);
     // The path provided by the registry will often contain
     // %CommonProgramFiles%, which will need to be replaced if it is present.
@@ -740,36 +740,37 @@ IMEHandler::ShowOnScreenKeyboard()
       } else {
         PWSTR path = nullptr;
         HRESULT hres =
           WinUtils::SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0,
                                          nullptr, &path);
         if (FAILED(hres) || !path) {
           return;
         }
-        commonProgramFilesPath = nsDependentString(path).get();
+        commonProgramFilesPath =
+          static_cast<const wchar_t*>(nsDependentString(path).get());
         ::CoTaskMemFree(path);
       }
       wstrpath.replace(commonProgramFilesOffset,
                        wcslen(L"%CommonProgramFiles%"),
                        commonProgramFilesPath);
     }
 
     cachedPath.Assign(wstrpath.data());
     Preferences::SetString(kOskPathPrefName, cachedPath);
   }
 
-  LPCWSTR cachedPathPtr;
+  const char16_t *cachedPathPtr;
   cachedPath.GetData(&cachedPathPtr);
-  HINSTANCE ret = ::ShellExecuteW(nullptr,
-                                  L"",
-                                  cachedPathPtr,
-                                  nullptr,
-                                  nullptr,
-                                  SW_SHOW);
+  ShellExecuteW(nullptr,
+                L"",
+                char16ptr_t(cachedPathPtr),
+                nullptr,
+                nullptr,
+                SW_SHOW);
   sShowingOnScreenKeyboard = true;
 }
 
 // Based on DismissVirtualKeyboard() in Chromium's base/win/win_util.cc.
 // static
 void
 IMEHandler::DismissOnScreenKeyboard()
 {