Merge m-c and birch
authorPhil Ringnalda <philringnalda@gmail.com>
Mon, 27 May 2013 18:58:47 -0700
changeset 133076 c190422547ed82c4d4c904baa16deb825f1ea060
parent 133075 1ba392c9103c997e09afaaaab7a1d979559c85df (current diff)
parent 133065 1d6a04be8c0f91865c2fc844c79314450ed580ed (diff)
child 133077 759bbcf59ab1b7784aafdc40fe984413c0193a75
child 133136 26488f5030f13d841ecfbc725e03cb8518f624d1
child 133139 0b13a25593652b3f07a9b16864b93c1d8de487fb
push id28591
push userphilringnalda@gmail.com
push dateTue, 28 May 2013 03:24:50 +0000
treeherdermozilla-inbound@759bbcf59ab1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone24.0a1
first release with
nightly linux32
c190422547ed / 24.0a1 / 20130528030942 / files
nightly linux64
c190422547ed / 24.0a1 / 20130528030942 / files
nightly mac
c190422547ed / 24.0a1 / 20130528030942 / files
nightly win32
c190422547ed / 24.0a1 / 20130528030942 / files
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
Merge m-c and birch
testing/marionette/client/marionette/tests/unit/test_tap.py
testing/marionette/client/marionette/tests/unit/test_touch.py
testing/marionette/client/marionette/www/testTouch.html
xpcom/tests/static-checker/StackAggrInit.cpp
xpcom/tests/static-checker/StackCond.cpp
xpcom/tests/static-checker/StackCondBad.cpp
xpcom/tests/static-checker/StackConditional.cpp
xpcom/tests/static-checker/StackFalsePositive.cpp
xpcom/tests/static-checker/StackNoConstructor.cpp
xpcom/tests/static-checker/StackPlacementNewOK.cpp
xpcom/tests/static-checker/StackVector.cpp
xpcom/tests/static-checker/TestStack.cpp
xpcom/tests/static-checker/TestStackTemplate.cpp
--- a/browser/base/content/test/social/browser_social_chatwindow.js
+++ b/browser/base/content/test/social/browser_social_chatwindow.js
@@ -658,25 +658,26 @@ function resizeWindowToChatAreaWidth(des
   // So just callback now saying the test must be skipped.
   if (window.screen.availWidth - window.outerWidth < delta) {
     info("skipping this as screen available width is less than necessary");
     cb(false);
     return;
   }
   // Otherwise we request resize and expect a resize event
   window.addEventListener("resize", function resize_handler() {
-    window.removeEventListener("resize", resize_handler);
     // we did resize - but did we get far enough to be able to continue?
     let newSize = window.SocialChatBar.chatbar.getBoundingClientRect().width;
     let sizedOk = widthDeltaCloseEnough(newSize - desired);
     if (!sizedOk) {
-      // not an error...
-      info("skipping this as we can't resize chat area to " + desired + " - got " + newSize);
+      return;
     }
-    cb(sizedOk);
+    window.removeEventListener("resize", resize_handler);
+    executeSoon(function() {
+      cb(sizedOk);
+    });
   });
   window.resizeBy(delta, 0);
 }
 
 function resizeAndCheckWidths(first, second, third, checks, cb) {
   if (checks.length == 0) {
     cb(); // nothing more to check!
     return;
--- a/build/clang-plugin/Makefile.in
+++ b/build/clang-plugin/Makefile.in
@@ -11,16 +11,17 @@ VPATH		 := @srcdir@
 NULL :=
 
 CPPSRCS := \
 	clang-plugin.cpp \
 	$(NULL)
 
 TESTSRCS := \
 	TestMustOverride.cpp \
+	TestNonHeapClass.cpp \
 	TestStackClass.cpp \
 	$(NULL)
 
 OBJS := $(patsubst %.cpp,%.o,$(CPPSRCS))
 TESTS := $(patsubst %.cpp,test-%,$(TESTSRCS))
 
 PLUGIN := libclang-plugin.so
 
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -6,16 +6,17 @@
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Basic/Version.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendPluginRegistry.h"
 #include "clang/Frontend/MultiplexConsumer.h"
 #include "clang/Sema/Sema.h"
+#include "llvm/ADT/DenseMap.h"
 
 #define CLANG_VERSION_FULL (CLANG_VERSION_MAJOR * 100 + CLANG_VERSION_MINOR)
 
 using namespace llvm;
 using namespace clang;
 
 namespace {
 
@@ -27,19 +28,27 @@ public:
   ASTConsumer *makeASTConsumer() {
     return astMatcher.newASTConsumer();
   }
 
 private:
   class StackClassChecker : public MatchFinder::MatchCallback {
   public:
     virtual void run(const MatchFinder::MatchResult &Result);
+    void noteInferred(QualType T, DiagnosticsEngine &Diag);
+  };
+
+  class NonHeapClassChecker : public MatchFinder::MatchCallback {
+  public:
+    virtual void run(const MatchFinder::MatchResult &Result);
+    void noteInferred(QualType T, DiagnosticsEngine &Diag);
   };
 
   StackClassChecker stackClassChecker;
+  NonHeapClassChecker nonheapClassChecker;
   MatchFinder astMatcher;
 };
 
 class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> {
   DiagnosticsEngine &Diag;
   const CompilerInstance &CI;
   DiagnosticsMatcher matcher;
 public:
@@ -82,26 +91,16 @@ public:
         continue;
       }
       parent = parent->getDefinition();
       for (CXXRecordDecl::method_iterator M = parent->method_begin();
           M != parent->method_end(); ++M) {
         if (hasCustomAnnotation(*M, "moz_must_override"))
           must_overrides.push_back(*M);
       }
-
-      // While we are looping over parent classes, we'll also check to make sure
-      // that the subclass has the annotation if the superclass does.
-      if (hasCustomAnnotation(parent, "moz_stack_class") &&
-          !hasCustomAnnotation(d, "moz_stack_class")) {
-        unsigned badInheritID = Diag.getDiagnosticIDs()->getCustomDiagID(
-            DiagnosticIDs::Error, "%0 inherits from a stack class %1");
-        Diag.Report(d->getLocation(), badInheritID)
-          << d->getDeclName() << parent->getDeclName();
-      }
     }
 
     for (OverridesVector::iterator it = must_overrides.begin();
         it != must_overrides.end(); ++it) {
       bool overridden = false;
       for (CXXRecordDecl::method_iterator M = d->method_begin();
           !overridden && M != d->method_end(); ++M) {
         // The way that Clang checks if a method M overrides its parent method
@@ -118,79 +117,230 @@ public:
         Diag.Report(d->getLocation(), overrideID) << d->getDeclName() <<
           (*it)->getDeclName();
         Diag.Report((*it)->getLocation(), overrideNote);
       }
     }
     return true;
   }
 };
+
+/**
+ * Where classes may be allocated. Regular classes can be allocated anywhere,
+ * non-heap classes on the stack or as static variables, and stack classes only
+ * on the stack. Note that stack classes subsumes non-heap classes.
+ */
+enum ClassAllocationNature {
+  RegularClass = 0,
+  NonHeapClass = 1,
+  StackClass = 2
+};
+
+/// A cached data of whether classes are stack classes, non-heap classes, or
+/// neither.
+DenseMap<const CXXRecordDecl *,
+  std::pair<const Decl *, ClassAllocationNature> > inferredAllocCauses;
+
+ClassAllocationNature getClassAttrs(QualType T);
+
+ClassAllocationNature getClassAttrs(CXXRecordDecl *D) {
+  // Normalize so that D points to the definition if it exists. If it doesn't,
+  // then we can't allocate it anyways.
+  if (!D->hasDefinition())
+    return RegularClass;
+  D = D->getDefinition();
+  // Base class: anyone with this annotation is obviously a stack class
+  if (MozChecker::hasCustomAnnotation(D, "moz_stack_class"))
+    return StackClass;
+
+  // See if we cached the result.
+  DenseMap<const CXXRecordDecl *,
+    std::pair<const Decl *, ClassAllocationNature> >::iterator it =
+    inferredAllocCauses.find(D);
+  if (it != inferredAllocCauses.end()) {
+    return it->second.second;
+  }
+
+  // Continue looking, we might be a stack class yet. Even if we're a nonheap
+  // class, it might be possible that we've inferred to be a stack class.
+  ClassAllocationNature type = RegularClass;
+  if (MozChecker::hasCustomAnnotation(D, "moz_nonheap_class")) {
+    type = NonHeapClass;
+  }
+  inferredAllocCauses.insert(std::make_pair(D,
+    std::make_pair((const Decl *)0, type)));
+
+  // Look through all base cases to figure out if the parent is a stack class or
+  // a non-heap class. Since we might later infer to also be a stack class, keep
+  // going.
+  for (CXXRecordDecl::base_class_iterator base = D->bases_begin(),
+       e = D->bases_end(); base != e; ++base) {
+    ClassAllocationNature super = getClassAttrs(base->getType());
+    if (super == StackClass) {
+      inferredAllocCauses[D] = std::make_pair(
+        base->getType()->getAsCXXRecordDecl(), StackClass);
+      return StackClass;
+    } else if (super == NonHeapClass) {
+      inferredAllocCauses[D] = std::make_pair(
+        base->getType()->getAsCXXRecordDecl(), NonHeapClass);
+      type = NonHeapClass;
+    }
+  }
+
+  // Maybe it has a member which is a stack class.
+  for (RecordDecl::field_iterator field = D->field_begin(), e = D->field_end();
+       field != e; ++field) {
+    ClassAllocationNature fieldType = getClassAttrs(field->getType());
+    if (fieldType == StackClass) {
+      inferredAllocCauses[D] = std::make_pair(*field, StackClass);
+      return StackClass;
+    } else if (fieldType == NonHeapClass) {
+      inferredAllocCauses[D] = std::make_pair(*field, NonHeapClass);
+      type = NonHeapClass;
+    }
+  }
+
+  return type;
+}
+
+ClassAllocationNature getClassAttrs(QualType T) {
+  while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
+    T = arrTy->getElementType();
+  CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
+  return clazz ? getClassAttrs(clazz) : RegularClass;
+}
+
 }
 
 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) {
-  QualType t = Node;
-  while (const ArrayType *arrTy = t->getAsArrayTypeUnsafe())
-    t = arrTy->getElementType();
-  CXXRecordDecl *clazz = t->getAsCXXRecordDecl();
-  return clazz && MozChecker::hasCustomAnnotation(clazz, "moz_stack_class");
+  return getClassAttrs(Node) == StackClass;
+}
+
+/// This matcher will match any class with the stack class assertion or an
+/// array of such classes.
+AST_MATCHER(QualType, nonheapClassAggregate) {
+  return getClassAttrs(Node) == NonHeapClass;
 }
 }
 }
 
 namespace {
 
 DiagnosticsMatcher::DiagnosticsMatcher() {
   // 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);
-  // Stack class assertion: a stack-class field is permitted only if it's a
-  // member of a class with the annotation
-  astMatcher.addMatcher(fieldDecl(hasType(stackClassAggregate())).bind("field"),
-    &stackClassChecker);
+  // Non-heap class assertion: new non-heap class is forbidden (unless placement
+  // new)
+  astMatcher.addMatcher(newExpr(hasType(pointerType(
+      pointee(nonheapClassAggregate())
+    ))).bind("node"), &nonheapClassChecker);
 }
 
 void DiagnosticsMatcher::StackClassChecker::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");
   if (const VarDecl *d = Result.Nodes.getNodeAs<VarDecl>("node")) {
     // Ignore the match if it's a local variable.
     if (d->hasLocalStorage())
       return;
+
     Diag.Report(d->getLocation(), stackID) << d->getType();
+    noteInferred(d->getType(), Diag);
   } else if (const CXXNewExpr *expr =
       Result.Nodes.getNodeAs<CXXNewExpr>("node")) {
     // If it's placement new, then this match doesn't count.
     if (expr->getNumPlacementArgs() > 0)
       return;
     Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
-  } else if (const FieldDecl *field =
-      Result.Nodes.getNodeAs<FieldDecl>("field")) {
-    // AST matchers don't let me get the class that contains a field...
-    const RecordDecl *parent = field->getParent();
-    if (!MozChecker::hasCustomAnnotation(parent, "moz_stack_class")) {
-      // We use a more verbose error message here.
-      unsigned stackID = Diag.getDiagnosticIDs()->getCustomDiagID(
-        DiagnosticIDs::Error,
-        "member of type %0 in class %1 that is not a stack class");
-      Diag.Report(field->getLocation(), stackID) << field->getType() <<
-        parent->getDeclName();
-    }
+    noteInferred(expr->getAllocatedType(), Diag);
+  }
+}
+
+void DiagnosticsMatcher::StackClassChecker::noteInferred(QualType T,
+    DiagnosticsEngine &Diag) {
+  unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
+    DiagnosticIDs::Note,
+    "%0 is a stack class because it inherits from a stack class %1");
+  unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID(
+    DiagnosticIDs::Note,
+    "%0 is a stack class because member %1 is a stack class %2");
+
+  // Find the CXXRecordDecl that is the stack class of interest
+  while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
+    T = arrTy->getElementType();
+  CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
+
+  // Direct result, we're done.
+  if (MozChecker::hasCustomAnnotation(clazz, "moz_stack_class"))
+    return;
+
+  const Decl *cause = inferredAllocCauses[clazz].first;
+  if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) {
+    Diag.Report(clazz->getLocation(), inheritsID) << T << CRD->getDeclName();
+  } else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) {
+    Diag.Report(FD->getLocation(), memberID) << T << FD << FD->getType();
   }
+  
+  // Recursively follow this back.
+  noteInferred(cast<ValueDecl>(cause)->getType(), Diag);
+}
+
+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");
+  const CXXNewExpr *expr = Result.Nodes.getNodeAs<CXXNewExpr>("node");
+  // If it's placement new, then this match doesn't count.
+  if (expr->getNumPlacementArgs() > 0)
+    return;
+  Diag.Report(expr->getStartLoc(), stackID) << expr->getAllocatedType();
+  noteInferred(expr->getAllocatedType(), Diag);
+}
+
+void DiagnosticsMatcher::NonHeapClassChecker::noteInferred(QualType T,
+    DiagnosticsEngine &Diag) {
+  unsigned inheritsID = Diag.getDiagnosticIDs()->getCustomDiagID(
+    DiagnosticIDs::Note,
+    "%0 is a non-heap class because it inherits from a non-heap class %1");
+  unsigned memberID = Diag.getDiagnosticIDs()->getCustomDiagID(
+    DiagnosticIDs::Note,
+    "%0 is a non-heap class because member %1 is a non-heap class %2");
+
+  // Find the CXXRecordDecl that is the stack class of interest
+  while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
+    T = arrTy->getElementType();
+  CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
+
+  // Direct result, we're done.
+  if (MozChecker::hasCustomAnnotation(clazz, "moz_nonheap_class"))
+    return;
+
+  const Decl *cause = inferredAllocCauses[clazz].first;
+  if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(cause)) {
+    Diag.Report(clazz->getLocation(), inheritsID) << T << CRD->getDeclName();
+  } else if (const FieldDecl *FD = dyn_cast<FieldDecl>(cause)) {
+    Diag.Report(FD->getLocation(), memberID) << T << FD << FD->getType();
+  }
+  
+  // Recursively follow this back.
+  noteInferred(cast<ValueDecl>(cause)->getType(), Diag);
 }
 
 class MozCheckAction : public PluginASTAction {
 public:
   ASTConsumer *CreateASTConsumer(CompilerInstance &CI, StringRef fileName) {
     MozChecker *checker = new MozChecker(CI);
 
     ASTConsumer *consumers[] = { checker, checker->getOtherConsumer() };
copy from build/clang-plugin/tests/TestStackClass.cpp
copy to build/clang-plugin/tests/TestNonHeapClass.cpp
--- a/build/clang-plugin/tests/TestStackClass.cpp
+++ b/build/clang-plugin/tests/TestNonHeapClass.cpp
@@ -1,47 +1,62 @@
+#define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class")))
 #define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
 #include <stddef.h>
 
-struct MOZ_STACK_CLASS Stack {
+struct MOZ_NONHEAP_CLASS NonHeap {
   int i;
   void *operator new(size_t x) { return 0; }
   void *operator new(size_t blah, char *buffer) { return buffer; }
 };
 
 template <class T>
-struct MOZ_STACK_CLASS TemplateClass {
+struct MOZ_NONHEAP_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}}
+void misuseNonHeapClass(int len) {
+  NonHeap valid;
+  NonHeap alsoValid[2];
+  static NonHeap validStatic;
+  static NonHeap alsoValidStatic[2];
 
   gobble(&valid);
-  gobble(&notValid);
+  gobble(&validStatic);
   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 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}}
 
-  char buffer[sizeof(Stack)];
-  gobble(new (buffer) Stack);
+  char buffer[sizeof(NonHeap)];
+  gobble(new (buffer) NonHeap);
 }
 
-Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
+NonHeap validStatic;
 struct RandomClass {
-  Stack nonstaticMember; // expected-error {{member of type 'Stack' in class 'RandomClass' that is not a stack class}}
-  static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
+  NonHeap nonstaticMember; // expected-note {{'RandomClass' is a non-heap class because member 'nonstaticMember' is a non-heap class 'NonHeap'}}
+  static NonHeap staticMember;
 };
-struct MOZ_STACK_CLASS RandomStackClass {
-  Stack nonstaticMember;
-  static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
+struct MOZ_NONHEAP_CLASS RandomNonHeapClass {
+  NonHeap nonstaticMember;
+  static NonHeap staticMember;
 };
 
-struct BadInherit : Stack {}; // expected-error {{'BadInherit' inherits from a stack class 'Stack'}}
-struct MOZ_STACK_CLASS GoodInherit : Stack {};
+struct BadInherit : NonHeap {}; // expected-note {{'BadInherit' is a non-heap class because it inherits from a non-heap class '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}}
+}
+
+// Stack class overrides non-heap classes.
+struct MOZ_STACK_CLASS StackClass {};
+struct MOZ_NONHEAP_CLASS InferredStackClass : GoodInherit {
+  NonHeap nonstaticMember;
+  StackClass stackClass; // expected-note {{'InferredStackClass' is a stack class because member 'stackClass' is a stack class 'StackClass'}}
+};
+
+InferredStackClass global; // expected-error {{variable of type 'InferredStackClass' only valid on the stack}}
--- a/build/clang-plugin/tests/TestStackClass.cpp
+++ b/build/clang-plugin/tests/TestStackClass.cpp
@@ -30,18 +30,21 @@ void misuseStackClass(int len) {
   gobble(len <= 5 ? &valid : new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}}
 
   char buffer[sizeof(Stack)];
   gobble(new (buffer) Stack);
 }
 
 Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}}
 struct RandomClass {
-  Stack nonstaticMember; // expected-error {{member of type 'Stack' in class 'RandomClass' that is not a stack class}}
+  Stack nonstaticMember; // expected-note {{'RandomClass' is a stack class because member 'nonstaticMember' is a stack class 'Stack'}}
   static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
 };
 struct MOZ_STACK_CLASS RandomStackClass {
   Stack nonstaticMember;
   static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}}
 };
 
-struct BadInherit : Stack {}; // expected-error {{'BadInherit' inherits from a stack class 'Stack'}}
+struct BadInherit : Stack {}; // expected-note {{'BadInherit' is a stack class because it inherits from a stack class '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}}
--- a/configure.in
+++ b/configure.in
@@ -9342,30 +9342,40 @@ if test -z "$MOZ_NATIVE_NSPR"; then
     fi
     if test -n "$HAVE_64BIT_OS"; then
         ac_configure_args="$ac_configure_args --enable-64bit"
     fi
     if test -n "$USE_ARM_KUSER"; then
         ac_configure_args="$ac_configure_args --with-arm-kuser"
     fi
     ac_configure_args="$ac_configure_args $NSPR_CONFIGURE_ARGS"
+
+    # Save these, so we can mess with them for the subconfigure ..
+    _SAVE_CFLAGS="$CFLAGS"
+    _SAVE_CPPFLAGS="$CPPFLAGS"
+    _SAVE_LDFLAGS="$LDFLAGS"
+
     if test -n "$MOZ_LINKER" -a "$ac_cv_func_dladdr" = no ; then
       # dladdr is supported by the new linker, even when the system linker doesn't
       # support it. Trick nspr into using dladdr when it's not supported.
-      _SAVE_CPPFLAGS="$CPPFLAGS"
       export CPPFLAGS="-include $_topsrcdir/mozglue/linker/dladdr.h $CPPFLAGS"
     fi
-    _SAVE_LDFLAGS="$LDFLAGS"
     export LDFLAGS="$LDFLAGS $NSPR_LDFLAGS"
+    export CFLAGS="$CFLAGS $MOZ_FRAMEPTR_FLAGS"
+
     AC_OUTPUT_SUBDIRS(nsprpub)
+
+    # .. and restore them
+    unset CFLAGS
+    unset CPPFLAGS
+    unset LDFLAGS
+    CFLAGS="$_SAVE_CFLAGS"
+    CPPFLAGS="$_SAVE_CPPFLAGS"
     LDFLAGS="$_SAVE_LDFLAGS"
-    if test -n "$MOZ_LINKER" -a "$ac_cv_func_dladdr" = no; then
-      unset CPPFLAGS
-      CPPFLAGS="$_SAVE_CFLAGS"
-    fi
+
     ac_configure_args="$_SUBDIR_CONFIG_ARGS"
 fi
 
 dnl ========================================================
 dnl = Setup a nice relatively clean build environment for
 dnl = sub-configures.
 dnl ========================================================
 CC="$_SUBDIR_CC"
--- a/content/base/public/nsDeprecatedOperationList.h
+++ b/content/base/public/nsDeprecatedOperationList.h
@@ -25,8 +25,9 @@ DEPRECATED_OPERATION(DOMExceptionCode)
 DEPRECATED_OPERATION(NoExposedProps)
 DEPRECATED_OPERATION(MutationEvent)
 DEPRECATED_OPERATION(MozSlice)
 DEPRECATED_OPERATION(Components)
 DEPRECATED_OPERATION(PrefixedVisibilityAPI)
 DEPRECATED_OPERATION(NodeIteratorDetach)
 DEPRECATED_OPERATION(MozAudioData)
 DEPRECATED_OPERATION(LenientThis)
+DEPRECATED_OPERATION(GetPreventDefault)
--- a/content/base/src/nsContentIterator.cpp
+++ b/content/base/src/nsContentIterator.cpp
@@ -1139,22 +1139,18 @@ protected:
 };
 
 NS_IMPL_ADDREF_INHERITED(nsContentSubtreeIterator, nsContentIterator)
 NS_IMPL_RELEASE_INHERITED(nsContentSubtreeIterator, nsContentIterator)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsContentSubtreeIterator)
 NS_INTERFACE_MAP_END_INHERITING(nsContentIterator)
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsContentSubtreeIterator, nsContentIterator)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRange)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsContentSubtreeIterator, nsContentIterator)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRange)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(nsContentSubtreeIterator, nsContentIterator,
+                                     mRange)
 
 
 
 
 /******************************************************
  * repository cruft
  ******************************************************/
 
--- a/content/events/src/nsDOMDeviceMotionEvent.cpp
+++ b/content/events/src/nsDOMDeviceMotionEvent.cpp
@@ -1,29 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDOMDeviceMotionEvent.h"
 #include "nsDOMClassInfoID.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMDeviceMotionEvent, nsDOMEvent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAcceleration)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccelerationIncludingGravity)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRotationRate)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMDeviceMotionEvent, nsDOMEvent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAcceleration)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccelerationIncludingGravity)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRotationRate)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_3(nsDOMDeviceMotionEvent, nsDOMEvent,
+                                     mAcceleration,
+                                     mAccelerationIncludingGravity,
+                                     mRotationRate)
 
 NS_IMPL_ADDREF_INHERITED(nsDOMDeviceMotionEvent, nsDOMEvent)
 NS_IMPL_RELEASE_INHERITED(nsDOMDeviceMotionEvent, nsDOMEvent)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMDeviceMotionEvent)
   NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceMotionEvent)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
 
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -1215,16 +1215,27 @@ const char* nsDOMEvent::GetEventName(uin
   // XXXldb We can hit this case for nsEvent objects that we didn't
   // create and that are not user defined events since this function and
   // SetEventType are incomplete.  (But fixing that requires fixing the
   // arrays in nsEventListenerManager too, since the events for which
   // this is a problem generally *are* created by nsDOMEvent.)
   return nullptr;
 }
 
+bool
+nsDOMEvent::GetPreventDefault() const
+{
+  if (mOwner) {
+    if (nsIDocument* doc = mOwner->GetExtantDoc()) {
+      doc->WarnOnceAbout(nsIDocument::eGetPreventDefault);
+    }
+  }
+  return DefaultPrevented();
+}
+
 NS_IMETHODIMP
 nsDOMEvent::GetPreventDefault(bool* aReturn)
 {
   NS_ENSURE_ARG_POINTER(aReturn);
   *aReturn = GetPreventDefault();
   return NS_OK;
 }
 
--- a/content/events/src/nsDOMEvent.h
+++ b/content/events/src/nsDOMEvent.h
@@ -184,20 +184,17 @@ public:
                  mozilla::ErrorResult& aRv)
   {
     aRv = InitEvent(aType, aBubbles, aCancelable);
   }
 
   mozilla::dom::EventTarget* GetOriginalTarget() const;
   mozilla::dom::EventTarget* GetExplicitOriginalTarget() const;
 
-  bool GetPreventDefault() const
-  {
-    return DefaultPrevented();
-  }
+  bool GetPreventDefault() const;
 
 protected:
 
   // Internal helper functions
   void SetEventType(const nsAString& aEventTypeArg);
   already_AddRefed<nsIContent> GetTargetFromFrame();
 
   nsEvent*                    mEvent;
--- a/content/events/src/nsDOMTouchEvent.cpp
+++ b/content/events/src/nsDOMTouchEvent.cpp
@@ -1,9 +1,10 @@
-/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDOMTouchEvent.h"
 #include "nsGUIEvent.h"
 #include "nsContentUtils.h"
 #include "mozilla/Preferences.h"
@@ -89,27 +90,20 @@ nsDOMTouchEvent::nsDOMTouchEvent(mozilla
 nsDOMTouchEvent::~nsDOMTouchEvent()
 {
   if (mEventIsInternal && mEvent) {
     delete static_cast<nsTouchEvent*>(mEvent);
     mEvent = nullptr;
   }
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMTouchEvent, nsDOMUIEvent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTouches)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTargetTouches)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChangedTouches)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMTouchEvent, nsDOMUIEvent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTouches)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTargetTouches)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChangedTouches)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_3(nsDOMTouchEvent, nsDOMUIEvent,
+                                     mTouches,
+                                     mTargetTouches,
+                                     mChangedTouches)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMTouchEvent)
   NS_INTERFACE_MAP_ENTRY(nsIDOMTouchEvent)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMUIEvent)
 
 NS_IMPL_ADDREF_INHERITED(nsDOMTouchEvent, nsDOMUIEvent)
 NS_IMPL_RELEASE_INHERITED(nsDOMTouchEvent, nsDOMUIEvent)
 
--- a/content/events/src/nsDOMUIEvent.cpp
+++ b/content/events/src/nsDOMUIEvent.cpp
@@ -86,23 +86,18 @@ nsDOMUIEvent::Constructor(const mozilla:
   e->SetIsDOMBinding();
   bool trusted = e->Init(t);
   aRv = e->InitUIEvent(aType, aParam.mBubbles, aParam.mCancelable, aParam.mView,
                        aParam.mDetail);
   e->SetTrusted(trusted);
   return e.forget();
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMUIEvent, nsDOMEvent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mView)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMUIEvent, nsDOMEvent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mView)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(nsDOMUIEvent, nsDOMEvent,
+                                     mView)
 
 NS_IMPL_ADDREF_INHERITED(nsDOMUIEvent, nsDOMEvent)
 NS_IMPL_RELEASE_INHERITED(nsDOMUIEvent, nsDOMEvent)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMUIEvent)
   NS_INTERFACE_MAP_ENTRY(nsIDOMUIEvent)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
 
--- a/content/events/src/nsDOMXULCommandEvent.cpp
+++ b/content/events/src/nsDOMXULCommandEvent.cpp
@@ -20,25 +20,18 @@ nsDOMXULCommandEvent::nsDOMXULCommandEve
     mEvent->time = PR_Now();
   }
   SetIsDOMBinding();
 }
 
 NS_IMPL_ADDREF_INHERITED(nsDOMXULCommandEvent, nsDOMUIEvent)
 NS_IMPL_RELEASE_INHERITED(nsDOMXULCommandEvent, nsDOMUIEvent)
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMXULCommandEvent,
-                                                nsDOMUIEvent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceEvent)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMXULCommandEvent,
-                                                  nsDOMUIEvent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceEvent)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(nsDOMXULCommandEvent, nsDOMUIEvent,
+                                     mSourceEvent)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMXULCommandEvent)
   NS_INTERFACE_MAP_ENTRY(nsIDOMXULCommandEvent)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMUIEvent)
 
 NS_IMETHODIMP
 nsDOMXULCommandEvent::GetAltKey(bool* aIsDown)
 {
--- a/content/html/content/src/HTMLButtonElement.cpp
+++ b/content/html/content/src/HTMLButtonElement.cpp
@@ -70,24 +70,18 @@ HTMLButtonElement::HTMLButtonElement(alr
 }
 
 HTMLButtonElement::~HTMLButtonElement()
 {
 }
 
 // nsISupports
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLButtonElement,
-                                                  nsGenericHTMLFormElement)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLButtonElement,
-                                                nsGenericHTMLFormElement)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(HTMLButtonElement, nsGenericHTMLFormElement,
+                                     mValidity)
 
 NS_IMPL_ADDREF_INHERITED(HTMLButtonElement, Element)
 NS_IMPL_RELEASE_INHERITED(HTMLButtonElement, Element)
 
 
 // QueryInterface implementation for HTMLButtonElement
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLButtonElement)
   NS_HTML_CONTENT_INTERFACE_TABLE2(HTMLButtonElement,
--- a/content/html/content/src/HTMLDataListElement.cpp
+++ b/content/html/content/src/HTMLDataListElement.cpp
@@ -16,25 +16,18 @@ HTMLDataListElement::~HTMLDataListElemen
 }
 
 JSObject*
 HTMLDataListElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aScope)
 {
   return HTMLDataListElementBinding::Wrap(aCx, aScope, this);
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLDataListElement,
-                                                nsGenericHTMLElement)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOptions)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLDataListElement,
-                                                  nsGenericHTMLElement)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOptions)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(HTMLDataListElement, nsGenericHTMLElement,
+                                     mOptions)
 
 NS_IMPL_ADDREF_INHERITED(HTMLDataListElement, Element)
 NS_IMPL_RELEASE_INHERITED(HTMLDataListElement, Element)
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLDataListElement)
   NS_HTML_CONTENT_INTERFACE_TABLE1(HTMLDataListElement,
                                    nsIDOMHTMLDataListElement)
   NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(HTMLDataListElement,
--- a/content/html/content/src/HTMLFieldSetElement.cpp
+++ b/content/html/content/src/HTMLFieldSetElement.cpp
@@ -32,27 +32,18 @@ HTMLFieldSetElement::~HTMLFieldSetElemen
   uint32_t length = mDependentElements.Length();
   for (uint32_t i = 0; i < length; ++i) {
     mDependentElements[i]->ForgetFieldSet(this);
   }
 }
 
 // nsISupports
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLFieldSetElement,
-                                                nsGenericHTMLFormElement)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLFieldSetElement,
-                                                  nsGenericHTMLFormElement)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_2(HTMLFieldSetElement, nsGenericHTMLFormElement,
+                                     mValidity, mElements)
 
 NS_IMPL_ADDREF_INHERITED(HTMLFieldSetElement, Element)
 NS_IMPL_RELEASE_INHERITED(HTMLFieldSetElement, Element)
 
 // QueryInterface implementation for HTMLFieldSetElement
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLFieldSetElement)
   NS_HTML_CONTENT_INTERFACE_TABLE2(HTMLFieldSetElement,
                                    nsIDOMHTMLFieldSetElement,
--- a/content/html/content/src/HTMLTextAreaElement.cpp
+++ b/content/html/content/src/HTMLTextAreaElement.cpp
@@ -71,28 +71,20 @@ HTMLTextAreaElement::HTMLTextAreaElement
   AddStatesSilently(NS_EVENT_STATE_ENABLED |
                     NS_EVENT_STATE_OPTIONAL |
                     NS_EVENT_STATE_VALID);
 
   SetIsDOMBinding();
 }
 
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLTextAreaElement,
-                                                nsGenericHTMLFormElement)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
-  tmp->mState.Unlink();
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLTextAreaElement,
-                                                  nsGenericHTMLFormElement)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mValidity)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
-  tmp->mState.Traverse(cb);
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_3(HTMLTextAreaElement, nsGenericHTMLFormElement,
+                                     mValidity,
+                                     mControllers,
+                                     mState)
 
 NS_IMPL_ADDREF_INHERITED(HTMLTextAreaElement, Element)
 NS_IMPL_RELEASE_INHERITED(HTMLTextAreaElement, Element)
 
 
 // QueryInterface implementation for HTMLTextAreaElement
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLTextAreaElement)
   NS_HTML_CONTENT_INTERFACE_TABLE5(HTMLTextAreaElement,
--- a/content/html/content/src/nsTextEditorState.h
+++ b/content/html/content/src/nsTextEditorState.h
@@ -279,9 +279,24 @@ private:
   bool mInitializing; // Whether we're in the process of initialization
   bool mValueTransferInProgress; // Whether a value is being transferred to the frame
   bool mSelectionCached; // Whether mSelectionProperties is valid
   mutable bool mSelectionRestoreEagerInit; // Whether we're eager initing because of selection restore
   SelectionProperties mSelectionProperties;
   bool mPlaceholderVisibility;
 };
 
+inline void
+ImplCycleCollectionUnlink(nsTextEditorState& aField)
+{
+  aField.Unlink();
+}
+
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+                            nsTextEditorState& aField,
+                            const char* aName,
+                            uint32_t aFlags = 0)
+{
+  aField.Traverse(aCallback);
+}
+
 #endif
--- a/content/html/document/src/ImageDocument.cpp
+++ b/content/html/document/src/ImageDocument.cpp
@@ -133,23 +133,18 @@ ImageDocument::ImageDocument()
   // bother initializing members to 0.
 }
 
 ImageDocument::~ImageDocument()
 {
 }
 
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ImageDocument, MediaDocument)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageContent)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ImageDocument, MediaDocument)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageContent)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(ImageDocument, MediaDocument,
+                                     mImageContent)
 
 NS_IMPL_ADDREF_INHERITED(ImageDocument, MediaDocument)
 NS_IMPL_RELEASE_INHERITED(ImageDocument, MediaDocument)
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(ImageDocument)
   NS_HTML_DOCUMENT_INTERFACE_TABLE_BEGIN(ImageDocument)
     NS_INTERFACE_TABLE_ENTRY(ImageDocument, nsIImageDocument)
     NS_INTERFACE_TABLE_ENTRY(ImageDocument, imgINotificationObserver)
--- a/content/html/document/src/PluginDocument.cpp
+++ b/content/html/document/src/PluginDocument.cpp
@@ -104,23 +104,18 @@ PluginStreamListener::OnStartRequest(nsI
 
 PluginDocument::PluginDocument()
 {}
 
 PluginDocument::~PluginDocument()
 {}
 
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PluginDocument, MediaDocument)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPluginContent)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PluginDocument, MediaDocument)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPluginContent)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(PluginDocument, MediaDocument,
+                                     mPluginContent)
 
 NS_IMPL_ADDREF_INHERITED(PluginDocument, MediaDocument)
 NS_IMPL_RELEASE_INHERITED(PluginDocument, MediaDocument)
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(PluginDocument)
   NS_INTERFACE_TABLE_INHERITED1(PluginDocument, nsIPluginDocument)
 NS_INTERFACE_TABLE_TAIL_INHERITING(MediaDocument)
 
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -197,43 +197,27 @@ nsHTMLDocument::nsHTMLDocument()
   mIsRegularHTML = true;
   mDefaultElementType = kNameSpaceID_XHTML;
   mCompatMode = eCompatibility_NavQuirks;
 
   SetIsDOMBinding();
 }
 
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLDocument, nsDocument)
-  NS_ASSERTION(!nsCCUncollectableMarker::InGeneration(cb, tmp->GetMarkedCCGeneration()),
-               "Shouldn't traverse nsHTMLDocument!");
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImages)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplets)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEmbeds)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLinks)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchors)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScripts)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mForms)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFormControls)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWyciwygChannel)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMidasCommandManager)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLDocument, nsDocument)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mImages)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplets)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEmbeds)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mLinks)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchors)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mScripts)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mForms)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFormControls)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWyciwygChannel)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMidasCommandManager)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_10(nsHTMLDocument, nsDocument,
+                                      mImages,
+                                      mApplets,
+                                      mEmbeds,
+                                      mLinks,
+                                      mAnchors,
+                                      mScripts,
+                                      mForms,
+                                      mFormControls,
+                                      mWyciwygChannel,
+                                      mMidasCommandManager)
 
 NS_IMPL_ADDREF_INHERITED(nsHTMLDocument, nsDocument)
 NS_IMPL_RELEASE_INHERITED(nsHTMLDocument, nsDocument)
 
 // QueryInterface implementation for nsHTMLDocument
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLDocument)
   NS_DOCUMENT_INTERFACE_TABLE_BEGIN(nsHTMLDocument)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLDocument, nsIHTMLDocument)
--- a/content/smil/nsDOMTimeEvent.cpp
+++ b/content/smil/nsDOMTimeEvent.cpp
@@ -35,23 +35,18 @@ nsDOMTimeEvent::nsDOMTimeEvent(mozilla::
       nsCOMPtr<nsIDOMWindow> window = do_GetInterface(container);
       if (window) {
         mView = do_QueryInterface(window);
       }
     }
   }
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMTimeEvent, nsDOMEvent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mView)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMTimeEvent, nsDOMEvent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mView)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(nsDOMTimeEvent, nsDOMEvent,
+                                     mView)
 
 NS_IMPL_ADDREF_INHERITED(nsDOMTimeEvent, nsDOMEvent)
 NS_IMPL_RELEASE_INHERITED(nsDOMTimeEvent, nsDOMEvent)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMTimeEvent)
   NS_INTERFACE_MAP_ENTRY(nsIDOMTimeEvent)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
 
--- a/content/svg/content/src/SVGSVGElement.cpp
+++ b/content/svg/content/src/SVGSVGElement.cpp
@@ -46,25 +46,18 @@ namespace dom {
 class SVGAnimatedLength;
 
 JSObject*
 SVGSVGElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aScope)
 {
   return SVGSVGElementBinding::Wrap(aCx, aScope, this);
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMSVGTranslatePoint,
-                                                nsISVGPoint)
-NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMSVGTranslatePoint,
-                                                  nsISVGPoint)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(DOMSVGTranslatePoint, nsISVGPoint,
+                                     mElement)
 
 NS_IMPL_ADDREF_INHERITED(DOMSVGTranslatePoint, nsISVGPoint)
 NS_IMPL_RELEASE_INHERITED(DOMSVGTranslatePoint, nsISVGPoint)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGTranslatePoint)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   // We have to qualify nsISVGPoint because NS_GET_IID looks for a class in the
   // global namespace
--- a/content/svg/content/src/SVGSwitchElement.cpp
+++ b/content/svg/content/src/SVGSwitchElement.cpp
@@ -21,24 +21,18 @@ JSObject*
 SVGSwitchElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aScope)
 {
   return SVGSwitchElementBinding::Wrap(aCx, aScope, this);
 }
 
 //----------------------------------------------------------------------
 // nsISupports methods
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGSwitchElement,
-                                                  SVGSwitchElementBase)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActiveChild)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGSwitchElement,
-                                                SVGSwitchElementBase)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveChild)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(SVGSwitchElement, SVGSwitchElementBase,
+                                     mActiveChild)
 
 NS_IMPL_ADDREF_INHERITED(SVGSwitchElement,SVGSwitchElementBase)
 NS_IMPL_RELEASE_INHERITED(SVGSwitchElement,SVGSwitchElementBase)
 
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGSwitchElement)
   NS_NODE_INTERFACE_TABLE3(SVGSwitchElement, nsIDOMNode, nsIDOMElement,
                            nsIDOMSVGElement)
 NS_INTERFACE_MAP_END_INHERITING(SVGSwitchElementBase)
--- a/content/xul/document/src/XULDocument.cpp
+++ b/content/xul/document/src/XULDocument.cpp
@@ -319,27 +319,21 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
                  "Shouldn't traverse XULDocument!");
     // XXX tmp->mForwardReferences?
     // XXX tmp->mContextStack?
 
     // An element will only have a template builder as long as it's in the
     // document, so we'll traverse the table here instead of from the element.
     if (tmp->mTemplateBuilderTable)
         tmp->mTemplateBuilderTable->EnumerateRead(TraverseTemplateBuilders, &cb);
-        
+
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPrototype)
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMasterPrototype)
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher)
-
-    uint32_t i, count = tmp->mPrototypes.Length();
-    for (i = 0; i < count; ++i) {
-        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mPrototypes[i]");
-        cb.NoteXPCOMChild(static_cast<nsIScriptGlobalObjectOwner*>(tmp->mPrototypes[i]));
-    }
-
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypes);
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStore)
 
     if (tmp->mOverlayLoadObservers.IsInitialized())
         tmp->mOverlayLoadObservers.EnumerateRead(TraverseObservers, &cb);
     if (tmp->mPendingOverlayLoadNotifications.IsInitialized())
         tmp->mPendingOverlayLoadNotifications.EnumerateRead(TraverseObservers, &cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
--- a/content/xul/templates/src/nsXULTreeBuilder.cpp
+++ b/content/xul/templates/src/nsXULTreeBuilder.cpp
@@ -268,29 +268,21 @@ NS_NewXULTreeBuilder(nsISupports* aOuter
 
     NS_RELEASE(result);
     return rv;
 }
 
 NS_IMPL_ADDREF_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
 NS_IMPL_RELEASE_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
-    NS_IMPL_CYCLE_COLLECTION_UNLINK(mBoxObject)
-    NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection)
-    NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersistStateStore)
-    NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
-    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBoxObject)
-    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
-    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersistStateStore)
-    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservers)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_4(nsXULTreeBuilder, nsXULTemplateBuilder,
+                                     mBoxObject,
+                                     mSelection,
+                                     mPersistStateStore,
+                                     mObservers)
 
 DOMCI_DATA(XULTreeBuilder, nsXULTreeBuilder)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder)
     NS_INTERFACE_MAP_ENTRY(nsIXULTreeBuilder)
     NS_INTERFACE_MAP_ENTRY(nsITreeView)
     NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULTreeBuilder)
 NS_INTERFACE_MAP_END_INHERITING(nsXULTemplateBuilder)
--- a/dom/base/DOMCursor.cpp
+++ b/dom/base/DOMCursor.cpp
@@ -6,25 +6,18 @@
 
 #include "DOMCursor.h"
 #include "nsError.h"
 #include "mozilla/dom/DOMCursorBinding.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMCursor,
-                                                  DOMRequest)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMCursor,
-                                                DOMRequest)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(DOMCursor, DOMRequest,
+                                     mCallback)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMCursor)
   NS_INTERFACE_MAP_ENTRY(nsIDOMDOMCursor)
 NS_INTERFACE_MAP_END_INHERITING(DOMRequest)
 
 NS_IMPL_ADDREF_INHERITED(DOMCursor, DOMRequest)
 NS_IMPL_RELEASE_INHERITED(DOMCursor, DOMRequest)
 
--- a/dom/browser-element/mochitest/browserElement_SendEvent.js
+++ b/dom/browser-element/mochitest/browserElement_SendEvent.js
@@ -34,32 +34,34 @@ function runTest() {
         ok(true, "Receive a mouseup event.");
         break;
       case "#click":
         ok(true, "Receive a click event.");
         if (SpecialPowers.getIntPref("dom.w3c_touch_events.enabled") != 0) {
           iframe.sendTouchEvent("touchstart", [1], [10], [10], [2], [2],
                                 [20], [0.5], 1, 0);
         } else {
+          iframe.removeEventListener('mozbrowserlocationchange', onlocchange);
           SimpleTest.finish();
         }
         break;
       case "#touchstart":
         ok(true, "Receive a touchstart event.");
         iframe.sendTouchEvent("touchmove", [1], [10], [10], [2], [2],
                               [20], [0.5], 1, 0);
       case "#touchmove":
         ok(true, "Receive a touchmove event.");
         iframe.sendTouchEvent("touchend", [1], [10], [10], [2], [2],
                               [20], [0.5], 1, 0);
         break;
       case "#touchend":
         ok(true, "Receive a touchend event.");
         iframe.sendTouchEvent("touchcancel", [1], [10], [10], [2], [2],
                               [20], [0.5], 1, 0);
+        iframe.removeEventListener('mozbrowserlocationchange', onlocchange);
         SimpleTest.finish();
         break;
     }
   });
 
   iframe.src = "data:text/html,<html><body>" +
                "<button>send[Mouse|Touch]Event</button>" +
                "</body><script>" +
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -299,22 +299,24 @@ IDBRequest::GetError(nsISupports** aErro
   return rv.ErrorCode();
 }
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache)
   // Don't need NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS because
   // nsDOMEventTargetHelper does it for us.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest, IDBWrapperCache)
   tmp->mResultVal = JSVAL_VOID;
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransaction)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache)
   // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
   // nsDOMEventTargetHelper does it for us.
   NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResultVal)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2327,25 +2327,18 @@ TabChildGlobal::Init()
 {
   NS_ASSERTION(!mMessageManager, "Re-initializing?!?");
   mMessageManager = new nsFrameMessageManager(mTabChild,
                                               nullptr,
                                               mTabChild->GetJSContext(),
                                               MM_CHILD);
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TabChildGlobal,
-                                                nsDOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TabChildGlobal,
-                                                  nsDOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(TabChildGlobal, nsDOMEventTargetHelper,
+                                     mMessageManager)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TabChildGlobal)
   NS_INTERFACE_MAP_ENTRY(nsIMessageListenerManager)
   NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsISyncMessageSender)
   NS_INTERFACE_MAP_ENTRY(nsIContentFrameMessageManager)
   NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ContentFrameMessageManager)
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -125,8 +125,10 @@ PrefixedVisibilityApiWarning='mozHidden'
 # LOCALIZATION NOTE: Do not translate "NodeIterator" or "detach()".
 NodeIteratorDetachWarning=Calling detach() on a NodeIterator no longer has an effect.
 # LOCALIZATION NOTE: Do not translate "Mozilla Audio Data API" and "Web Audio API".
 MozAudioDataWarning=The Mozilla Audio Data API is deprecated.  Please use the Web Audio API instead.
 # LOCALIZATION NOTE: Do not translate "LenientThis" and "this"
 LenientThisWarning=Ignoring get or set of property that has [LenientThis] because the "this" object is incorrect.
 # LOCALIZATION NOTE: Do not translate "nsIDOMWindowUtils", "getWindowWithOuterId", or "nsIWindowMediator"
 GetWindowWithOuterIdWarning=Use of nsIDOMWindowUtils.getOuterWindowWithId() is deprecated.  Instead, use the nsIWindowMediator method of the same name.
+# LOCALIZATION NOTE: Do not translate "getPreventDefault" or "defaultPrevented".
+GetPreventDefaultWarning=Use of getPreventDefault() is deprecated.  Use defaultPrevented instead.
--- a/dom/telephony/TelephonyCall.cpp
+++ b/dom/telephony/TelephonyCall.cpp
@@ -155,19 +155,20 @@ TelephonyCall::NotifyError(const nsAStri
   ChangeStateInternal(nsITelephonyProvider::CALL_STATE_DISCONNECTED, true);
 
   nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("error"), this);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch error event!");
   }
 }
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED_1(TelephonyCall,
+NS_IMPL_CYCLE_COLLECTION_INHERITED_2(TelephonyCall,
                                      nsDOMEventTargetHelper,
-                                     mTelephony)
+                                     mTelephony,
+                                     mError);
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TelephonyCall)
   NS_INTERFACE_MAP_ENTRY(nsIDOMTelephonyCall)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(TelephonyCall)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(TelephonyCall, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(TelephonyCall, nsDOMEventTargetHelper)
--- a/editor/libeditor/base/ChangeAttributeTxn.cpp
+++ b/editor/libeditor/base/ChangeAttributeTxn.cpp
@@ -13,23 +13,18 @@
 
 using namespace mozilla;
 
 ChangeAttributeTxn::ChangeAttributeTxn()
   : EditTxn()
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ChangeAttributeTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ChangeAttributeTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(ChangeAttributeTxn, EditTxn,
+                                     mElement)
 
 NS_IMPL_ADDREF_INHERITED(ChangeAttributeTxn, EditTxn)
 NS_IMPL_RELEASE_INHERITED(ChangeAttributeTxn, EditTxn)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChangeAttributeTxn)
 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
 
 NS_IMETHODIMP ChangeAttributeTxn::Init(nsEditor      *aEditor,
                                        dom::Element *aElement,
--- a/editor/libeditor/base/ChangeCSSInlineStyleTxn.cpp
+++ b/editor/libeditor/base/ChangeCSSInlineStyleTxn.cpp
@@ -20,25 +20,18 @@
 #include "nsString.h"                   // for nsAutoString, nsString, etc
 #include "nsUnicharUtils.h"
 #include "nsXPCOM.h"                    // for NS_Free
 
 class nsIEditor;
 
 #define kNullCh (PRUnichar('\0'))
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ChangeCSSInlineStyleTxn,
-                                                EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ChangeCSSInlineStyleTxn,
-                                                  EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(ChangeCSSInlineStyleTxn, EditTxn,
+                                     mElement)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChangeCSSInlineStyleTxn)
 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
 
 // answers true if aValue is in the string list of white-space separated values aValueList
 // a case-sensitive search is performed if aCaseSensitive is true
 bool
 ChangeCSSInlineStyleTxn::ValueIncludes(const nsAString &aValueList, const nsAString &aValue, bool aCaseSensitive)
--- a/editor/libeditor/base/CreateElementTxn.cpp
+++ b/editor/libeditor/base/CreateElementTxn.cpp
@@ -29,27 +29,20 @@ static bool gNoisy = false;
 
 using namespace mozilla;
 
 CreateElementTxn::CreateElementTxn()
   : EditTxn()
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CreateElementTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNewNode)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRefNode)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CreateElementTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNewNode)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRefNode)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_3(CreateElementTxn, EditTxn,
+                                     mParent,
+                                     mNewNode,
+                                     mRefNode)
 
 NS_IMPL_ADDREF_INHERITED(CreateElementTxn, EditTxn)
 NS_IMPL_RELEASE_INHERITED(CreateElementTxn, EditTxn)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CreateElementTxn)
 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
 NS_IMETHODIMP CreateElementTxn::Init(nsEditor      *aEditor,
                                      const nsAString &aTag,
                                      nsINode       *aParent,
--- a/editor/libeditor/base/DeleteNodeTxn.cpp
+++ b/editor/libeditor/base/DeleteNodeTxn.cpp
@@ -12,27 +12,20 @@
 
 using namespace mozilla;
 
 DeleteNodeTxn::DeleteNodeTxn()
   : EditTxn(), mNode(), mParent(), mRefNode(), mRangeUpdater(nullptr)
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DeleteNodeTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNode)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRefNode)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DeleteNodeTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNode)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRefNode)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_3(DeleteNodeTxn, EditTxn,
+                                     mNode,
+                                     mParent,
+                                     mRefNode)
 
 NS_IMPL_ADDREF_INHERITED(DeleteNodeTxn, EditTxn)
 NS_IMPL_RELEASE_INHERITED(DeleteNodeTxn, EditTxn)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteNodeTxn)
 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
 
 nsresult
 DeleteNodeTxn::Init(nsEditor* aEditor, nsINode* aNode,
--- a/editor/libeditor/base/DeleteRangeTxn.cpp
+++ b/editor/libeditor/base/DeleteRangeTxn.cpp
@@ -27,25 +27,18 @@ using namespace mozilla;
 DeleteRangeTxn::DeleteRangeTxn()
   : EditAggregateTxn(),
     mRange(),
     mEditor(nullptr),
     mRangeUpdater(nullptr)
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DeleteRangeTxn,
-                                                EditAggregateTxn)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRange)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DeleteRangeTxn,
-                                                  EditAggregateTxn)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRange)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(DeleteRangeTxn, EditAggregateTxn,
+                                     mRange)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteRangeTxn)
 NS_INTERFACE_MAP_END_INHERITING(EditAggregateTxn)
 
 nsresult
 DeleteRangeTxn::Init(nsEditor* aEditor,
                      nsRange* aRange,
                      nsRangeUpdater* aRangeUpdater)
--- a/editor/libeditor/base/DeleteTextTxn.cpp
+++ b/editor/libeditor/base/DeleteTextTxn.cpp
@@ -23,23 +23,18 @@ DeleteTextTxn::DeleteTextTxn() :
   mEditor(nullptr),
   mCharData(),
   mOffset(0),
   mNumCharsToDelete(0),
   mRangeUpdater(nullptr)
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DeleteTextTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCharData)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DeleteTextTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCharData)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(DeleteTextTxn, EditTxn,
+                                     mCharData)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteTextTxn)
 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
 
 NS_IMETHODIMP
 DeleteTextTxn::Init(nsEditor* aEditor,
                     nsIDOMCharacterData* aCharData,
                     uint32_t aOffset,
--- a/editor/libeditor/base/EditAggregateTxn.cpp
+++ b/editor/libeditor/base/EditAggregateTxn.cpp
@@ -11,26 +11,18 @@
 #include "nsITransaction.h"             // for nsITransaction
 #include "nsString.h"                   // for nsAutoString
 
 EditAggregateTxn::EditAggregateTxn()
   : EditTxn()
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EditAggregateTxn, EditTxn)
-  tmp->mChildren.Clear();
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(EditAggregateTxn, EditTxn)
-  for (uint32_t i = 0; i < tmp->mChildren.Length(); ++i) {
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildren[i]");
-    cb.NoteXPCOMChild(static_cast<nsITransaction*>(tmp->mChildren[i]));
-  }
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(EditAggregateTxn, EditTxn,
+                                     mChildren)
 
 NS_IMPL_ADDREF_INHERITED(EditAggregateTxn, EditTxn)
 NS_IMPL_RELEASE_INHERITED(EditAggregateTxn, EditTxn)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EditAggregateTxn)
 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
 
 NS_IMETHODIMP EditAggregateTxn::DoTransaction(void)
 {
--- a/editor/libeditor/base/IMETextTxn.cpp
+++ b/editor/libeditor/base/IMETextTxn.cpp
@@ -26,25 +26,19 @@
 
 // #define DEBUG_IMETXN
 
 IMETextTxn::IMETextTxn()
   : EditTxn()
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IMETextTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement)
-  // mRangeList can't lead to cycles
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IMETextTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement)
-  // mRangeList can't lead to cycles
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(IMETextTxn, EditTxn,
+                                     mElement)
+// mRangeList can't lead to cycles
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IMETextTxn)
   if (aIID.Equals(IMETextTxn::GetCID())) {
     *aInstancePtr = (void*)(IMETextTxn*)this;
     NS_ADDREF_THIS();
     return NS_OK;
   } else
 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
--- a/editor/libeditor/base/InsertElementTxn.cpp
+++ b/editor/libeditor/base/InsertElementTxn.cpp
@@ -24,25 +24,19 @@ static bool gNoisy = false;
 #endif
 
 
 InsertElementTxn::InsertElementTxn()
   : EditTxn()
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(InsertElementTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNode)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(InsertElementTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNode)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_2(InsertElementTxn, EditTxn,
+                                     mNode,
+                                     mParent)
 
 NS_IMPL_ADDREF_INHERITED(InsertElementTxn, EditTxn)
 NS_IMPL_RELEASE_INHERITED(InsertElementTxn, EditTxn)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InsertElementTxn)
 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
 
 NS_IMETHODIMP InsertElementTxn::Init(nsINode *aNode,
                                      nsINode *aParent,
--- a/editor/libeditor/base/InsertTextTxn.cpp
+++ b/editor/libeditor/base/InsertTextTxn.cpp
@@ -19,23 +19,18 @@
 static bool gNoisy = false;
 #endif
 
 InsertTextTxn::InsertTextTxn()
   : EditTxn()
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(InsertTextTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(InsertTextTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(InsertTextTxn, EditTxn,
+                                     mElement)
 
 NS_IMPL_ADDREF_INHERITED(InsertTextTxn, EditTxn)
 NS_IMPL_RELEASE_INHERITED(InsertTextTxn, EditTxn)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(InsertTextTxn)
   if (aIID.Equals(InsertTextTxn::GetCID())) {
     *aInstancePtr = (void*)(InsertTextTxn*)this;
     NS_ADDREF_THIS();
     return NS_OK;
--- a/editor/libeditor/base/JoinElementTxn.cpp
+++ b/editor/libeditor/base/JoinElementTxn.cpp
@@ -21,27 +21,20 @@ using namespace mozilla;
 static bool gNoisy = false;
 #endif
 
 JoinElementTxn::JoinElementTxn()
   : EditTxn()
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(JoinElementTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mLeftNode)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mRightNode)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(JoinElementTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLeftNode)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRightNode)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_3(JoinElementTxn, EditTxn,
+                                     mLeftNode,
+                                     mRightNode,
+                                     mParent)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JoinElementTxn)
 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
 
 NS_IMETHODIMP JoinElementTxn::Init(nsEditor   *aEditor,
                                    nsINode *aLeftNode,
                                    nsINode *aRightNode)
 {
--- a/editor/libeditor/base/SplitElementTxn.cpp
+++ b/editor/libeditor/base/SplitElementTxn.cpp
@@ -24,25 +24,19 @@ static bool gNoisy = false;
 
 
 // note that aEditor is not refcounted
 SplitElementTxn::SplitElementTxn()
   : EditTxn()
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SplitElementTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mNewLeftNode)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SplitElementTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNewLeftNode)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_2(SplitElementTxn, EditTxn,
+                                     mParent,
+                                     mNewLeftNode)
 
 NS_IMPL_ADDREF_INHERITED(SplitElementTxn, EditTxn)
 NS_IMPL_RELEASE_INHERITED(SplitElementTxn, EditTxn)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SplitElementTxn)
 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
 
 NS_IMETHODIMP SplitElementTxn::Init(nsEditor   *aEditor,
                                     nsINode    *aNode,
--- a/editor/libeditor/base/nsStyleSheetTxns.cpp
+++ b/editor/libeditor/base/nsStyleSheetTxns.cpp
@@ -46,53 +46,48 @@ RemoveStyleSheet(nsIEditor *aEditor, nsI
 }
 
 AddStyleSheetTxn::AddStyleSheetTxn()
 :  EditTxn()
 ,  mEditor(nullptr)
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AddStyleSheetTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSheet)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AddStyleSheetTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSheet)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(AddStyleSheetTxn, EditTxn,
+                                     mSheet)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AddStyleSheetTxn)
 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
 
 NS_IMETHODIMP
 AddStyleSheetTxn::Init(nsIEditor *aEditor, nsCSSStyleSheet *aSheet)
 {
   NS_ENSURE_TRUE(aEditor && aSheet, NS_ERROR_INVALID_ARG);
 
   mEditor = aEditor;
   mSheet = aSheet;
-  
+
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 AddStyleSheetTxn::DoTransaction()
 {
   NS_ENSURE_TRUE(mEditor && mSheet, NS_ERROR_NOT_INITIALIZED);
-  
+
   AddStyleSheet(mEditor, mSheet);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AddStyleSheetTxn::UndoTransaction()
 {
   NS_ENSURE_TRUE(mEditor && mSheet, NS_ERROR_NOT_INITIALIZED);
-  
+
   RemoveStyleSheet(mEditor, mSheet);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AddStyleSheetTxn::GetTxnDescription(nsAString& aString)
 {
   aString.AssignLiteral("AddStyleSheetTxn");
@@ -101,23 +96,18 @@ AddStyleSheetTxn::GetTxnDescription(nsAS
 
 
 RemoveStyleSheetTxn::RemoveStyleSheetTxn()
 :  EditTxn()
 ,  mEditor(nullptr)
 {
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(RemoveStyleSheetTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSheet)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(RemoveStyleSheetTxn, EditTxn)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSheet)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(RemoveStyleSheetTxn, EditTxn,
+                                     mSheet)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RemoveStyleSheetTxn)
 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
 
 NS_IMETHODIMP
 RemoveStyleSheetTxn::Init(nsIEditor *aEditor, nsCSSStyleSheet *aSheet)
 {
   NS_ENSURE_TRUE(aEditor && aSheet, NS_ERROR_INVALID_ARG);
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -236,17 +236,17 @@ class Heap : public js::HeapBase<T>
  * Reference to a T that has been rooted elsewhere. This is most useful
  * as a parameter type, which guarantees that the T lvalue is properly
  * rooted. See "Move GC Stack Rooting" above.
  *
  * If you want to add additional methods to Handle for a specific
  * specialization, define a HandleBase<T> specialization containing them.
  */
 template <typename T>
-class MOZ_STACK_CLASS Handle : public js::HandleBase<T>
+class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T>
 {
     friend class MutableHandle<T>;
 
   public:
     /* Creates a handle from a handle of a type convertible to T. */
     template <typename S>
     Handle(Handle<S> handle,
            typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0)
--- a/js/src/config/static-checking-config.mk
+++ b/js/src/config/static-checking-config.mk
@@ -26,11 +26,11 @@ DEHYDRA_FLAGS = -fplugin=$(DEHYDRA_PATH)
 ifdef DEHYDRA_PATH
 OS_CXXFLAGS += $(DEHYDRA_FLAGS)
 endif
 
 ifdef ENABLE_CLANG_PLUGIN
 # Load the clang plugin from the mozilla topsrcdir. This implies that the clang
 # plugin is only usable if we're building js/src under mozilla/, though.
 CLANG_PLUGIN := $(DEPTH)/../../build/clang-plugin/$(DLL_PREFIX)clang-plugin$(DLL_SUFFIX)
-OS_CXXFLAGS += -fplugin=$(CLANG_PLUGIN)
-OS_CFLAGS += -fplugin=$(CLANG_PLUGIN)
+OS_CXXFLAGS += -Xclang -load -Xclang $(CLANG_PLUGIN) -Xclang -add-plugin -Xclang moz-check
+OS_CFLAGS += -Xclang -load -Xclang $(CLANG_PLUGIN) -Xclang -add-plugin -Xclang moz-check
 endif
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2463,22 +2463,16 @@ END_CASE(JSOP_ARGUMENTS)
 
 BEGIN_CASE(JSOP_REST)
 {
     RootedObject &rest = rootObject0;
     rest = regs.fp()->createRestParameter(cx);
     if (!rest)
         goto error;
     PUSH_COPY(ObjectValue(*rest));
-    if (!SetInitializerObjectType(cx, script, regs.pc, rest, GenericObject))
-        goto error;
-    rootType0 = GetTypeCallerInitObject(cx, JSProto_Array);
-    if (!rootType0)
-        goto error;
-    rest->setType(rootType0);
 }
 END_CASE(JSOP_REST)
 
 BEGIN_CASE(JSOP_CALLALIASEDVAR)
 BEGIN_CASE(JSOP_GETALIASEDVAR)
 {
     ScopeCoordinate sc = ScopeCoordinate(regs.pc);
     PUSH_COPY(regs.fp()->aliasedVarScope(sc).aliasedVar(sc));
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -116,25 +116,16 @@ StackFrame::initCallFrame(JSContext *cx,
 }
 
 inline void
 StackFrame::initVarsToUndefined()
 {
     SetValueRangeToUndefined(slots(), script()->nfixed);
 }
 
-inline JSObject *
-StackFrame::createRestParameter(JSContext *cx)
-{
-    JS_ASSERT(fun()->hasRest());
-    unsigned nformal = fun()->nargs - 1, nactual = numActualArgs();
-    unsigned nrest = (nactual > nformal) ? nactual - nformal : 0;
-    return NewDenseCopiedArray(cx, nrest, argv() + nformal, NULL);
-}
-
 inline Value &
 StackFrame::unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing)
 {
     JS_ASSERT_IF(checkAliasing, !script()->varIsAliased(i));
     JS_ASSERT(i < script()->nfixed);
     return slots()[i];
 }
 
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -221,16 +221,39 @@ StackFrame::copyRawFrameSlots(AutoValueV
 {
     if (!vec->resize(numFormalArgs() + script()->nfixed))
         return false;
     PodCopy(vec->begin(), argv(), numFormalArgs());
     PodCopy(vec->begin() + numFormalArgs(), slots(), script()->nfixed);
     return true;
 }
 
+JSObject *
+StackFrame::createRestParameter(JSContext *cx)
+{
+    JS_ASSERT(fun()->hasRest());
+    unsigned nformal = fun()->nargs - 1, nactual = numActualArgs();
+    unsigned nrest = (nactual > nformal) ? nactual - nformal : 0;
+    Value *restvp = argv() + nformal;
+    RootedObject obj(cx, NewDenseCopiedArray(cx, nrest, restvp, NULL));
+    if (!obj)
+        return NULL;
+
+    RootedTypeObject type(cx, types::GetTypeCallerInitObject(cx, JSProto_Array));
+    if (!type)
+        return NULL;
+    obj->setType(type);
+
+    /* Ensure that values in the rest array are represented in the type of the array. */
+    for (unsigned i = 0; i < nrest; i++)
+        types::AddTypePropertyId(cx, obj, JSID_VOID, restvp[i]);
+
+    return obj;
+}
+
 static inline void
 AssertDynamicScopeMatchesStaticScope(JSContext *cx, JSScript *script, JSObject *scope)
 {
 #ifdef DEBUG
     RootedObject enclosingScope(cx, script->enclosingStaticScope());
     for (StaticScopeIter i(cx, enclosingScope); !i.done(); i++) {
         if (i.hasDynamicScopeObject()) {
             /*
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -539,17 +539,17 @@ class StackFrame
      * mutable, the arguments object can be overwritten and we can "lose" the
      * arguments object. Thus, StackFrame keeps an explicit argsObj_ field so
      * that the original arguments object is always available.
      */
 
     ArgumentsObject &argsObj() const;
     void initArgsObj(ArgumentsObject &argsobj);
 
-    inline JSObject *createRestParameter(JSContext *cx);
+    JSObject *createRestParameter(JSContext *cx);
 
     /*
      * Scope chain
      *
      * In theory, the scope chain would contain an object for every lexical
      * scope. However, only objects that are required for dynamic lookup are
      * actually created.
      *
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -377,23 +377,33 @@
  *   subclasses must provide an exact override of this method; if a subclass
  *   does not override this method, the compiler will emit an error. This
  *   attribute is not limited to virtual methods, so if it is applied to a
  *   nonvirtual method and the subclass does not provide an equivalent
  *   definition, the compiler will emit an error.
  * MOZ_STACK_CLASS: Applies to all classes. Any class with this annotation is
  *   expected to live on the stack, so it is a compile-time error to use it, or
  *   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). It may be a base or
- *   a member of another class only if both classes are marked with this
- *   annotation.
+ *   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 stack class as well, although this attribute need
+ *   not be provided in such cases.
  */
 #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")))
 #else
 # define MOZ_MUST_OVERRIDE /* nothing */
 # define MOZ_STACK_CLASS /* nothing */
+# define MOZ_NONHEAP_CLASS /* nothing */
 #endif /* MOZ_CLANG_PLUGIN */
 
 #endif /* __cplusplus */
 
 #endif  /* mozilla_Attributes_h_ */
--- a/testing/marionette/client/marionette/chrome/test.xul
+++ b/testing/marionette/client/marionette/chrome/test.xul
@@ -1,21 +1,23 @@
 <?xml version="1.0"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <!DOCTYPE window [
 ]>
-
-<dialog id="dia"
-            xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<window id="winTest" title="Title Test" windowtype="Test Type"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+    <dialog id="dia"
+                xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
-  <vbox id="things">
-    <checkbox id="testBox"  label="box" />
-    <textbox id="textInput"  size="6" value="test" label="input" />
-    <textbox id="textInput2"  size="6" value="test" label="input" />
-    <textbox id="textInput3" class="asdf" size="6" value="test" label="input" />
-  </vbox>
-     
-  <iframe id="iframe" name="iframename" src="chrome://marionette/content/test2.xul"/>
-  <iframe id="iframe" name="iframename" src="chrome://marionette/content/test_nested_iframe.xul"/>
-</dialog>
+      <vbox id="things">
+        <checkbox id="testBox"  label="box" />
+        <textbox id="textInput"  size="6" value="test" label="input" />
+        <textbox id="textInput2"  size="6" value="test" label="input" />
+        <textbox id="textInput3" class="asdf" size="6" value="test" label="input" />
+      </vbox>
+         
+      <iframe id="iframe" name="iframename" src="chrome://marionette/content/test2.xul"/>
+      <iframe id="iframe" name="iframename" src="chrome://marionette/content/test_nested_iframe.xul"/>
+    </dialog>
+</window>
--- a/testing/marionette/client/marionette/marionette.py
+++ b/testing/marionette/client/marionette/marionette.py
@@ -474,20 +474,16 @@ class Marionette(object):
     def set_script_timeout(self, timeout):
         response = self._send_message('setScriptTimeout', 'ok', value=timeout)
         return response
 
     def set_search_timeout(self, timeout):
         response = self._send_message('setSearchTimeout', 'ok', value=timeout)
         return response
 
-    def send_mouse_event(self, send):
-        response = self._send_message('sendMouseEvent', 'ok', value=send)
-        return response
-
     @property
     def current_window_handle(self):
         self.window = self._send_message('getWindow', 'value')
         return self.window
 
     @property
     def title(self):
         response = self._send_message('getTitle', 'value') 
@@ -524,16 +520,20 @@ class Marionette(object):
         else:
             response = self._send_message('switchToFrame', 'ok', value=frame, focus=focus)
         return response
 
     def get_url(self):
         response = self._send_message('getUrl', 'value')
         return response
 
+    def get_window_type(self):
+        response = self._send_message('getWindowType', 'value')
+        return response
+
     def navigate(self, url):
         response = self._send_message('goUrl', 'ok', value=url)
         return response
 
     def timeouts(self, timeout_type, ms):
         assert(timeout_type == self.TIMEOUT_SEARCH or timeout_type == self.TIMEOUT_SCRIPT or timeout_type == self.TIMEOUT_PAGE)
         response = self._send_message('timeouts', 'ok', timeoutType=timeout_type, ms=ms)
         return response
--- a/testing/marionette/client/marionette/marionette_test.py
+++ b/testing/marionette/client/marionette/marionette_test.py
@@ -154,16 +154,25 @@ class MarionetteTestCase(CommonTestCase)
                                noWindow=self.marionette.noWindow,
                                gecko_path=self.marionette.gecko_path)
             qemu.start_session()
             self.marionette.extra_emulators.append(qemu)
         else:
             qemu = self.marionette.extra_emulators[self.extra_emulator_index]
         return qemu
 
+    def wait_for_condition(self, method, timeout=30):
+        timeout = float(timeout) + time.time()
+        while time.time() < timeout:
+            value = method(self.marionette)
+            if value:
+                return value
+            time.sleep(0.5)
+        else:
+            raise TimeoutException("wait_for_condition timed out")
 
 class MarionetteJSTestCase(CommonTestCase):
 
     context_re = re.compile(r"MARIONETTE_CONTEXT(\s*)=(\s*)['|\"](.*?)['|\"];")
     timeout_re = re.compile(r"MARIONETTE_TIMEOUT(\s*)=(\s*)(\d+);")
     match_re = re.compile(r"test_(.*)\.js$")
 
     def __init__(self, marionette_weakref, methodName='runTest', jsFile=None):
--- a/testing/marionette/client/marionette/marionette_touch.py
+++ b/testing/marionette/client/marionette/marionette_touch.py
@@ -26,19 +26,18 @@ class MarionetteTouchMixin(object):
     def tap(self, element):
         self.check_element(element)
         # we pass in touch/mouse/click events if mouse_event_shim.js isn't included in the gaia app
         # otherwise, we just send touch events. See Bug 829566
         send_all = self.execute_script("return typeof window.wrappedJSObject.MouseEventShim === 'undefined';") 
         self.execute_script("%s.tap(arguments[0], null, null, null, null, arguments[1]);" % self.library_name, [element, send_all])
 
     def double_tap(self, element):
-        self.check_element(element)
-        self.execute_script("%s.dbltap(arguments[0]);" % self.library_name, [element])
+        action = Actions(self)
+        action.double_tap(element).perform()
 
     def flick(self, element, x1, y1, x2, y2, duration=200):
-        self.check_element(element)
         action = Actions(self)
         action.flick(element, x1, y1, x2, y2, duration).perform()
 
     def pinch(self, element, x1, y1, x2, y2, x3, y3, x4, y4, duration = 200):
         self.check_element(element)
         gestures.pinch(self, element, x1, y1, x2, y2, x3, y3, x4, y4, duration)
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/tests/unit/single_finger_functions.py
@@ -0,0 +1,102 @@
+from marionette import Actions
+def press_release(marionette, wait_for_condition, expected):
+    testAction = marionette.absolute_url("testAction.html")
+    marionette.navigate(testAction)
+    action = Actions(marionette)
+    button = marionette.find_element("id", "button1")
+    action.press(button).release().perform()
+    wait_for_condition(lambda m: expected in m.execute_script("return document.getElementById('button1').innerHTML;"))
+
+def move_element(marionette, wait_for_condition, expected1, expected2):
+    testAction = marionette.absolute_url("testAction.html")
+    marionette.navigate(testAction)
+    ele = marionette.find_element("id", "button1")
+    drop = marionette.find_element("id", "button2")
+    action = Actions(marionette)
+    action.press(ele).move(drop).release()
+    action.perform()
+    wait_for_condition(lambda m: expected1 in m.execute_script("return document.getElementById('button1').innerHTML;"))
+    wait_for_condition(lambda m: expected2 in m.execute_script("return document.getElementById('button2').innerHTML;"))
+
+def move_element_offset(marionette, wait_for_condition, expected1, expected2):
+    testAction = marionette.absolute_url("testAction.html")
+    marionette.navigate(testAction)
+    ele = marionette.find_element("id", "button1")
+    action = Actions(marionette)
+    action.press(ele).move_by_offset(0,150).move_by_offset(0, 150).release()
+    action.perform()
+    wait_for_condition(lambda m: expected1 in m.execute_script("return document.getElementById('button1').innerHTML;"))
+    wait_for_condition(lambda m: expected2 in m.execute_script("return document.getElementById('button2').innerHTML;"))
+
+def chain(marionette, wait_for_condition, expected1, expected2):
+    testAction = marionette.absolute_url("testAction.html")
+    marionette.navigate(testAction)
+    marionette.set_search_timeout(15000)
+    action = Actions(marionette)
+    button1 = marionette.find_element("id", "button1")
+    action.press(button1).perform()
+    button2 = marionette.find_element("id", "delayed")
+    wait_for_condition(lambda m: expected1 in m.execute_script("return document.getElementById('button1').innerHTML;"))
+    action.move(button2).release().perform()
+    wait_for_condition(lambda m: expected2 in m.execute_script("return document.getElementById('delayed').innerHTML;"))
+
+def chain_flick(marionette, wait_for_condition, expected1, expected2):
+    testAction = marionette.absolute_url("testAction.html")
+    marionette.navigate(testAction)
+    button = marionette.find_element("id", "button1")
+    action = Actions(marionette)
+    action.flick(button, 0, 0, 0, 200).perform()
+    wait_for_condition(lambda m: expected1 in m.execute_script("return document.getElementById('button1').innerHTML;"))
+    wait_for_condition(lambda m: expected2 in m.execute_script("return document.getElementById('buttonFlick').innerHTML;"))
+
+
+def wait(marionette, wait_for_condition, expected):
+    testAction = marionette.absolute_url("testAction.html")
+    marionette.navigate(testAction)
+    action = Actions(marionette)
+    button = marionette.find_element("id", "button1")
+    action.press(button).wait().release().perform()
+    wait_for_condition(lambda m: expected in m.execute_script("return document.getElementById('button1').innerHTML;"))
+
+def wait_with_value(marionette, wait_for_condition, expected):
+    testAction = marionette.absolute_url("testAction.html")
+    marionette.navigate(testAction)
+    button = marionette.find_element("id", "button1")
+    action = Actions(marionette)
+    action.press(button).wait(0.01).release()
+    action.perform()
+    wait_for_condition(lambda m: expected in m.execute_script("return document.getElementById('button1').innerHTML;"))
+
+def context_menu(marionette, wait_for_condition, expected1, expected2):
+    testAction = marionette.absolute_url("testAction.html")
+    marionette.navigate(testAction)
+    button = marionette.find_element("id", "button1")
+    action = Actions(marionette)
+    action.press(button).wait(5).perform()
+    wait_for_condition(lambda m: expected1 in m.execute_script("return document.getElementById('button1').innerHTML;"))
+    action.release().perform()
+    wait_for_condition(lambda m: expected2 in m.execute_script("return document.getElementById('button1').innerHTML;"))
+
+def long_press_action(marionette, wait_for_condition, expected):
+    testAction = marionette.absolute_url("testAction.html")
+    marionette.navigate(testAction)
+    button = marionette.find_element("id", "button1")
+    action = Actions(marionette)
+    action.long_press(button, 5).perform()
+    wait_for_condition(lambda m: expected in m.execute_script("return document.getElementById('button1').innerHTML;"))
+
+def single_tap(marionette, wait_for_condition, expected):
+    testAction = marionette.absolute_url("testAction.html")
+    marionette.navigate(testAction)
+    button = marionette.find_element("id", "button1")
+    action = Actions(marionette)
+    action.tap(button).perform()
+    wait_for_condition(lambda m: expected in m.execute_script("return document.getElementById('button1').innerHTML;"))
+
+def double_tap(marionette, wait_for_condition, expected):
+    testAction = marionette.absolute_url("testAction.html")
+    marionette.navigate(testAction)
+    button = marionette.find_element("id", "button1")
+    action = Actions(marionette)
+    action.double_tap(button).perform()
+    wait_for_condition(lambda m: expected in m.execute_script("return document.getElementById('button1').innerHTML;"))
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/tests/unit/test_element_touch.py
@@ -0,0 +1,32 @@
+# 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 marionette_test import MarionetteTestCase
+from errors import MarionetteException
+
+class testElementTouch(MarionetteTestCase):
+    def test_touch(self):
+      testAction = self.marionette.absolute_url("testAction.html")
+      self.marionette.navigate(testAction)
+      button = self.marionette.find_element("id", "button1")
+      button.tap()
+      expected = "button1-touchstart-touchend-mousemove-mousedown-mouseup-click"
+      self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button1').innerHTML;") == expected)
+      button.tap(0, 300)
+      expected = "button2-touchstart-touchend-mousemove-mousedown-mouseup-click"
+      self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button2').innerHTML;") == expected)
+
+    def test_invisible(self):
+      testAction = self.marionette.absolute_url("testAction.html")
+      self.marionette.navigate(testAction)
+      ele = self.marionette.find_element("id", "hidden")
+      self.assertRaises(MarionetteException, ele.tap)
+
+    def test_scrolling(self):
+      testAction = self.marionette.absolute_url("testAction.html")
+      self.marionette.navigate(testAction)
+      ele = self.marionette.find_element("id", "buttonScroll")
+      ele.tap()
+      expected = "buttonScroll-touchstart-touchend-mousemove-mousedown-mouseup-click"
+      self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('buttonScroll').innerHTML;") == expected)
--- a/testing/marionette/client/marionette/tests/unit/test_gesture.py
+++ b/testing/marionette/client/marionette/tests/unit/test_gesture.py
@@ -1,23 +1,36 @@
 # 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 time
 from marionette_test import MarionetteTestCase
 from gestures import smooth_scroll, pinch
 
 class testGestures(MarionetteTestCase):
+    check_in_viewport = """
+        function elementInViewport(el) {
+          let rect = el.getBoundingClientRect();
+          return (rect.top >= window.pageYOffset &&
+                 rect.left >= window.pageXOffset &&
+                 rect.bottom <= (window.pageYOffset + window.innerHeight) &&
+                 rect.right <= (window.pageXOffset + window.innerWidth)
+         );   
+        };
+    """
     def test_smooth_scroll(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        button = self.marionette.find_element("id", "mozLinkScrollStart")
-        smooth_scroll(self.marionette, button, "y",  -1, 250)
-        time.sleep(15)
-        self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkScroll').innerHTML;"))
+        testAction = self.marionette.absolute_url("testAction.html")
+        self.marionette.navigate(testAction)
+        button = self.marionette.find_element("id", "button2")
+        self.assertFalse(self.marionette.execute_script("%s; return elementInViewport(document.getElementById('buttonScroll'));" % self.check_in_viewport))
+        smooth_scroll(self.marionette, button, "y",  -1, 800)
+        buttonScroll = self.marionette.find_element("id", "buttonScroll")
+        self.wait_for_condition(lambda m: m.execute_script("%s; return elementInViewport(arguments[0]);" % self.check_in_viewport, [buttonScroll]) == True)
+        self.assertEqual("button2-touchstart", self.marionette.execute_script("return document.getElementById('button2').innerHTML;"))
 
+    """
+    #This test doesn't manipulate the page, filed Bug 870377 about it.
     def test_pinch(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        button = self.marionette.find_element("id", "mozLinkScrollStart")
+        testAction = self.marionette.absolute_url("testAction.html")
+        self.marionette.navigate(testAction)
+        button = self.marionette.find_element("id", "button1")
         pinch(self.marionette, button, 0, 0, 0, 0, 0, -50, 0, 50)
-        time.sleep(15)
+    """
--- a/testing/marionette/client/marionette/tests/unit/test_marionette_touch.py
+++ b/testing/marionette/client/marionette/tests/unit/test_marionette_touch.py
@@ -1,40 +1,56 @@
 # 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 time
 from marionette_test import MarionetteTestCase
 from marionette import Marionette
 from marionette_touch import MarionetteTouchMixin   
 
 class TestTouchMixin(MarionetteTestCase):
 
     def setUp(self):
         super(TestTouchMixin, self).setUp()
         self.marionette.__class__ = type('Marionette', (Marionette, MarionetteTouchMixin), {})
         self.marionette.setup_touch()
+        testAction = self.marionette.absolute_url("testAction.html")
+        self.marionette.navigate(testAction)
+
+    def tap(self):
+        button = self.marionette.find_element("id", "button1")
+        self.marionette.tap(button)
+
+    def double_tap(self):
+        button = self.marionette.find_element("id", "button1")
+        self.marionette.double_tap(button)
 
     def test_tap(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        button = self.marionette.find_element("id", "mozLinkCopy")
-        self.marionette.tap(button)
-        time.sleep(10)
-        self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkCopy').innerHTML;"))
+        self.tap()
+        expected = "button1-touchstart-touchend-mousedown-mouseup-click"
+        self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button1').innerHTML;") == expected)
+
+    def test_tap_mouse_shim(self):
+        self.marionette.execute_script("window.wrappedJSObject.MouseEventShim = 'mock value';")
+        self.tap()
+        expected = "button1-touchstart-touchend"
+        self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button1').innerHTML;") == expected)
 
-    def test_dbtap(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        button = self.marionette.find_element("id", "mozMouse")
-        self.marionette.double_tap(button)
-        time.sleep(10)
-        self.assertEqual("TouchEnd2", self.marionette.execute_script("return document.getElementById('mozMouse').innerHTML;"))
+    def test_double_tap(self):
+        self.double_tap()
+        expected = "button1-touchstart-touchend-mousemove-mousedown-mouseup-click-touchstart-touchend-mousemove-mousedown-mouseup-click"
+        self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button1').innerHTML;") == expected)
+
+    """
+    #Enable this when we have logic in double_tap to handle the shim
+    def test_double_tap_mouse_shim(self):
+        self.marionette.execute_script("window.wrappedJSObject.MouseEventShim = 'mock value';")
+        self.double_tap()
+        expected = "button1-touchstart-touchend-touchstart-touchend"
+        self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button1').innerHTML;") == expected)
+    """
 
     def test_flick(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        button = self.marionette.find_element("id", "mozLinkScrollStart")
-        self.marionette.flick(button, 0, 0, 0, -250)
-        time.sleep(15)
-        self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkScroll').innerHTML;"))
-        self.assertEqual("Start", self.marionette.execute_script("return document.getElementById('mozLinkScrollStart').innerHTML;"))
+        button = self.marionette.find_element("id", "button1")
+        self.marionette.flick(button, 0, 0, 0, 200)
+        expected = "button1-touchstart-touchmove"
+        self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button1').innerHTML;") == expected)
+        self.assertEqual("buttonFlick-touchmove-touchend", self.marionette.execute_script("return document.getElementById('buttonFlick').innerHTML;"))
--- a/testing/marionette/client/marionette/tests/unit/test_multi_finger.py
+++ b/testing/marionette/client/marionette/tests/unit/test_multi_finger.py
@@ -1,62 +1,65 @@
 # 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 time
 from marionette_test import MarionetteTestCase
 from marionette import MultiActions, Actions
 
-class testSingleFinger(MarionetteTestCase):
+class testMultiFinger(MarionetteTestCase):
     def test_move_element(self):
-      testTouch = self.marionette.absolute_url("testAction.html")
-      self.marionette.navigate(testTouch)
-      start = self.marionette.find_element("id", "mozLink")
-      drop = self.marionette.find_element("id", "mozLinkPos")
-      ele = self.marionette.find_element("id", "mozLinkCopy")
+      testAction = self.marionette.absolute_url("testAction.html")
+      self.marionette.navigate(testAction)
+      start = self.marionette.find_element("id", "button1")
+      drop = self.marionette.find_element("id", "button2")
+      ele = self.marionette.find_element("id", "button3")
       multi_action = MultiActions(self.marionette)
       action1 = Actions(self.marionette)
       action2 = Actions(self.marionette)
       action1.press(start).move(drop).wait(3).release()
       action2.press(ele).wait().release()
       multi_action.add(action1).add(action2).perform()
-      time.sleep(15)
-      self.assertEqual("Move", self.marionette.execute_script("return document.getElementById('mozLink').innerHTML;"))
-      self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkPos').innerHTML;"))
-      self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkCopy').innerHTML;"))
+      expected = "button1-touchstart"
+      self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button1').innerHTML;") == expected)
+      self.assertEqual("button2-touchmove-touchend", self.marionette.execute_script("return document.getElementById('button2').innerHTML;"))
+      self.assertIn("button3-touchstart-touchend", self.marionette.execute_script("return document.getElementById('button3').innerHTML;"))
 
     def test_move_offset_element(self):
-      testTouch = self.marionette.absolute_url("testAction.html")
-      self.marionette.navigate(testTouch)
-      start = self.marionette.find_element("id", "mozLink")
-      ele = self.marionette.find_element("id", "mozLinkCopy")
+      testAction = self.marionette.absolute_url("testAction.html")
+      self.marionette.navigate(testAction)
+      start = self.marionette.find_element("id", "button1")
+      ele = self.marionette.find_element("id", "button3")
       multi_action = MultiActions(self.marionette)
       action1 = Actions(self.marionette)
       action2 = Actions(self.marionette)
       action1.press(start).move_by_offset(0,300).wait().release()
       action2.press(ele).wait(5).release()
       multi_action.add(action1).add(action2).perform()
-      time.sleep(15)
-      self.assertEqual("Move", self.marionette.execute_script("return document.getElementById('mozLink').innerHTML;"))
-      self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkPos').innerHTML;"))
-      self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkCopy').innerHTML;"))
+      expected = "button1-touchstart"
+      self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button1').innerHTML;") == expected)
+      self.assertEqual("button2-touchmove-touchend", self.marionette.execute_script("return document.getElementById('button2').innerHTML;"))
+      self.assertIn("button3-touchstart-touchend", self.marionette.execute_script("return document.getElementById('button3').innerHTML;"))
 
     def test_three_fingers(self):
-      testTouch = self.marionette.absolute_url("testAction.html")
-      self.marionette.navigate(testTouch)
-      start_one = self.marionette.find_element("id", "mozLink")
-      start_two = self.marionette.find_element("id", "mozLinkStart")
-      drop_two = self.marionette.find_element("id", "mozLinkEnd")
-      ele = self.marionette.find_element("id", "mozLinkCopy2")
+      testAction = self.marionette.absolute_url("testAction.html")
+      self.marionette.navigate(testAction)
+      start_one = self.marionette.find_element("id", "button1")
+      start_two = self.marionette.find_element("id", "button2")
+      element1 = self.marionette.find_element("id", "button3")
+      element2 = self.marionette.find_element("id", "button4")
       multi_action = MultiActions(self.marionette)
       action1 = Actions(self.marionette)
       action2 = Actions(self.marionette)
       action3 = Actions(self.marionette)
       action1.press(start_one).move_by_offset(0,300).release()
-      action2.press(ele).wait().wait(5).release()
-      action3.press(start_two).move(drop_two).wait(2).release()
+      action2.press(element1).wait().wait(5).release()
+      action3.press(element2).wait().wait().release()
       multi_action.add(action1).add(action2).add(action3).perform()
-      time.sleep(15)
-      self.assertEqual("Move", self.marionette.execute_script("return document.getElementById('mozLink').innerHTML;"))
-      self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkPos').innerHTML;"))
-      self.assertTrue(self.marionette.execute_script("return document.getElementById('mozLinkCopy2').innerHTML >= 5000;"))
-      self.assertTrue(self.marionette.execute_script("return document.getElementById('mozLinkEnd').innerHTML >= 5000;"))
+      expected = "button1-touchstart"
+      self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button1').innerHTML;") == expected)
+      self.assertEqual("button2-touchmove-touchend", self.marionette.execute_script("return document.getElementById('button2').innerHTML;"))
+      button3_text = self.marionette.execute_script("return document.getElementById('button3').innerHTML;")
+      button4_text = self.marionette.execute_script("return document.getElementById('button4').innerHTML;")
+      self.assertIn("button3-touchstart-touchend", button3_text)
+      self.assertIn("button4-touchstart-touchend", button4_text)
+      self.assertTrue(int(button3_text.rsplit("-")[-1]) >= 5000)
+      self.assertTrue(int(button4_text.rsplit("-")[-1]) >= 5000)
--- a/testing/marionette/client/marionette/tests/unit/test_single_finger.py
+++ b/testing/marionette/client/marionette/tests/unit/test_single_finger.py
@@ -1,211 +1,84 @@
 # 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 time
 from marionette_test import MarionetteTestCase
 from marionette import Actions
-from errors import NoSuchElementException, MarionetteException
+from errors import MarionetteException
+#add this directory to the path
+import os
+import sys
+sys.path.append(os.path.dirname(__file__))
+from single_finger_functions import *
 
 class testSingleFinger(MarionetteTestCase):
-    def test_wait(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        button = self.marionette.find_element("id", "mozLinkCopy")
-        action = Actions(self.marionette)
-        action.press(button).wait(0.2).release()
-        action.perform()
-        time.sleep(15)
-        self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkCopy').innerHTML;"))
+    def test_press_release(self):
+        press_release(self.marionette, self.wait_for_condition, "button1-touchstart-touchend-mousemove-mousedown-mouseup-click")
 
     def test_move_element(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        ele = self.marionette.find_element("id", "mozLink")
-        drop = self.marionette.find_element("id", "mozLinkPos")
-        action = Actions(self.marionette)
-        action.press(ele).move(drop).release()
-        action.perform()
-        time.sleep(15)
-        self.assertEqual("Move", self.marionette.execute_script("return document.getElementById('mozLink').innerHTML;"))
-        self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkPos').innerHTML;"))
+        move_element(self.marionette, self.wait_for_condition, "button1-touchstart", "button2-touchmove-touchend")
 
+    """
+    #Skipping due to Bug 874914
     def test_move_by_offset(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        ele = self.marionette.find_element("id", "mozLink")
-        action = Actions(self.marionette)
-        action.press(ele).move_by_offset(0,150).move_by_offset(0,150).release()
-        action.perform()
-        time.sleep(15)
-        self.assertEqual("Move", self.marionette.execute_script("return document.getElementById('mozLink').innerHTML;"))
-        self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkPos').innerHTML;"))
-
-    def test_chain(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        action = Actions(self.marionette)
-        button1 = self.marionette.find_element("id", "mozLinkCopy2")
-        action.press(button1).perform()
-        button2 = self.marionette.find_element("id", "delayed")
-        time.sleep(5)
-        action.move(button2).release().perform()
-        time.sleep(15)
-        self.assertEqual("End", self.marionette.execute_script("return document.getElementById('delayed').innerHTML;"))
+        move_element_offset(self.marionette, self.wait_for_condition, "button1-touchstart", "button2-touchmove-touchend")
+    """
 
     def test_no_press(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
+        testAction = self.marionette.absolute_url("testAction.html")
+        self.marionette.navigate(testAction)
         action = Actions(self.marionette)
         action.release()
-        self.assertRaises(NoSuchElementException, action.perform)
-
-    def test_mouse_wait(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        self.marionette.send_mouse_event(True)
-        action = Actions(self.marionette)
-        button = self.marionette.find_element("id", "mozMouse")
-        action.press(button).wait().release().perform()
-        time.sleep(15)
-        self.assertEqual("MouseClick", self.marionette.execute_script("return document.getElementById('mozMouse').innerHTML;"))
+        self.assertRaises(MarionetteException, action.perform)
 
-    def test_mouse_wait_more(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        self.marionette.send_mouse_event(True)
-        action = Actions(self.marionette)
-        button = self.marionette.find_element("id", "mozMouse")
-        action.press(button).wait(0.1).release().perform()
-        time.sleep(15)
-        self.assertEqual("MouseClick", self.marionette.execute_script("return document.getElementById('mozMouse').innerHTML;"))
-
-    def test_mouse_no_wait(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        self.marionette.send_mouse_event(True)
-        action = Actions(self.marionette)
-        button = self.marionette.find_element("id", "mozMouse")
-        action.press(button).release().perform()
-        time.sleep(15)
-        self.assertEqual("MouseClick", self.marionette.execute_script("return document.getElementById('mozMouse').innerHTML;"))
+    def test_wait(self):
+        wait(self.marionette, self.wait_for_condition, "button1-touchstart-touchend-mousemove-mousedown-mouseup-click")
 
-    def test_no_mouse_wait(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        self.marionette.send_mouse_event(False)
-        action = Actions(self.marionette)
-        button = self.marionette.find_element("id", "mozMouse")
-        action.press(button).wait().release().perform()
-        time.sleep(15)
-        self.assertEqual("TouchEnd", self.marionette.execute_script("return document.getElementById('mozMouse').innerHTML;"))
+    def test_wait_with_value(self):
+        wait_with_value(self.marionette, self.wait_for_condition, "button1-touchstart-touchend-mousemove-mousedown-mouseup-click")
 
-    def test_no_mouse_no_wait(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        self.marionette.send_mouse_event(False)
-        action = Actions(self.marionette)
-        button = self.marionette.find_element("id", "mozMouse")
-        action.press(button).release().perform()
-        time.sleep(15)
-        self.assertEqual("TouchEnd", self.marionette.execute_script("return document.getElementById('mozMouse').innerHTML;"))
-
-    def test_long_press(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        button = self.marionette.find_element("id", "mozLinkCopy")
-        action = Actions(self.marionette)
-        action.press(button).wait(5).perform()
-        time.sleep(10)
-        self.assertEqual("Context", self.marionette.execute_script("return document.getElementById('mozLinkCopy').innerHTML;"))
-        action.release().perform()
-        time.sleep(10)
-        self.assertEqual("ContextEnd", self.marionette.execute_script("return document.getElementById('mozLinkCopy').innerHTML;"))
+    def test_context_menu(self):
+        context_menu(self.marionette, self.wait_for_condition, "button1-touchstart-contextmenu", "button1-touchstart-contextmenu-touchend")
 
     def test_long_press_action(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        button = self.marionette.find_element("id", "mozLinkCopy")
-        action = Actions(self.marionette)
-        action.long_press(button, 5).perform()
-        time.sleep(10)
-        self.assertEqual("ContextEnd", self.marionette.execute_script("return document.getElementById('mozLinkCopy').innerHTML;"))
+        long_press_action(self.marionette, self.wait_for_condition, "button1-touchstart-contextmenu-touchend")
 
     """
     #Skipping due to Bug 865334
     def test_long_press_fail(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        button = self.marionette.find_element("id", "mozLinkCopy")
+        testAction = self.marionette.absolute_url("testAction.html")
+        self.marionette.navigate(testAction)
+        button = self.marionette.find_element("id", "button1Copy")
         action = Actions(self.marionette)
         action.press(button).long_press(button, 5)
         self.assertRaises(MarionetteException, action.perform)
     """
 
-    def test_wrong_value(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        self.assertRaises(MarionetteException, self.marionette.send_mouse_event, "boolean")
+    def test_chain(self):
+        chain(self.marionette, self.wait_for_condition, "button1-touchstart", "delayed-touchmove-touchend")
 
+    """
+    #Skipping due to Bug 874914. Flick uses chained moveByOffset calls
     def test_chain_flick(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        button = self.marionette.find_element("id", "mozLinkScrollStart")
-        action = Actions(self.marionette)
-        action.flick(button, 0, 0, 0, -250).perform()
-        time.sleep(15)
-        self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkScroll').innerHTML;"))
-        self.assertEqual("Start", self.marionette.execute_script("return document.getElementById('mozLinkScrollStart').innerHTML;"))
+        chain_flick(self.marionette, self.wait_for_condition, "button1-touchstart-touchmove", "buttonFlick-touchmove-touchend")
+    """
 
     """
     #Skipping due to Bug 865334
     def test_touchcancel_chain(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        button = self.marionette.find_element("id", "mozLinkCancel")
+        testAction = self.marionette.absolute_url("testAction.html")
+        self.marionette.navigate(testAction)
+        button = self.marionette.find_element("id", "button1")
         action = Actions(self.marionette)
         action.press(button).wait(5).cancel()
         action.perform()
-        time.sleep(15)
-        self.assertEqual("End", self.marionette.execute_script("return document.getElementById('mozLinkCancel').innerHTML;"))
+        expected = "button1-touchstart-touchcancel"
+        self.wait_for_condition(lambda m: m.execute_script("return document.getElementById('button1').innerHTML;") == expected)
     """
 
-    def test_mouse_single_tap(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        self.marionette.send_mouse_event(True)
-        button = self.marionette.find_element("id", "mozMouse")
-        action = Actions(self.marionette)
-        action.tap(button).perform()
-        time.sleep(15)
-        self.assertEqual("MouseClick", self.marionette.execute_script("return document.getElementById('mozMouse').innerHTML;"))
-
-    def test_mouse_double_tap(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        self.marionette.send_mouse_event(True)
-        button = self.marionette.find_element("id", "mozMouse")
-        action = Actions(self.marionette)
-        action.double_tap(button).perform()
-        time.sleep(15)
-        self.assertEqual("MouseClick2", self.marionette.execute_script("return document.getElementById('mozMouse').innerHTML;"))
+    def test_single_tap(self):
+        single_tap(self.marionette, self.wait_for_condition, "button1-touchstart-touchend-mousemove-mousedown-mouseup-click")
 
-    def test_touch(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        self.marionette.send_mouse_event(False)
-        button = self.marionette.find_element("id", "mozMouse")
-        action = Actions(self.marionette)
-        action.tap(button).perform()
-        time.sleep(10)
-        self.assertEqual("TouchEnd", self.marionette.execute_script("return document.getElementById('mozMouse').innerHTML;"))
+    def test_double_tap(self):
+        double_tap(self.marionette, self.wait_for_condition, "button1-touchstart-touchend-mousemove-mousedown-mouseup-click-touchstart-touchend-mousemove-mousedown-mouseup-click")
 
-    def test_dbtouch(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        self.marionette.send_mouse_event(False)
-        button = self.marionette.find_element("id", "mozMouse")
-        action = Actions(self.marionette)
-        action.double_tap(button).perform()
-        time.sleep(10)
-        self.assertEqual("TouchEnd2", self.marionette.execute_script("return document.getElementById('mozMouse').innerHTML;"))
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/tests/unit/test_single_finger_desktop.py
@@ -0,0 +1,98 @@
+from marionette_test import MarionetteTestCase
+from marionette import Actions
+from errors import MarionetteException
+#add this directory to the path
+import os
+import sys
+sys.path.append(os.path.dirname(__file__))
+from single_finger_functions import *
+
+class testSingleFingerMouse(MarionetteTestCase):
+    def setUp(self):
+        super(MarionetteTestCase, self).setUp()
+        # set context menu related preferences needed for some tests
+        self.marionette.set_context("chrome")
+        self.enabled = self.marionette.execute_script("""
+let prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                              .getService(Components.interfaces.nsIPrefBranch);
+let value = false;
+try {
+  value = prefs.getBoolPref("ui.click_hold_context_menus");
+}
+catch (e) {}
+prefs.setBoolPref("ui.click_hold_context_menus", true);
+return value;
+""")
+        self.wait_time = self.marionette.execute_script("""
+let prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                              .getService(Components.interfaces.nsIPrefBranch);
+let value = 750;
+try {
+  value = prefs.getIntPref("ui.click_hold_context_menus.delay");
+}
+catch (e) {}
+prefs.setIntPref("ui.click_hold_context_menus.delay", value);
+return value;
+""")
+        self.marionette.set_context("content")
+
+    def tearDown(self):
+        self.marionette.set_context("chrome")
+        self.marionette.execute_script(
+                          """
+let prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                              .getService(Components.interfaces.nsIPrefBranch);
+prefs.setBoolPref("ui.click_hold_context_menus", arguments[0]);
+""", [self.enabled])
+        self.marionette.execute_script(
+                          """
+let prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                              .getService(Components.interfaces.nsIPrefBranch);
+prefs.setIntPref("ui.click_hold_context_menus.delay", arguments[0]);
+""", [self.wait_time])
+        self.marionette.set_context("content")
+        super(MarionetteTestCase, self).tearDown()
+
+    def test_press_release(self):
+        press_release(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click")
+
+    def test_move_element(self):
+        move_element(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown", "button2-mousemove-mouseup")
+
+    def test_move_by_offset(self):
+        move_element_offset(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown", "button2-mousemove-mouseup")
+
+    def test_wait(self):
+        wait(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click")
+
+    def test_wait_with_value(self):
+        wait_with_value(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click")
+
+    def test_context_menu(self):
+        context_menu(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-contextmenu", "button1-mousemove-mousedown-contextmenu-mouseup-click")
+
+    def test_long_press_action(self):
+        long_press_action(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-contextmenu-mouseup-click")
+
+    """
+    //Skipping due to Bug 865334
+    def test_long_press_fail(self):
+        testAction = self.marionette.absolute_url("testAction.html")
+        self.marionette.navigate(testAction)
+        button = self.marionette.find_element("id", "button1Copy")
+        action = Actions(self.marionette)
+        action.press(button).long_press(button, 5)
+        assertRaises(MarionetteException, action.perform)
+    """
+
+    def test_chain(self):
+        chain(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown", "delayed-mousemove-mouseup")
+
+    def test_chain_flick(self):
+        chain_flick(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mousemove", "buttonFlick-mousemove-mouseup")
+
+    def test_single_tap(self):
+        single_tap(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click")
+
+    def test_double_tap(self):
+        double_tap(self.marionette, self.wait_for_condition, "button1-mousemove-mousedown-mouseup-click-mousemove-mousedown-mouseup-click")
deleted file mode 100644
--- a/testing/marionette/client/marionette/tests/unit/test_tap.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-import time
-from marionette_test import MarionetteTestCase
-from marionette import HTMLElement
-
-class testSingleFinger(MarionetteTestCase):
-    def test_mouse_single_tap(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        self.marionette.send_mouse_event(True)
-        button = self.marionette.find_element("id", "mozMouse")
-        button.tap()
-        time.sleep(15)
-        self.assertEqual("MouseClick", self.marionette.execute_script("return document.getElementById('mozMouse').innerHTML;"))
-
-    def test_touch(self):
-        testTouch = self.marionette.absolute_url("testAction.html")
-        self.marionette.navigate(testTouch)
-        self.marionette.send_mouse_event(False)
-        button = self.marionette.find_element("id", "mozMouse")
-        button.tap()
-        time.sleep(10)
-        self.assertEqual("TouchEnd", self.marionette.execute_script("return document.getElementById('mozMouse').innerHTML;"))
deleted file mode 100644
--- a/testing/marionette/client/marionette/tests/unit/test_touch.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-import os
-import time
-from marionette_test import MarionetteTestCase
-from marionette import HTMLElement
-from errors import MarionetteException
-
-class testTouch(MarionetteTestCase):
-    def test_touch(self):
-      testTouch = self.marionette.absolute_url("testTouch.html")
-      self.marionette.navigate(testTouch)
-      button = self.marionette.find_element("id", "mozLink")
-      button.tap(0, 300)
-      time.sleep(10)
-      self.assertEqual("Clicked", self.marionette.execute_script("return document.getElementById('mozLinkPos').innerHTML;"))
-      button.tap()
-      time.sleep(10)
-      self.assertEqual("Clicked", self.marionette.execute_script("return document.getElementById('mozLink').innerHTML;"))
-
-    def test_invisible(self):
-      testTouch = self.marionette.absolute_url("testTouch.html")
-      self.marionette.navigate(testTouch)
-      ele = self.marionette.find_element("id", "testh2")
-      self.assertRaises(MarionetteException, ele.tap)
-
-    def test_scrolling(self):
-      testTouch = self.marionette.absolute_url("testTouch.html")
-      self.marionette.navigate(testTouch)
-      ele = self.marionette.find_element("id", "scroll")
-      ele.tap()
-      time.sleep(10)
-      self.assertEqual("Clicked", self.marionette.execute_script("return document.getElementById('scroll').innerHTML;"))
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/tests/unit/test_window_title.py
@@ -0,0 +1,33 @@
+# 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 marionette_test import MarionetteTestCase
+
+#TODO use @skip_if_b2g when Bug 875921 is fixed
+class TestTitleChrome(MarionetteTestCase):
+    def setUp(self):
+        MarionetteTestCase.setUp(self)
+        self.marionette.set_context("chrome")
+        self.win = self.marionette.current_window_handle
+        self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
+        self.marionette.switch_to_window('foo')
+        self.assertNotEqual(self.win, self.marionette.current_window_handle)
+
+    def tearDown(self):
+        self.assertNotEqual(self.win, self.marionette.current_window_handle)
+        self.marionette.execute_script("window.close();")
+        self.marionette.switch_to_window(self.win)
+        MarionetteTestCase.tearDown(self)
+
+    def test_get_chrome_title(self):
+        title = self.marionette.execute_script("return window.document.documentElement.getAttribute('title');")
+        self.assertEqual(title, self.marionette.title)
+        self.assertEqual('Title Test', self.marionette.title)
+
+class TestTitle(MarionetteTestCase):
+    def test_get_html_title(self):
+        test_html = self.marionette.absolute_url("test.html")
+        self.marionette.navigate(test_html)
+        self.assertEqual('Marionette Test', self.marionette.title)
+
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/tests/unit/test_window_type.py
@@ -0,0 +1,26 @@
+# 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 marionette_test import MarionetteTestCase
+
+class TestWindowTypeChrome(MarionetteTestCase):
+    def setUp(self):
+        MarionetteTestCase.setUp(self)
+        self.marionette.set_context("chrome")
+        self.win = self.marionette.current_window_handle
+        self.marionette.execute_script("window.open('chrome://marionette/content/test.xul', 'foo', 'chrome,centerscreen');")
+        self.marionette.switch_to_window('foo')
+        self.assertNotEqual(self.win, self.marionette.current_window_handle)
+
+    def tearDown(self):
+        self.assertNotEqual(self.win, self.marionette.current_window_handle)
+        self.marionette.execute_script("window.close();")
+        self.marionette.switch_to_window(self.win)
+        MarionetteTestCase.tearDown(self)
+
+    def test_get_window_type(self):
+        window_type = self.marionette.execute_script("return window.document.documentElement.getAttribute('windowtype');")
+        self.assertEqual(window_type, self.marionette.get_window_type())
+        self.assertEqual('Test Type', self.marionette.get_window_type())
+
--- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini
@@ -6,19 +6,16 @@ qemu = false
 browser = true
 
 ; true if the test is compatible with b2g, otherwise false
 b2g = true
 
 ; true if the test should be skipped
 skip = false
 
-; true if the test requires unagi
-unagi = false
-
 [test_getstatus.py]
 [test_import_script.py]
 [test_import_script_content.py.py]
 b2g = false
 [test_click.py]
 b2g = false
 [test_selected.py]
 b2g = false
@@ -41,48 +38,49 @@ qemu = true
 b2g = false
 
 [test_navigation.py]
 b2g = false
 
 [test_timeouts.py]
 b2g = false
 
-[test_touch.py]
+[test_element_touch.py]
 b2g = true
 browser = false
 
 [test_gesture.py]
 b2g = true
 browser = false
-unagi = true
 
 [test_marionette_touch.py]
 b2g = true
 browser = false
 
 [test_single_finger.py]
 b2g = true
 browser = false
+[test_single_finger_desktop.py]
+b2g = false
 
 [test_multi_finger.py]
 b2g = true
 browser = false
 
-[test_tap.py]
-b2g = true
-browser = false
-
 [test_simpletest_pass.js]
 [test_simpletest_sanity.py]
 [test_simpletest_chrome.js]
 [test_simpletest_timeout.js]
 [test_specialpowers.py]
 [test_switch_frame.py]
 b2g = false
 
 [test_window_management.py]
 b2g = false
 
 [test_appcache.py]
 [test_screenshot.py]
 [test_cookies.py]
 b2g = false
+[test_window_title.py]
+b2g = false
+[test_window_type.py]
+b2g = false
--- a/testing/marionette/client/marionette/www/testAction.html
+++ b/testing/marionette/client/marionette/www/testAction.html
@@ -1,263 +1,94 @@
 <!-- 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/. -->
 
 <!DOCTYPE html>
 
 <html>
+<meta charset="UTF-8">
 <head>
 <title>Marionette Test</title>
 </head>
 <body>
   <h1 id="testh1">Test Page</h1>
-  <!-- "mozLink" and "mozLinkPos" work together to perform touchdown on mozLink, vertical move and then touchup on mozLinkPos-->
-  <button id="mozLink" style="position:absolute;left:0px;top:55px;" type="button" allowevents=true>Button1</button>
-  <button id="mozLinkPos" style="position:absolute;left:0px;top:355px;" type="button" allowevents=true>Button2</button>
-  <!-- "mozLinkCopy" listens for a touchdown and touchup -->
-  <button id="mozLinkCopy" style="position:absolute;left:0px;top:455px;" type="button" allowevents=true>Button3</button>
-  <!-- "mozLinkScroll" listens for scroll -->
-  <button id="mozLinkScroll" style="position:absolute;left:0px;top:655px;" type="button" allowevents=true>Button8</button>
-  <!-- "mozLinkScrollStart" listens for scroll -->
-  <button id="mozLinkScrollStart" style="position:absolute;left:0px;top:405px;" type="button" allowevents=true>Button9</button>
-  <!-- "mozLinkStart" and "mozLinkEnd" work together to perform touchdown on mozLinkStart, horizontal move and then touchup on mozLinkEnd -->  
-  <button id="mozLinkStart" style="position:absolute;left:10px;top:200px;" type="button" allowevents=true>Press</button>
-  <button id="mozLinkEnd" style="position:absolute;left:140px;top:200px;" type="button" allowevents=true>Release</button>
-  <!-- "mozLinkCopy2" listens for a touchdown and touchup. It shows the time when it's fired-->
-  <button id="mozLinkCopy2" style="position:absolute;left:80px;top:455px;" type="button" allowevents=true>Button4</button>
-  <!-- "mozLinkCancel" listens for a touchdown and touchcancel -->
-  <button id="mozLinkCancel" style="position:absolute;left:0px;top:255px;" type="button" allowevents=true>Button5</button>
-  <!-- "mozMouse" listens for mouse events -->
-  <button id="mozMouse" style="position:absolute;left:0px;top:305px;" type="button" allowevents=true>Button7</button>
+  <button id="button1" style="position:absolute;left:0px;top:55px;" type="button" allowevents=true>button1</button>
+  <button id="button2" style="position:absolute;left:0px;top:355px;" type="button" allowevents=true>button2</button>
+  <button id="button3" style="position:absolute;left:0px;top:455px;" type="button" allowevents=true>button3</button>
+  <button id="button4" style="position:absolute;left:100px;top:455px;" type="button" allowevents=true>button4</button>
+  <button id="buttonScroll" style="position:absolute;left:100px;top:855px;" type="button" allowevents=true>buttonScroll</button>
+  <h2 id="hidden" style="visibility: hidden" class="linkClass">Hidden</h2>
+  <button id="buttonFlick" style="position:absolute;left:0px;top:255px;" type="button" allowevents=true>buttonFlick</button>
   <script type="text/javascript">
-    window.ready = true;
-    var press = document.getElementById("mozLink");
-    var second = document.getElementById("mozLinkCopy");
-    var third = document.getElementById("mozLinkStart");
-    var fourth = document.getElementById("mozLinkCopy2");
-    var fifth = document.getElementById("mozLinkCancel");
-    var sixth = document.getElementById("mozMouse");
-    var seventh = document.getElementById("mozLinkScrollStart");
-    // touchmove and touchend must be performed on the same element as touchstart
-    // here is press for vertical move
-    press.addEventListener("touchstart", function(){changePressText("mozLink")}, false);
-    press.addEventListener("touchmove", changeMoveText, false);
-    press.addEventListener("touchend", changeReleaseText, false);
-    // here is second for a tap
-    second.addEventListener("touchstart", function(){changePressText("mozLinkCopy")}, false);
-    second.addEventListener("touchend", function(){changeClickText("mozLinkCopy")}, false);
-    // change for contextmenu
-    second.addEventListener("contextmenu", onContextMenuChange, false);
-    // here is third for horizontal move
-    third.addEventListener("touchstart", function(){changePressText("mozLinkStart")}, false);
-    third.addEventListener("touchmove", changeHorizontalMove, false);
-    third.addEventListener("touchend", changeHorizontalRelease, false);
-    // here is fourth for touch up and down with time shown
-    fourth.addEventListener("touchstart", changeTimePress, false);
-    fourth.addEventListener("touchend", changeTimeRelease, false);
-    // here is fifth for a cancel
-    fifth.addEventListener("touchstart", function(){changePressText("mozLinkCancel")}, false);
-    fifth.addEventListener("touchcancel", function(){changeClickText("mozLinkCancel")}, false);
-    // here is sixth for mouse event
-    sixth.addEventListener("touchstart", function(){changeMouseText("TouchStart")}, false);
-    sixth.addEventListener("touchend", function(){changeMouseText("TouchEnd")}, false);
-    sixth.addEventListener("mousemove", function(){changeMouseText("MouseMove")}, false);
-    sixth.addEventListener("mousedown", function(){changeMouseText("MouseDown")}, false);
-    sixth.addEventListener("mouseup", function(){changeMouseText("MouseUp")}, false);
-    sixth.addEventListener("click", function(){changeMouseText("MouseClick")}, false);
-    // here is seventh for a scroll
-    seventh.addEventListener("touchstart", function(){changePressText("mozLinkScrollStart")}, false);
-    seventh.addEventListener("touchend", function(){changeScrollText("mozLinkScroll")}, false);
-    function changeMouseText(strId) {
-      var mouse = document.getElementById("mozMouse");
-      switch(strId) {
-        case "TouchStart":
-          if (mouse.innerHTML == "MouseClick") {
-            mouse.innerHTML = "TouchStart2";
-          }
-          else if (mouse.innerHTML == "TouchEnd") {
-            mouse.innerHTML = "TouchStart2";
-          }
-          else {
-            mouse.innerHTML = strId;
-          }
-          break;
-        case "TouchEnd":
-          if (mouse.innerHTML == "TouchStart") {
-            mouse.innerHTML = strId;
-          }
-          else if (mouse.innerHTML == "TouchStart2") {
-            mouse.innerHTML = "TouchEnd2";
-          }
-          else {
-            mouse.innerHTML = "Error";
-          }
-          break;
-        case "MouseMove":
-          if (mouse.innerHTML == "TouchEnd") {
-            mouse.innerHTML = strId;
-          }
-          else if (mouse.innerHTML == "TouchEnd2") {
-            mouse.innerHTML = "MouseMove2";
-          }
-          else {
-            mouse.innerHTML = "Error";
-          }
-          break;
-        case "MouseDown":
-          if (mouse.innerHTML == "MouseMove") {
-            mouse.innerHTML = strId;
-          }
-          else if (mouse.innerHTML == "MouseMove2") {
-            mouse.innerHTML = "MouseDown2";
-          }
-          else {
-            mouse.innerHTML = "Error";
-          }
-          break;
-        case "MouseUp":
-          if (mouse.innerHTML == "MouseDown") {
-            mouse.innerHTML = strId;
-          }
-          else if (mouse.innerHTML == "MouseDown2") {
-            mouse.innerHTML = "MouseUp2";
-          }
-          else {
-            mouse.innerHTML = "Error";
-          }
-          break;
-        case "MouseClick":
-          if (mouse.innerHTML == "MouseUp") {
-            mouse.innerHTML = strId;
-          }
-          else if (mouse.innerHTML == "MouseUp2") {
-            mouse.innerHTML = "MouseClick2";
-          }
-          else {
-            mouse.innerHTML = "Error";
-          }
-          break;
-        default:
-          mouse.innerHTML = "Error";
-          break;
-      }
+    var button3Timer = null;
+    var button4Timer = null;
+    //appends passed in text to the innerHTML of the event's target
+    function appendText(text) {
+      return function(evt) {
+         var element;
+         if (evt.type.indexOf("touch") !== -1) {
+           if (evt.type == "touchstart") {
+             element = evt.target;
+           }
+           else {
+             //since the target of touchstart is the target of all subsequent events, then
+             //changedTouches holds the current coordinates of this touch event, so we 
+             //use these coordinates to find the element under the touch event
+             var touches = evt.changedTouches;
+             var x = touches[0].clientX;
+             var y = touches[0].clientY;
+             element = document.elementFromPoint(x,y);
+           }
+         }
+         //handle mouse events or contextmenu
+         else {
+          element = evt.target;
+         }
+         element.innerHTML += text;
+      };
+    };
+    //use this function outside of attachListeners when you want to test sendMouseOnlyEvents on a target
+    function attachMouseListeners(element) {
+      element.addEventListener("contextmenu", appendText("-contextmenu"), false);
+      element.addEventListener("mousedown", appendText("-mousedown"), false);
+      element.addEventListener("mousemove", appendText("-mousemove"), false);
+      element.addEventListener("mouseup", appendText("-mouseup"), false);
+      element.addEventListener("click", appendText("-click"), false);
+    };
+    function attachListeners(id) {
+      var element = document.getElementById(id);
+      element.addEventListener("touchstart", appendText("-touchstart"), false);
+      element.addEventListener("touchmove", appendText("-touchmove"), false);
+      element.addEventListener("touchend", appendText("-touchend"), false);
+      element.addEventListener("touchcancel", appendText("-touchcancel"), false);
+      attachMouseListeners(element);
+    };
+    //for tracking time on an element
+    function addTimers(id, timer) {
+      var element = document.getElementById(id);
+      element.addEventListener("touchstart", function(evt) { timer = (new Date()).getTime();}, false);
+      element.addEventListener("touchend", function(evt) { timer = (new Date()).getTime() - timer; evt.target.innerHTML += "-" + timer;}, false);
     }
-
-    function changePressText(strId) {
-      var press = document.getElementById(strId);
-      press.innerHTML = "Start";
-    }
-
-    function changeMoveText() {
-      var move = document.getElementById("mozLink");
-      move.innerHTML = "Move";
-    }
-
-    function checkPosition(event, ele) {
-      var touches = event.changedTouches;
-      var clientX = touches[0].clientX;
-      var clientY = touches[0].clientY;
-      var release = document.getElementById(ele);
-      var boxr = release.getBoundingClientRect();
-      return (clientY >= boxr.top &&
-              clientY <= boxr.bottom &&
-              clientX >= boxr.left &&
-              clientX <= boxr.right);
-    }
-
-    function changeReleaseText(event) {
-      if (checkPosition(event, "mozLinkPos")) {
-        document.getElementById("mozLinkPos").innerHTML = "End";
-      }
-    }
-
-    function changeHorizontalMove() {
-      var press = document.getElementById("mozLinkStart");
-      if (press.innerHTML == "Start") {
-        var d = new Date();
-        press.innerHTML = d.getTime();
-      }
-    }
-
-    function changeHorizontalRelease(event) {
-      if (checkPosition(event, "mozLinkEnd")) {
-        var press = document.getElementById("mozLinkStart");
-        var d = new Date();
-        var timeDiff = d.getTime() - press.innerHTML;
-        document.getElementById("mozLinkEnd").innerHTML = timeDiff;
-        
-      }
-    }
-
-    function changeClickText(strId) {
-      var second = document.getElementById(strId);
-      if (second.innerHTML == "Start") {
-        second.innerHTML = "End";
-      }
-      else if (second.innerHTML == "Context") {
-        second.innerHTML = "ContextEnd";
-      }
-      else {
-        second.innerHTML = "Error";
-      }
-    }
-
-    function changeScrollText(strId) {
-      var seventh = document.getElementById(strId);
-      if (elementInViewport(seventh)) {
-        seventh.innerHTML = "End";
-      }
-      else {
-        seventh.innerHTML = "Error";
-      }
-    }
-
-    function changeTimePress() {
-      var fourth = document.getElementById("mozLinkCopy2");
-      var d = new Date();
-      fourth.innerHTML = d.getTime();
+    attachListeners("button1");
+    attachListeners("button2");
+    attachListeners("button3");
+    attachListeners("button4");
+    attachListeners("buttonScroll");
+    addTimers("button3");
+    addTimers("button4");
+    var buttonFlick = document.getElementById("buttonFlick");
+    attachMouseListeners(buttonFlick);
+    function createDelayed() {
       var newButton = document.createElement("button");
       newButton.id = "delayed";
       newButton.setAttribute("style", "position:absolute;left:220px;top:455px;");
-      var content = document.createTextNode("Button6");
+      var content = document.createTextNode("delayed");
       newButton.appendChild(content);
       document.body.appendChild(newButton);
-    }
-
-    function changeTimeRelease(event) {
-      var fourth = document.getElementById("mozLinkCopy2");
-      if (fourth.innerHTML != "Button4") {
-        var d = new Date();
-        var timeDiff = d.getTime() - fourth.innerHTML;
-        fourth.innerHTML = timeDiff;
-      }
-      else {
-        fourth.innerHTML = "Error";
-      }
-      if (checkPosition(event, "delayed")) {
-        document.getElementById("delayed").innerHTML = "End";
-      }
-    }
-
-    function onContextMenuChange() {
-      var context = document.getElementById("mozLinkCopy");
-      context.innerHTML = "Context";
-    }
-
-    function elementInViewport(el) {
-      var top = el.offsetTop;
-      var left = el.offsetLeft;
-      var width = el.offsetWidth;
-      var height = el.offsetHeight;
-      while(el.offsetParent) {
-        el = el.offsetParent;
-        top += el.offsetTop;
-        left += el.offsetLeft;
-      }
-      return (top >= window.pageYOffset &&
-              left >= window.pageXOffset &&
-              (top + height) <= (window.pageYOffset + window.innerHeight) &&
-              (left + width) <= (window.pageXOffset + window.innerWidth));
-    }
+      newButton.addEventListener("mousemove", appendText("-mousemove"), false);
+      newButton.addEventListener("mouseup", appendText("-mouseup"), false);
+      newButton.addEventListener("click", appendText("-click"), false);
+    };
+    window.setTimeout(createDelayed, 5000);
   </script>
 </body>
 </html>
deleted file mode 100644
--- a/testing/marionette/client/marionette/www/testTouch.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<!DOCTYPE html>
-
-<html>
-<head>
-<script type="application/javascript" src="shim.js">
-</script>
-<title>Marionette Test</title>
-</head>
-<body>
-  <h1 id="testh1">Test Page</h1>
-  <script type="text/javascript">
-    window.ready = true;
-    setTimeout(addDelayedElement, 1000);  
-    function addDelayedElement() {
-      var newDiv = document.createElement("div");
-      newDiv.id = "newDiv";
-      var newContent = document.createTextNode("I am a newly created div!");
-      newDiv.appendChild(newContent);
-      document.body.appendChild(newDiv);
-    }
-    function clicked() {
-      var link = document.getElementById("mozLink");
-      link.innerHTML = "Clicked";
-    }
-    function clicked2() {
-      var link2 = document.getElementById("scroll");
-      link2.innerHTML = "Clicked";
-    }
-    function clicked3() {
-      var link3 = document.getElementById("mozLinkPos");
-      link3.innerHTML = "Clicked";
-    }
-  </script>
-  <button id="mozLink" style="position:absolute;left:0px;top:55px;" type="button" onclick="clicked()" allowevents=true>Click Me!</button>
-  <button id="mozLinkPos" style="position:absolute;left:0px;top:355px;" type="button" onclick="clicked3()" allowevents=true>Position!</button>
-  <div id="testDiv">
-    <a href="#" id="divLink" class="linkClass" onclick="clicked()">Div click me!</a>
-    <a href="#" id="divLink2" class="linkClass" onclick="clicked()">Div click me!</a>
-  </div>
-  <input name="myInput" type="text" value="asdf"/>
-  <input name="myCheckBox" type="checkbox" />
-  <h2 id="testh2" style="visibility: hidden" class="linkClass">Hidden</h2>
-  <h3 id="testh3">Voluntary Termination</h3>
-  <br style="margin-bottom:600px;"/>
-  <button id="scroll" type="button" onclick="clicked2()" allowevents=true>Click Me!</button>
-</body>
-</html>
--- a/testing/marionette/client/setup.py
+++ b/testing/marionette/client/setup.py
@@ -1,12 +1,12 @@
 import os
 from setuptools import setup, find_packages
 
-version = '0.5.27'
+version = '0.5.28'
 
 # get documentation from the README
 try:
     here = os.path.dirname(os.path.abspath(__file__))
     description = file(os.path.join(here, 'README.md')).read()
 except (OSError, IOError):
     description = ''
 
--- a/testing/marionette/marionette-actors.js
+++ b/testing/marionette/marionette-actors.js
@@ -1070,17 +1070,34 @@ MarionetteDriverActor.prototype = {
     }
   },
 
   /**
    * Gets the current title of the window
    */
   getTitle: function MDA_getTitle() {
     this.command_id = this.getCommandId();
-    this.sendAsync("getTitle", {}, this.command_id);
+    if (this.context == "chrome"){
+      var curWindow = this.getCurrentWindow();
+      var title = curWindow.document.documentElement.getAttribute('title');
+      this.sendResponse(title, this.command_id);
+    }
+    else {
+      this.sendAsync("getTitle", {}, this.command_id);
+    }
+  },
+
+  /**
+   * Gets the current type of the window
+   */
+  getWindowType: function MDA_getWindowType() {
+    this.command_id = this.getCommandId();
+      var curWindow = this.getCurrentWindow();
+      var type = curWindow.document.documentElement.getAttribute('windowtype');
+      this.sendResponse(type, this.command_id);
   },
 
   /**
    * Gets the page source of the content document
    */
   getPageSource: function MDA_getPageSource(){
     this.command_id = this.getCommandId();
     if (this.context == "chrome"){
@@ -1297,33 +1314,16 @@ MarionetteDriverActor.prototype = {
       this.sendError("Not a Number", 500, null, this.command_id);
     }
     else {
       this.searchTimeout = timeout;
       this.sendOk(this.command_id);
     }
   },
 
-/**
- * Set a value to decide if sending mouse event
- *
- * @param object aRequest
- *        'value' holds the boolean value
- */
- sendMouseEvent: function MDA_sendMouseEvent(aRequest) {
-   this.command_id = this.getCommandId();
-   if (this.context == "chrome") {
-     this.sendError("Not in Chrome", 500, null, this.command_id);
-    }
-    else {
-      this.sendAsync("sendMouseEvent", {value: aRequest.value,
-                                        command_id: this.command_id});
-    }
- },
-
   /**
    * Set timeout for page loading, searching and scripts
    *
    * @param object aRequest
    *        'type' hold the type of timeout
    *        'ms' holds the timeout in milliseconds
    */
   timeouts: function MDA_timeouts(aRequest){
@@ -1357,17 +1357,17 @@ MarionetteDriverActor.prototype = {
             'element' represents the ID of the element to single tap on
    */
   singleTap: function MDA_singleTap(aRequest) {
     this.command_id = this.getCommandId();
     let serId = aRequest.element;
     let x = aRequest.x;
     let y = aRequest.y;
     if (this.context == "chrome") {
-      this.sendError("Not in Chrome", 500, null, this.command_id);
+      this.sendError("Command 'singleTap' is not available in chrome context", 500, null, this.command_id);
     }
     else {
       this.sendAsync("singleTap",
                      {
                        value: serId,
                        corx: x,
                        cory: y
                      },
@@ -1379,17 +1379,17 @@ MarionetteDriverActor.prototype = {
    * actionChain
    *
    * @param object aRequest
    *        'value' represents a nested array: inner array represents each event; outer array represents collection of events
    */
   actionChain: function MDA_actionChain(aRequest) {
     this.command_id = this.getCommandId();
     if (this.context == "chrome") {
-      this.sendError("Not in Chrome", 500, null, this.command_id);
+      this.sendError("Command 'actionChain' is not available in chrome context", 500, null, this.command_id);
     }
     else {
       this.sendAsync("actionChain",
                      {
                        chain: aRequest.chain,
                        nextId: aRequest.nextId
                      },
                      this.command_id);
@@ -1403,17 +1403,17 @@ MarionetteDriverActor.prototype = {
    *        'value' represents a nested array: inner array represents each event;
    *        middle array represents collection of events for each finger
    *        outer array represents all the fingers
    */
 
   multiAction: function MDA_multiAction(aRequest) {
     this.command_id = this.getCommandId();
     if (this.context == "chrome") {
-       this.sendError("Not in Chrome", 500, null, this.command_id);
+       this.sendError("Command 'multiAction' is not available in chrome context", 500, null, this.command_id);
     }
     else {
       this.sendAsync("multiAction",
                      {
                        value: aRequest.value,
                        maxlen: aRequest.max_length
                      },
                      this.command_id);
@@ -2198,32 +2198,32 @@ MarionetteDriverActor.prototype.requestT
   "setScriptTimeout": MarionetteDriverActor.prototype.setScriptTimeout,
   "timeouts": MarionetteDriverActor.prototype.timeouts,
   "singleTap": MarionetteDriverActor.prototype.singleTap,
   "actionChain": MarionetteDriverActor.prototype.actionChain,
   "multiAction": MarionetteDriverActor.prototype.multiAction,
   "executeAsyncScript": MarionetteDriverActor.prototype.executeWithCallback,
   "executeJSScript": MarionetteDriverActor.prototype.executeJSScript,
   "setSearchTimeout": MarionetteDriverActor.prototype.setSearchTimeout,
-  "sendMouseEvent": MarionetteDriverActor.prototype.sendMouseEvent,
   "findElement": MarionetteDriverActor.prototype.findElement,
   "findElements": MarionetteDriverActor.prototype.findElements,
   "clickElement": MarionetteDriverActor.prototype.clickElement,
   "getElementAttribute": MarionetteDriverActor.prototype.getElementAttribute,
   "getElementText": MarionetteDriverActor.prototype.getElementText,
   "getElementTagName": MarionetteDriverActor.prototype.getElementTagName,
   "isElementDisplayed": MarionetteDriverActor.prototype.isElementDisplayed,
   "getElementValueOfCssProperty": MarionetteDriverActor.prototype.getElementValueOfCssProperty,
   "getElementSize": MarionetteDriverActor.prototype.getElementSize,
   "isElementEnabled": MarionetteDriverActor.prototype.isElementEnabled,
   "isElementSelected": MarionetteDriverActor.prototype.isElementSelected,
   "sendKeysToElement": MarionetteDriverActor.prototype.sendKeysToElement,
   "getElementPosition": MarionetteDriverActor.prototype.getElementPosition,
   "clearElement": MarionetteDriverActor.prototype.clearElement,
   "getTitle": MarionetteDriverActor.prototype.getTitle,
+  "getWindowType": MarionetteDriverActor.prototype.getWindowType,
   "getPageSource": MarionetteDriverActor.prototype.getPageSource,
   "goUrl": MarionetteDriverActor.prototype.goUrl,
   "getUrl": MarionetteDriverActor.prototype.getUrl,
   "goBack": MarionetteDriverActor.prototype.goBack,
   "goForward": MarionetteDriverActor.prototype.goForward,
   "refresh":  MarionetteDriverActor.prototype.refresh,
   "getWindow":  MarionetteDriverActor.prototype.getWindow,
   "getWindows":  MarionetteDriverActor.prototype.getWindows,
--- a/testing/marionette/marionette-listener.js
+++ b/testing/marionette/marionette-listener.js
@@ -52,29 +52,26 @@ let onunload;
 let asyncTestRunning = false;
 let asyncTestCommandId;
 let asyncTestTimeoutId;
 let originalOnError;
 //timer for doc changes
 let checkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
 // Send move events about this often
 let EVENT_INTERVAL = 30; // milliseconds
-// The current array of all pending touches
-let touches = [];
 // For assigning unique ids to all touches
 let nextTouchId = 1000;
+//Keep track of active Touches
 let touchIds = {};
 // last touch for each fingerId
 let multiLast = {};
-// last touch for single finger
-let lastTouch = null;
-// last touch type
-let isTouchStart = false;
+let lastCoordinates = null;
+let isTap = false;
 // whether to send mouse event
-let mouseEvent = true;
+let mouseEventsOnly = false;
 /**
  * Called when listener is first started up. 
  * The listener sends its unique window ID and its current URI to the actor.
  * If the actor returns an ID, we start the listeners. Otherwise, nothing happens.
  */
 function registerSelf() {
   let msg = {value: winUtil.outerWindowID, href: content.location.href};
   let register = sendSyncMessage("Marionette:register", msg);
@@ -106,17 +103,16 @@ function removeMessageListenerId(message
 function startListeners() {
   addMessageListenerId("Marionette:newSession", newSession);
   addMessageListenerId("Marionette:executeScript", executeScript);
   addMessageListenerId("Marionette:executeAsyncScript", executeAsyncScript);
   addMessageListenerId("Marionette:executeJSScript", executeJSScript);
   addMessageListenerId("Marionette:singleTap", singleTap);
   addMessageListenerId("Marionette:actionChain", actionChain);
   addMessageListenerId("Marionette:multiAction", multiAction);
-  addMessageListenerId("Marionette:sendMouseEvent", sendMouseEvent);
   addMessageListenerId("Marionette:goUrl", goUrl);
   addMessageListenerId("Marionette:getUrl", getUrl);
   addMessageListenerId("Marionette:getTitle", getTitle);
   addMessageListenerId("Marionette:getPageSource", getPageSource);
   addMessageListenerId("Marionette:goBack", goBack);
   addMessageListenerId("Marionette:goForward", goForward);
   addMessageListenerId("Marionette:refresh", refresh);
   addMessageListenerId("Marionette:findElementContent", findElementContent);
@@ -181,17 +177,16 @@ function restart(msg) {
 function deleteSession(msg) {
   removeMessageListenerId("Marionette:newSession", newSession);
   removeMessageListenerId("Marionette:executeScript", executeScript);
   removeMessageListenerId("Marionette:executeAsyncScript", executeAsyncScript);
   removeMessageListenerId("Marionette:executeJSScript", executeJSScript);
   removeMessageListenerId("Marionette:singleTap", singleTap);
   removeMessageListenerId("Marionette:actionChain", actionChain);
   removeMessageListenerId("Marionette:multiAction", multiAction);
-  removeMessageListenerId("Marionette:sendMouseEvent", sendMouseEvent);
   removeMessageListenerId("Marionette:goUrl", goUrl);
   removeMessageListenerId("Marionette:getTitle", getTitle);
   removeMessageListenerId("Marionette:getPageSource", getPageSource);
   removeMessageListenerId("Marionette:getUrl", getUrl);
   removeMessageListenerId("Marionette:goBack", goBack);
   removeMessageListenerId("Marionette:goForward", goForward);
   removeMessageListenerId("Marionette:refresh", refresh);
   removeMessageListenerId("Marionette:findElementContent", findElementContent);
@@ -219,17 +214,16 @@ function deleteSession(msg) {
   removeMessageListenerId("Marionette:addCookie", addCookie);
   removeMessageListenerId("Marionette:getAllCookies", getAllCookies);
   removeMessageListenerId("Marionette:deleteAllCookies", deleteAllCookies);
   removeMessageListenerId("Marionette:deleteCookie", deleteCookie);
   this.elementManager.reset();
   // reset frame to the top-most frame
   curWindow = content;
   curWindow.focus();
-  touches = [];
   touchIds = {};
 }
 
 /*
  * Helper methods 
  */
 
 /**
@@ -272,16 +266,17 @@ function sendError(message, status, trac
 }
 
 /**
  * Clear test values after completion of test
  */
 function resetValues() {
   sandbox = null;
   curWindow = content;
+  mouseEventsOnly = false;
 }
 
 /*
  * Marionette Methods
  */
 
 /**
  * Returns a content sandbox that can be used by the execute_foo functions.
@@ -535,248 +530,67 @@ function executeWithCallback(msg, useFin
   } catch (e) {
     // 17 = JavascriptException
     sandbox.asyncComplete(e.name + ': ' + e.message, 17,
                           e.stack, asyncTestCommandId);
   }
 }
 
 /**
- * This function sets a value for mouseEvent to decide if we want to send mouse events
- */
-function sendMouseEvent(msg) {
-  let command_id = msg.json.command_id;
-  if (typeof msg.json.value === 'boolean') {
-      mouseEvent = msg.json.value;
-      sendOk(msg.json.command_id);
-  }
-  else {
-    sendError("Json value is not boolean as expected", 500, null, command_id);
-  }
-}
-
-/**
  * This function creates a touch event given a touch type and a touch
  */
 function emitTouchEvent(type, touch) {
   // Using domWindowUtils
   let domWindowUtils = curWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils);
   domWindowUtils.sendTouchEvent(type, [touch.identifier], [touch.screenX], [touch.screenY], [touch.radiusX], [touch.radiusY], [touch.rotationAngle], [touch.force], 1, 0);
 }
 
 /**
  * This function emit mouse event
  *   @param: doc is the current document
  *           type is the type of event to dispatch
  *           detail is the number of clicks, button notes the mouse button
  *           elClientX and elClientY are the coordinates of the mouse relative to the viewport
  */
-function emitMouseEvent(doc, type, detail, button, elClientX, elClientY) {
+function emitMouseEvent(doc, type, elClientX, elClientY, detail, button) {
+  detail = detail || 1;
+  button = button || 0;
   var win = doc.defaultView;
   // Figure out the element the mouse would be over at (x, y)
   var target = doc.elementFromPoint(elClientX, elClientY);
-  utils.synthesizeMouse(target, win.mozInnerScreenX, win.mozInnerScreenY, {type: type, button: button, clickCount: detail}, win);
-}
-
-/**
- * This function create a mouse and emit mouse events
- * @param 'xt' and 'yt' are functions of t that specify the mouse coordinates at time t
- */
-function mouse(doc, duration, xt, yt, then, detail, button) {
-  detail = detail || 1;
-  button = button || 0;
-  var x = xt;
-  if (typeof xt !== 'function') {
-    x = function(t) {
-      return xt[0] + t / duration * (xt[1] - xt[0]);
-    }
-  }
-  var y = yt;
-  if (typeof yt !== 'function') {
-    y = function(t) {
-      return yt[0] + t / duration * (yt[1] - yt[0]);
-    }
-  }
-  // viewport coordinates
-  var clientX = Math.round(x(0)), clientY = Math.round(y(0));
-  // Remember the coordinates
-  var lastX = clientX, lastY = clientY;
-  emitMouseEvent(doc, 'mousedown', detail, button, clientX, clientY);
-  // now send a sequence of mousemove events followed by mouse up
-  let startTime = Date.now();
-  checkTimer.initWithCallback(nextEvent, EVENT_INTERVAL, Ci.nsITimer.TYPE_ONE_SHOT);
-  function nextEvent() {
-    // figure out if we've sent all the mousemove events
-    var time = Date.now();
-    var dt = time - startTime;
-    let last = dt + EVENT_INTERVAL / 2 > duration;
-    // New coordinates of the touch
-    clientX = Math.round(x(dt));
-    clientY = Math.round(y(dt));
-    // If we moved, send a move event
-    if (clientX !== lastX || clientY !== lastY) { // If we moved
-      lastX = clientX;
-      lastY = clientY;
-      emitMouseEvent(doc, 'mousemove', detail, button, clientX, clientY);
-    }
-    // If this was the last move, send a mouse up and call the callback
-    // Otherwise, schedule the next move event
-    if (last) {
-      emitMouseEvent(doc, 'mouseup', detail, button, lastX, lastY);
-      if (then) {
-        checkTimer.initWithCallback(then, 0, Ci.nsITimer.TYPE_ONE_SHOT);
-      }
-    }
-    else {
-      checkTimer.initWithCallback(nextEvent, EVENT_INTERVAL, Ci.nsITimer.TYPE_ONE_SHOT);
-    }
-  }
+  utils.synthesizeMouseAtPoint(elClientX, elClientY, {type: type, button: button, clickCount: detail}, win);
 }
 
 /**
- * This function creates a touch and emit touch events
- * @param 'xt' and 'yt' are two-element array [from, to] and then is a callback that will be invoked after touchend event is sent
+ * Helper function that perform a mouse tap
  */
-function touch(target, duration, xt, yt, then) {
-  let doc = target.ownerDocument;
-  let win = doc.defaultView;
-  let touchId = nextTouchId++;
-  let x = xt;
-  if (typeof xt !== 'function') {
-    x = function(t) { return xt[0] + t / duration * (xt[1] - xt[0]); };
-  }
-  let y = yt;
-  if (typeof yt !== 'function') {
-    y = function(t) { return yt[0] + t / duration * (yt[1] - yt[0]); };
-  }
-  // viewport coordinates
-  let clientX = Math.round(x(0)), clientY = Math.round(y(0));
-  // document coordinates
-  let pageX = clientX + win.pageXOffset,
-      pageY = clientY + win.pageYOffset;
-  // screen coordinates
-  let screenX = clientX + win.mozInnerScreenX,
-      screenY = clientY + win.mozInnerScreenY;
-  // Remember the coordinates
-  let lastX = clientX, lastY = clientY;
-  // Create the touch object
-  let touch = doc.createTouch(win, target, touchId,
-                              pageX, pageY,
-                              screenX, screenY,
-                              clientX, clientY);
-  // Add this new touch to the list of touches
-  touches.push(touch);
-  // Send the start event
-  emitTouchEvent('touchstart', touch);
-  let startTime = Date.now();
-  checkTimer.initWithCallback(nextEvent, EVENT_INTERVAL, Ci.nsITimer.TYPE_ONE_SHOT);
-  function nextEvent() {
-  // Figure out if this is the last of the touchmove events
-    let time = Date.now();
-    let dt = time - startTime;
-    let last = dt + EVENT_INTERVAL / 2 > duration;
-    // Find our touch object in the touches[] array.
-    // Note that its index may have changed since we pushed it
-    let touchIndex = touches.indexOf(touch);
-    // If this is the last move event, make sure we move all the way
-    if (last)
-       dt = duration;
-    // New coordinates of the touch
-    clientX = Math.round(x(dt));
-    clientY = Math.round(y(dt));
-    // If we've moved, send a move event
-    if (clientX !== lastX || clientY !== lastY) { // If we moved
-      lastX = clientX;
-      lastY = clientY;
-      pageX = clientX + win.pageXOffset;
-      pageY = clientY + win.pageYOffset;
-      screenX = clientX + win.mozInnerScreenX;
-      screenY = clientY + win.mozInnerScreenY;
-      // Since we moved, we've got to create a new Touch object
-      // with the new coordinates
-      touch = doc.createTouch(win, target, touchId,
-                              pageX, pageY,
-                              screenX, screenY,
-                              clientX, clientY);
-      // Replace the old touch object with the new one
-      touches[touchIndex] = touch;
-      // And send the touchmove event
-      emitTouchEvent('touchmove', touch);
-    }
-    // If that was the last move, send the touchend event
-    // and call the callback 
-    if (last) {
-      touches.splice(touchIndex, 1);
-      emitTouchEvent('touchend', touch);
-      if (then)
-        checkTimer.initWithCallback(then, 0, Ci.nsITimer.TYPE_ONE_SHOT);
-    }
-    // Otherwise, schedule the next event
-    else {
-      checkTimer.initWithCallback(nextEvent, EVENT_INTERVAL, Ci.nsITimer.TYPE_ONE_SHOT);
-    }
-  }
+function mousetap(doc, x, y) {
+  emitMouseEvent(doc, 'mousemove', x, y);
+  emitMouseEvent(doc, 'mousedown', x, y);
+  emitMouseEvent(doc, 'mouseup', x, y);
 }
 
+
 /**
- * This function generates the coordinates of the element
- * @param 'x0', 'y0', 'x1', and 'y1' are the relative to the target.
+ * This function generates a pair of coordinates relative to the viewport given a
+ * target element and coordinates relative to that element's top-left corner.
+ * @param 'x', and 'y' are the relative to the target.
  *        If they are not specified, then the center of the target is used.
  */
-function coordinates(target, x0, y0, x1, y1) {
-  let coords = {};
+function coordinates(target, x, y) {
   let box = target.getBoundingClientRect();
-  let tx0 = typeof x0;
-  let ty0 = typeof y0;
-  let tx1 = typeof x1;
-  let ty1 = typeof y1; 
-  function percent(s, x) {
-    s = s.trim();
-    let f = parseFloat(s);
-    if (s[s.length - 1] === '%')
-      f = f * x / 100;
-      return f;
-  }
-  function relative(s, x) {
-    let factor;
-    if (s[0] === '+')
-      factor = 1;
-    else
-      factor = -1;
-      return factor * percent(s.substring(1), x);
+  if (x == null) {
+    x = box.width / 2;
   }
-  if (tx0 === 'number')
-    coords.x0 = box.left + x0;
-  else if (tx0 === 'string')
-    coords.x0 = box.left + percent(x0, box.width);
-  //check tx1 point
-  if (tx1 === 'number')
-    coords.x1 = box.left + x1;
-  else if (tx1 === 'string') {
-    x1 = x1.trim();
-    if (x1[0] === '+' || x1[0] === '-')
-      coords.x1 = coords.x0 + relative(x1, box.width);
-    else
-      coords.x1 = box.left + percent(x1, box.width);
+  if (y == null) {
+    y = box.height / 2;
   }
-  // check ty0
-  if (ty0 === 'number')
-    coords.y0 = box.top + y0;
-  else if (ty0 === 'string')
-    coords.y0 = box.top + percent(y0, box.height);
-  //check ty1
-  if (ty1 === 'number')
-    coords.y1 = box.top + y1;
-  else if (ty1 === 'string') {
-    y1 = y1.trim();
-    if (y1[0] === '+' || y1[0] === '-')
-      coords.y1 = coords.y0 + relative(y1, box.height);
-    else
-      coords.y1 = box.top + percent(y1, box.height);
-  }
+  let coords = {};
+  coords.x = box.left + x;
+  coords.y = box.top + y;
   return coords;
 }
 
 /**
  * This function returns if the element is in viewport 
  */
 function elementInViewport(el) {
   let rect = el.getBoundingClientRect();
@@ -785,17 +599,17 @@ function elementInViewport(el) {
           rect.bottom <= (curWindow.pageYOffset + curWindow.innerHeight) &&
           rect.right <= (curWindow.pageXOffset + curWindow.innerWidth)
          );
 }
 
 /**
  * This function throws the visibility of the element error
  */
-function checkVisible(el, command_id) {
+function checkVisible(el) {
   //check if the element is visible
   let visible = utils.isElementDisplayed(el);
   if (!visible) {
     return false;
   }
   if (el.tagName.toLowerCase() === 'body') {
     return true;
   }
@@ -809,217 +623,224 @@ function checkVisible(el, command_id) {
     }
     else {
       return false;
     }
   }
   return true;
 }
 
+//x and y are coordinates relative to the viewport
+function generateEvents(type, x, y, touchId, target) {
+  lastCoordinates = [x, y];
+  let doc = curWindow.document;
+  switch (type) {
+    case 'tap':
+      if (mouseEventsOnly) {
+        mousetap(target.ownerDocument, x, y);
+      }
+      else {
+        let touchId = nextTouchId++;
+        let touch = createATouch(target, x, y, touchId);
+        emitTouchEvent('touchstart', touch);
+        emitTouchEvent('touchend', touch);
+        mousetap(target.ownerDocument, x, y);
+      }
+      lastCoordinates = null;
+      break;
+    case 'press':
+      isTap = true;
+      if (mouseEventsOnly) {
+        emitMouseEvent(doc, 'mousemove', x, y);
+        emitMouseEvent(doc, 'mousedown', x, y);
+      }
+      else {
+        let touchId = nextTouchId++;
+        let touch = createATouch(target, x, y, touchId);
+        emitTouchEvent('touchstart', touch);
+        touchIds[touchId] = touch;
+        return touchId;
+      }
+      break;
+    case 'release':
+      if (mouseEventsOnly) {
+        emitMouseEvent(doc, 'mouseup', lastCoordinates[0], lastCoordinates[1]);
+      }
+      else {
+        let touch = touchIds[touchId];
+        touch = createATouch(touch.target, lastCoordinates[0], lastCoordinates[1], touchId);
+        emitTouchEvent('touchend', touch);
+        if (isTap) {
+          mousetap(touch.target.ownerDocument, touch.clientX, touch.clientY);
+        }
+        delete touchIds[touchId];
+      }
+      isTap = false;
+      lastCoordinates = null;
+      break;
+    case 'cancel':
+      isTap = false;
+      if (mouseEventsOnly) {
+        emitMouseEvent(doc, 'mouseup', lastCoordinates[0], lastCoordinates[1]);
+      }
+      else {
+        emitTouchEvent('touchcancel', touchIds[touchId]);
+        delete touchIds[touchId];
+      }
+      lastCoordinates = null;
+      break;
+    case 'move':
+      isTap = false;
+      if (mouseEventsOnly) {
+        emitMouseEvent(doc, 'mousemove', x, y);
+      }
+      else {
+        touch = createATouch(touchIds[touchId].target, x, y, touchId);
+        touchIds[touchId] = touch;
+        emitTouchEvent('touchmove', touch);
+      }
+      break;
+    case 'contextmenu':
+      isTap = false;
+      let event = curWindow.document.createEvent('HTMLEvents');
+      event.initEvent('contextmenu', true, true);
+      if (mouseEventsOnly) {
+        target = doc.elementFromPoint(lastCoordinates[0], lastCoordinates[1]);
+      }
+      else {
+        target = touchIds[touchId].target;
+      }
+      target.dispatchEvent(event);
+      break;
+    default:
+      throw {message:"Unknown event type: " + type, code: 500, stack:null};
+  }
+}
+
 /**
  * Function that perform a single tap
  */
 function singleTap(msg) {
   let command_id = msg.json.command_id;
-  let el;
   try {
-    el = elementManager.getKnownElement(msg.json.value, curWindow);
-    let x = msg.json.corx;
-    let y = msg.json.cory;
-    if (!checkVisible(el, command_id)) {
-      sendError("Element is not currently visible and may not be manipulated", 11, null, command_id);
-      return;
-    }
-    if (x == null) {
-      x = '50%';
+    let el = elementManager.getKnownElement(msg.json.value, curWindow);
+    // after this block, the element will be scrolled into view
+    if (!checkVisible(el)) {
+       sendError("Element is not currently visible and may not be manipulated", 11, null, command_id);
+       return;
     }
-    if (y == null) {
-      y = '50%';
+    if (!curWindow.document.createTouch) {
+      mouseEventsOnly = true;
     }
-    let c = coordinates(el, x, y);
-    if (mouseEvent) {
-      touch(el, 25, [c.x0, c.x0], [c.y0, c.y0], function() {
-        mousetap(el, 25, c.x0, c.y0, 1, 0, null);
-      });
-    }
-    else {
-      touch(el, 25, [c.x0, c.x0], [c.y0, c.y0], null);
-    }
+    let c = coordinates(el, msg.json.corx, msg.json.cory);
+    generateEvents('tap', c.x, c.y, null, el);
     sendOk(msg.json.command_id);
   }
   catch (e) {
     sendError(e.message, e.code, e.stack, msg.json.command_id);
   }
 }
 
 /**
- * Function that perform a mouse tap
+ * Function to create a touch based on the element
+ * corx and cory are relative to the viewport, id is the touchId
  */
-function mousetap(target, duration, x, y, detail, button, then) {
-  var doc = target.ownerDocument;
-  detail = detail || 1;
-  button = button || 0;
-  emitMouseEvent(doc, 'mousemove', detail, button, x, y);
-  mouse(doc, duration, [x, x], [y, y], then, detail, button);
-}
-
-/**
- * Function to create a touch based on the element
- * corx and cory are related to the el, id is the touchId
- */
-function createATouch(el, corx, cory, id) {
+function createATouch(el, corx, cory, touchId) {
   let doc = el.ownerDocument;
   let win = doc.defaultView;
-  if (corx == null) {
-    corx = '50%';
-  }
-  if (cory == null){
-    cory = '50%';
-  }
-  // corx and cory are relative to the el target. They must be within the same viewport
-  // c are the coordinates relative to the current viewport
-  let c = coordinates(el, corx, cory);
-  let clientX = Math.round(c.x0),
-      clientY = Math.round(c.y0);
+  let clientX = corx;
+  let clientY = cory;
   let pageX = clientX + win.pageXOffset,
       pageY = clientY + win.pageYOffset;
   let screenX = clientX + win.mozInnerScreenX,
       screenY = clientY + win.mozInnerScreenY;
-  let atouch = doc.createTouch(win, el, id, pageX, pageY, screenX, screenY, clientX, clientY);
+  let atouch = doc.createTouch(win, el, touchId, pageX, pageY, screenX, screenY, clientX, clientY);
   return atouch;
 }
 
 /**
  * Function to emit touch events for each finger. e.g. finger=[['press', id], ['wait', 5], ['release']]
- * touchId represents the finger id, i keeps track of the current action of the finger
+ * touchId represents the finger id, i keeps track of the current action of the chain 
  */
-function actions(finger, touchId, command_id, i){
+function actions(chain, touchId, command_id, i) {
   if (typeof i === "undefined") {
     i = 0;
   }
-  if (i == finger.length) {
+  if (i == chain.length) {
     sendResponse({value: touchId}, command_id);
     return;
   }
-  let pack = finger[i];
+  let pack = chain[i];
   let command = pack[0];
-  // el has the id
   let el;
-  let corx;
-  let cory;
-  let touch;
-  let contextmenu = false;
+  let c;
   i++;
+  if (command != 'press') {
+    //if mouseEventsOnly, then touchIds isn't used
+    if (!(touchId in touchIds) && !mouseEventsOnly) {
+      sendError("Element has not been pressed", 500, null, command_id);
+      return;
+    }
+  }
   switch(command) {
     case 'press':
-      if (lastTouch != null) {
-        touch = lastTouch;
-        emitTouchEvent('touchcancel', touch);
-        lastTouch = null;
-        sendError("Invalid Command: long_press cannot follow an active touch event", 500, null, command_id);
+      if (lastCoordinates) {
+        generateEvents('cancel', lastCoordinates[0], lastCoordinates[1], touchId);
+        sendError("Invalid Command: press cannot follow an active touch event", 500, null, command_id);
         return;
       }
       el = elementManager.getKnownElement(pack[1], curWindow);
-      corx = pack[2];
-      cory = pack[3];
-      // after this block, the element will be scrolled into view
-      if (!checkVisible(el, command_id)) {
+      if (!checkVisible(el)) {
          sendError("Element is not currently visible and may not be manipulated", 11, null, command_id);
          return;
       }
-      touch = createATouch(el, corx, cory, touchId);
-      lastTouch = touch;
-      isTouchStart = true;
-      emitTouchEvent('touchstart', touch);
-      actions(finger,touchId, command_id, i);
+      c = coordinates(el, pack[2], pack[3]);
+      touchId = generateEvents('press', c.x, c.y, null, el);
+      actions(chain, touchId, command_id, i);
       break;
     case 'release':
-      if (lastTouch == null) {
-        sendError("Element has not been pressed: no such element", 7, null, command_id);
-        return;
-      }
-      touch = lastTouch;
-      lastTouch = null;
-      emitTouchEvent('touchend', touch);
-      if (isTouchStart && mouseEvent) {
-        emitMouseEvent(touch.target.ownerDocument, 'mousemove', 1, 0, touch.clientX, touch.clientY);
-        emitMouseEvent(touch.target.ownerDocument, 'mousedown', 1, 0, touch.clientX, touch.clientY);
-        emitMouseEvent(touch.target.ownerDocument, 'mouseup', 1, 0, touch.clientX, touch.clientY);
-      }
-      isTouchStart = false;
-      actions(finger, touchId, command_id, i);
+      generateEvents('release', lastCoordinates[0], lastCoordinates[1], touchId);
+      actions(chain, null, command_id, i);
       break;
     case 'move':
-      if (lastTouch == null) {
-        sendError("Element has not been pressed: no such element", 7, null, command_id);
-        return;
-      }
       el = elementManager.getKnownElement(pack[1], curWindow);
-      let boxTarget = el.getBoundingClientRect();
-      let startElement = lastTouch.target;
-      let boxStart = startElement.getBoundingClientRect();
-      corx = boxTarget.left - boxStart.left + boxTarget.width * 0.5;
-      cory = boxTarget.top - boxStart.top + boxTarget.height * 0.5;
-      touch = createATouch(startElement, corx, cory, touchId);
-      lastTouch = touch;
-      isTouchStart = false;
-      emitTouchEvent('touchmove', touch);
-      actions(finger, touchId, command_id, i);
+      c = coordinates(el);
+      generateEvents('move', c.x, c.y, touchId);
+      actions(chain, touchId, command_id, i);
       break;
     case 'moveByOffset':
-      if (lastTouch == null) {
-        sendError("Element has not been pressed: no such element", 7, null, command_id);
-        return;
-      }
-      el = lastTouch.target;
-      let doc = el.ownerDocument;
-      let win = doc.defaultView;
-      let clientX = lastTouch.clientX + pack[1],
-          clientY = lastTouch.clientY + pack[2];
-      let pageX = clientX + win.pageXOffset,
-          pageY = clientY + win.pageYOffset;
-      let screenX = clientX + win.mozInnerScreenX,
-          screenY = clientY + win.mozInnerScreenY;
-      touch = doc.createTouch(win, el, touchId, pageX, pageY, screenX, screenY, clientX, clientY);
-      lastTouch = touch;
-      isTouchStart = false;
-      emitTouchEvent('touchmove', touch);
-      actions(finger, touchId, command_id, i);
+      generateEvents('move', lastCoordinates[0] + pack[1], lastCoordinates[1] + pack[2], touchId);
+      actions(chain, touchId, command_id, i);
       break;
     case 'wait':
       if (pack[1] != null ) {
         let time = pack[1]*1000;
         // standard waiting time to fire contextmenu
-        let standard = Services.prefs.getIntPref("ui.click_hold_context_menus.delay");
-        if (time >= standard && isTouchStart && !contextmenu) {
-            finger.splice(i, 0, ['longPress'], ['wait', (time-standard)/1000]);
+        let standard = 750;
+        try {
+          standard = Services.prefs.getIntPref("ui.click_hold_context_menus.delay");
+        }
+        catch (e){}
+        if (time >= standard && isTap) {
+            chain.splice(i, 0, ['longPress'], ['wait', (time-standard)/1000]);
             time = standard;
         }
-        checkTimer.initWithCallback(function(){actions(finger, touchId, command_id, i);}, time, Ci.nsITimer.TYPE_ONE_SHOT);
+        checkTimer.initWithCallback(function(){actions(chain, touchId, command_id, i);}, time, Ci.nsITimer.TYPE_ONE_SHOT);
       }
       else {
-        actions(finger, touchId, command_id, i);
+        actions(chain, touchId, command_id, i);
       }
       break;
     case 'cancel':
-      touch = lastTouch;
-      emitTouchEvent('touchcancel', touch);
-      lastTouch = null;
-      isTouchStart = false;
-      actions(finger, touchId, command_id, i);
+      generateEvents('cancel', lastCoordinates[0], lastCoordinates[1], touchId);
+      actions(chain, touchId, command_id, i);
       break;
     case 'longPress':
-      isTouchStart = false;
-      contextmenu = true;
-      let event = curWindow.document.createEvent('HTMLEvents');
-      event.initEvent('contextmenu',
-                      true,
-                      true);
-      lastTouch.target.dispatchEvent(event);
-      actions(finger, touchId, command_id, i);
+      generateEvents('contextmenu', lastCoordinates[0], lastCoordinates[1], touchId);
+      actions(chain, touchId, command_id, i);
       break;
   }
 }
 
 /**
  * Function to start action chain on one finger 
  */
 function actionChain(msg) {
@@ -1027,16 +848,19 @@ function actionChain(msg) {
   let args = msg.json.chain;
   let touchId = msg.json.nextId;
   try {
     let commandArray = elementManager.convertWrappedArguments(args, curWindow);
     // loop the action array [ ['press', id], ['move', id], ['release', id] ]
     if (touchId == null) {
       touchId = nextTouchId++;
     }
+    if (!curWindow.document.createTouch) {
+      mouseEventsOnly = true;
+    }
     actions(commandArray, touchId, command_id);
   }
   catch (e) {
     sendError(e.message, e.code, e.stack, msg.json.command_id);
   }
 }
 
 /**
@@ -1097,55 +921,43 @@ function setDispatch(batches, touches, c
   let el;
   let corx;
   let cory;
   let touch;
   let lastTouch;
   let touchIndex;
   let waitTime = 0;
   let maxTime = 0;
+  let c;
   batchIndex++;
   // loop through the batch
   for (let i = 0; i < batch.length; i++) {
     pack = batch[i];
     touchId = pack[0];
     command = pack[1];
     switch (command) {
       case 'press':
         el = elementManager.getKnownElement(pack[2], curWindow);
-        // after this block, the element will be scrolled into view
-        if (!checkVisible(el, command_id)) {
-           sendError("Element is not currently visible and may not be manipulated", 11, null, command_id);
-           return;
-        }
-        corx = pack[3];
-        cory = pack[4];
-        touch = createATouch(el, corx, cory, touchId);
+        c = coordinates(el, pack[3], pack[4]);
+        touch = createATouch(el, c.x, c.y, touchId);
         multiLast[touchId] = touch;
         touches.push(touch);
         emitMultiEvents('touchstart', touch, touches);
         break;
       case 'release':
         touch = multiLast[touchId];
         // the index of the previous touch for the finger may change in the touches array
         touchIndex = touches.indexOf(touch);
         touches.splice(touchIndex, 1);
         emitMultiEvents('touchend', touch, touches);
         break;
       case 'move':
         el = elementManager.getKnownElement(pack[2], curWindow);
-        lastTouch = multiLast[touchId];
-        let boxTarget = el.getBoundingClientRect();
-        let startTarget = lastTouch.target;
-        let boxStart = startTarget.getBoundingClientRect();
-        // note here corx and cory are relative to the target, not the viewport
-        // we always want to touch the center of the element if the element is specified
-        corx = boxTarget.left - boxStart.left + boxTarget.width * 0.5;
-        cory = boxTarget.top - boxStart.top + boxTarget.height * 0.5;
-        touch = createATouch(startTarget, corx, cory, touchId);
+        c = coordinates(el);
+        touch = createATouch(multiLast[touchId].target, c.x, c.y, touchId);
         touchIndex = touches.indexOf(lastTouch);
         touches[touchIndex] = touch;
         multiLast[touchId] = touch;
         emitMultiEvents('touchmove', touch, touches);
         break;
       case 'moveByOffset':
         el = multiLast[touchId].target;
         lastTouch = multiLast[touchId];
@@ -1350,17 +1162,17 @@ function getActiveElement(msg) {
 /**
  * Send click event to element
  */
 function clickElement(msg) {
   let command_id = msg.json.command_id;
   let el;
   try {
     el = elementManager.getKnownElement(msg.json.element, curWindow);
-    if (checkVisible(el, command_id)) {
+    if (checkVisible(el)) {
       if (utils.isElementEnabled(el)) {
         utils.synthesizeMouseAtCenter(el, {}, el.ownerDocument.defaultView)
       }
       else {
         sendError("Element is not Enabled", 12, null, command_id)
       }
     }
     else {
@@ -1497,17 +1309,17 @@ function isElementSelected(msg) {
 
 /**
  * Send keys to element
  */
 function sendKeysToElement(msg) {
   let command_id = msg.json.command_id;
   try {
     let el = elementManager.getKnownElement(msg.json.element, curWindow);
-    if (checkVisible(el, command_id)) {
+    if (checkVisible(el)) {
       utils.type(curWindow.document, el, msg.json.value.join(""), true);
       sendOk(command_id);
     }
     else {
       sendError("Element is not visible", 11, null, command_id)
     }
   }
   catch (e) {
--- a/toolkit/components/places/nsNavHistoryResult.cpp
+++ b/toolkit/components/places/nsNavHistoryResult.cpp
@@ -287,25 +287,19 @@ nsNavHistoryResultNode::GetGeneratingOpt
     cur = cur->mParent;
   }
 
   // We should always find a container node as an ancestor.
   NS_NOTREACHED("Can't find a generating node for this container, the tree seemes corrupted.");
   return nullptr;
 }
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResult)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildren)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END 
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResult)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildren)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED_2(nsNavHistoryContainerResultNode, nsNavHistoryResultNode,
+                                     mResult,
+                                     mChildren)
 
 NS_IMPL_ADDREF_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode)
 NS_IMPL_RELEASE_INHERITED(nsNavHistoryContainerResultNode, nsNavHistoryResultNode)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsNavHistoryContainerResultNode)
   NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsNavHistoryContainerResultNode)
   NS_INTERFACE_MAP_ENTRY(nsINavHistoryContainerResultNode)
 NS_INTERFACE_MAP_END_INHERITING(nsNavHistoryResultNode)
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -155,16 +155,19 @@ using namespace mozilla;
 
 //#define COLLECT_TIME_DEBUG
 
 // Enable assertions that are useful for diagnosing errors in graph construction.
 //#define DEBUG_CC_GRAPH
 
 #define DEFAULT_SHUTDOWN_COLLECTIONS 5
 
+// One to do the freeing, then another to detect there is no more work to do.
+#define NORMAL_SHUTDOWN_COLLECTIONS 2
+
 #if defined(XP_WIN)
 // Defined in nsThreadManager.cpp.
 extern DWORD gTLSThreadIDIndex;
 #elif defined(NS_TLS)
 // Defined in nsThreadManager.cpp.
 extern NS_TLS mozilla::threads::ID gTLSThreadID;
 #else
 PRThread* gCycleCollectorThread = nullptr;
@@ -2533,16 +2536,17 @@ nsCycleCollector::nsCycleCollector(CCThr
 {
     nsRefPtr<nsCycleCollectorRunner> runner =
         new nsCycleCollectorRunner(this, aModel);
     runner.forget(&mRunner);
 }
 
 nsCycleCollector::~nsCycleCollector()
 {
+    NS_ASSERTION(!mRunner, "Destroying cycle collector without destroying its runner, may leak");
     NS_UnregisterMemoryMultiReporter(mReporter);
 }
 
 nsresult
 nsCycleCollector::Init()
 {
   return mRunner->Init();
 }
@@ -2748,16 +2752,18 @@ void
 nsCycleCollector::ShutdownCollect(nsICycleCollectorListener *aListener)
 {
     nsAutoTArray<PtrInfo*, 4000> whiteNodes;
 
     if (!PrepareForCollection(nullptr, &whiteNodes))
         return;
 
     for (uint32_t i = 0; i < DEFAULT_SHUTDOWN_COLLECTIONS; ++i) {
+        NS_WARN_IF_FALSE(i < NORMAL_SHUTDOWN_COLLECTIONS, "Extra shutdown CC");
+
         // Synchronous cycle collection. Always force a JS GC beforehand.
         FixGrayBits(true);
         if (aListener && NS_FAILED(aListener->Begin()))
             aListener = nullptr;
         if (!(BeginCollection(ShutdownCC, aListener) &&
               FinishCollection(aListener)))
             break;
     }
--- a/xpcom/glue/nsCycleCollectionParticipant.h
+++ b/xpcom/glue/nsCycleCollectionParticipant.h
@@ -1167,11 +1167,61 @@ struct Skippable
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f3)                                        \
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f4)                                        \
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f5)                                        \
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f6)                                        \
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f7)                                        \
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f8)                                        \
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
+#define NS_IMPL_CYCLE_COLLECTION_INHERITED_9(_class, _base, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(_class, _base)                \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f1)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f2)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f3)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f4)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f5)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f6)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f7)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f8)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f9)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END                                           \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(_class, _base)              \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f1)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f2)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f3)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f4)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f5)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f6)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f7)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f8)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f9)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+#define NS_IMPL_CYCLE_COLLECTION_INHERITED_10(_class, _base, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _f10) \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(_class, _base)                \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f1)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f2)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f3)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f4)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f5)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f6)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f7)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f8)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f9)                                          \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(_f10)                                         \
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_END                                           \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(_class, _base)              \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f1)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f2)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f3)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f4)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f5)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f6)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f7)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f8)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f9)                                        \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(_f10)                                       \
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
 #define NS_CYCLE_COLLECTION_NOTE_EDGE_NAME CycleCollectionNoteEdgeName
 
 #endif // nsCycleCollectionParticipant_h__
--- a/xpcom/tests/static-checker/Makefile.in
+++ b/xpcom/tests/static-checker/Makefile.in
@@ -4,32 +4,16 @@
 
 DEPTH = @DEPTH@
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-STACK_FAILURE_TESTCASES = \
-  TestStack.cpp \
-  TestStackTemplate.cpp \
-  StackNoConstructor.cpp \
-  StackVector.cpp \
-  StackConditional.cpp \
-  StackAggrInit.cpp \
-  StackCondBad.cpp \
-  StackFalsePositive.cpp \
-  $(NULL)
-
-STACK_PASS_TESTCASES = \
-  StackPlacementNewOK.cpp \
-  StackCond.cpp \
-  $(NULL)
-
 OUTPARAMS_WARNING_TESTCASES = \
   e1.cpp \
   e4.cpp \
   e6.cpp \
   e7.cpp \
   e8.cpp \
   e9.cpp \
   e10.cpp \
@@ -110,24 +94,22 @@ STATIC_INIT_WARNING_TESTCASES = \
 STATIC_FAILURE_TESTCASES = \
   $(FLOW_FAILURE_TESTCASES) \
   $(MUST_OVERRIDE_FAILURE_TESTCASES) \
   $(OVERRIDE_FAILURE_TESTCASES) \
   $(NULL)
 
 STATIC_WARNING_TESTCASES = \
   $(OUTPARAMS_WARNING_TESTCASES) \
-  $(STACK_FAILURE_TESTCASES) \
   $(STATIC_INIT_WARNING_TESTCASES) \
   $(NULL)
 
 STATIC_PASS_TESTCASES = \
   $(OUTPARAMS_NS_FAILED_TESTCASES) \
   $(OUTPARAMS_PASS_TESTCASES) \
-  $(STACK_PASS_TESTCASES) \
   $(FLOW_PASS_TESTCASES) \
   $(MUST_OVERRIDE_PASS_TESTCASES) \
   $(OVERRIDE_PASS_TESTCASES) \
   $(STATIC_INIT_PASS_TESTCASES) \
   $(NULL)
 
 
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/xpcom/tests/static-checker/StackAggrInit.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-#include "nscore.h"
-#include "nsAutoPtr.h"
-
-class NS_STACK_CLASS A
-{
-  int i;
-};
-
-void Foo()
-{
-  nsAutoPtr<A> a(new A);
-}
deleted file mode 100644
--- a/xpcom/tests/static-checker/StackCond.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-#include "nscore.h"
-
-struct A
-{
-};
-
-A* tfunc(int len)
-{
-  A arr[5];
-  A* a = len <= 5 ? arr : new A[len];
-  return a;
-}
deleted file mode 100644
--- a/xpcom/tests/static-checker/StackCondBad.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-#include "nscore.h"
-
-struct NS_STACK_CLASS A
-{
-};
-
-A* tfunc(int len)
-{
-  A arr[5];
-  A* a = len <= 5 ? arr : new A[len];
-  return a;
-}
deleted file mode 100644
--- a/xpcom/tests/static-checker/StackConditional.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#define NS_STACK_CLASS __attribute__((user("NS_stack")))
-
-struct NS_STACK_CLASS A
-{
-  int i;
-};
-
-void
-GetIt(int i)
-{
-  A *a;
-
-  if (i)
-    a = new A();
-}
deleted file mode 100644
--- a/xpcom/tests/static-checker/StackFalsePositive.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-#include <new>
-#include "nscore.h"
-
-struct NS_STACK_CLASS A
-{
-};
-
-void foo() {
-  A* a;
-  void* v = ::operator new(16);
-  a = (A*) v; // tricks analysis
-}
deleted file mode 100644
--- a/xpcom/tests/static-checker/StackNoConstructor.cpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "nscore.h"
-
-struct NS_STACK_CLASS A
-{
-  int i;
-};
-
-void* Foo()
-{
-  return new A();
-}
deleted file mode 100644
--- a/xpcom/tests/static-checker/StackPlacementNewOK.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "nscore.h"
-#include NEW_H
-
-struct NS_STACK_CLASS A
-{
-  int i;
-};
-
-int Foo()
-{
-  char buf[sizeof(A)];
-
-  A *a = new(&buf) A;
-  return a->i;
-}
deleted file mode 100644
--- a/xpcom/tests/static-checker/StackVector.cpp
+++ /dev/null
@@ -1,12 +0,0 @@
-#include "nscore.h"
-
-struct NS_STACK_CLASS A
-{
-  int i;
-};
-
-void* Foo()
-{
-  A *a = new A[8];
-  return a;
-}
deleted file mode 100644
--- a/xpcom/tests/static-checker/TestStack.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#include "nscore.h"
-
-struct NS_STACK_CLASS A
-{
-  // BUG: currently classes which are marked NS_STACK_CLASS must have a
-  // constructor
-  A();
-
-  int i;
-};
-
-void* Foo()
-{
-  return new A();
-}
deleted file mode 100644
--- a/xpcom/tests/static-checker/TestStackTemplate.cpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#include "nscore.h"
-
-template<class T>
-struct NS_STACK_CLASS A
-{
-  A();
-
-  T i;
-};
-
-void *Foo()
-{
-  return new A<int>();
-}