Merge the last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 09 Aug 2012 17:56:47 -0400
changeset 102034 9efc7b150f4178452d9f159bcbe2b0cda1979362
parent 101992 ed5c848f50e0d48341e4f7ee5ffbff74f650e587 (current diff)
parent 101967 2adcd343440494c5acf39d548ede7b47e2caab2e (diff)
child 102035 08e0e3b95f03ad78cb3eb1e7cd00b918838cdad3
child 102289 aa23416861c53f6fe8d847f7a08db4c3dac41dc7
push id13285
push userryanvm@gmail.com
push dateFri, 10 Aug 2012 02:57:21 +0000
treeherdermozilla-inbound@8d17980dc06d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge the last PGO-green inbound changeset to m-c.
--- a/build/mobile/robocop/Assert.java.in
+++ b/build/mobile/robocop/Assert.java.in
@@ -5,18 +5,18 @@
 
 package @ANDROID_PACKAGE_NAME@;
 
 public interface Assert {
     void dumpLog(String message);
     void dumpLog(String message, Throwable t);
     void setLogFile(String filename);
     void setTestName(String testName);
+    void endTest();
 
-    void finalize();
     void ok(boolean condition, String name, String diag);
     void is(Object a, Object b, String name);
     void isnot(Object a, Object b, String name);
     void todo(boolean condition, String name, String diag);
     void todo_is(Object a, Object b, String name);
     void todo_isnot(Object a, Object b, String name);
     void info(String name, String message);
 
--- a/build/mobile/robocop/FennecMochitestAssert.java.in
+++ b/build/mobile/robocop/FennecMochitestAssert.java.in
@@ -103,18 +103,17 @@ public class FennecMochitestAssert imple
         } else {
             mPassed++;
         }
         if (isError) {
             junit.framework.Assert.fail(message);
         }
     }
 
-    public void finalize() {
-        // It appears that we call finalize during cleanup, this might be an invalid assertion.
+    public void endTest() {
         String message;
 
         if (mLogTestName != "") {
             long diff = SystemClock.uptimeMillis() - mStartTime;
             message = Integer.toString(mLineNumber++) + " INFO TEST-END | " + mLogTestName;
             message += " | finished in " + diff + "ms";
             dumpLog(message);
             mLogTestName = "";
--- a/build/mobile/robocop/FennecTalosAssert.java.in
+++ b/build/mobile/robocop/FennecTalosAssert.java.in
@@ -26,17 +26,17 @@ public class FennecTalosAssert implement
      *  Set the filename used for dumpLog.
      */
     public void setLogFile(String filename) {
         FennecNativeDriver.setLogFile(filename);
     }
 
     public void setTestName(String testName) { }
 
-    public void finalize() { }
+    public void endTest() { }
 
     public void ok(boolean condition, String name, String diag) {
         if (!condition) {
             dumpLog("__FAIL" + name + ": " + diag + "__FAIL");
         }
     }
 
     public void is(Object a, Object b, String name) {
--- a/build/unix/elfhack/Makefile.in
+++ b/build/unix/elfhack/Makefile.in
@@ -33,66 +33,73 @@ CPU := arm
 else
 CPU := $(TARGET_CPU)
 endif
 endif
 
 CSRCS := \
   inject/$(CPU).c \
   inject/$(CPU)-noinit.c \
-  test.c \
+  test-ctors.c \
+  test-array.c \
   $(NULL)
 
 ifndef CROSS_COMPILE
 CSRCS += dummy.c
 endif
 
 WRAP_LDFLAGS=
 
 # need this to suppress errors due to /usr/include/linux/byteorder/swab.h
 # on mozilla buildbots
 OS_CXXFLAGS := $(filter-out -pedantic,$(OS_CXXFLAGS))
 
 include $(topsrcdir)/config/rules.mk
 
-test$(DLL_SUFFIX): test.$(OBJ_SUFFIX) elfhack $(CSRCS:.c=.$(OBJ_SUFFIX))
-	$(MKSHLIB) $(LDFLAGS) $<
+test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX): %$(DLL_SUFFIX): %.$(OBJ_SUFFIX) elfhack $(filter inject/%,$(CSRCS:.c=.$(OBJ_SUFFIX)))
+	$(MKSHLIB) $(LDFLAGS) $< -nostartfiles
 	@echo ===
 	@echo === If you get failures below, please file a bug describing the error
 	@echo === and your environment \(compiler and linker versions\), and use
 	@echo === --disable-elf-hack until this is fixed.
 	@echo ===
+	# Fail if the library doesn't have $(DT_TYPE) .dynamic info
+	$(TOOLCHAIN_PREFIX)readelf -d $@ | grep '($(DT_TYPE))'
 	@rm -f $@.bak
 	$(CURDIR)/elfhack -b -f $@
 	# Fail if the backup file doesn't exist
 	[ -f "$@.bak" ]
 	# Fail if the new library doesn't contain less relocations
-	[ $$(objdump -R $@.bak | wc -l) -gt $$(objdump -R $@ | wc -l) ]
+	[ $$($(TOOLCHAIN_PREFIX)objdump -R $@.bak | wc -l) -gt $$(objdump -R $@ | wc -l) ]
 
-.PRECIOUS: test$(DLL_SUFFIX)
+test-array$(DLL_SUFFIX): DT_TYPE=INIT_ARRAY
+test-ctors$(DLL_SUFFIX): DT_TYPE=INIT
 
-GARBAGE += test$(DLL_SUFFIX) test$(DLL_SUFFIX).bak
+.PRECIOUS: test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX)
 
-libs:: test$(DLL_SUFFIX)
+GARBAGE += test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX) test-array$(DLL_SUFFIX).bak test-ctors$(DLL_SUFFIX).bak
+
+libs:: test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX)
 
 ifndef CROSS_COMPILE
-dummy: dummy.$(OBJ_SUFFIX) test$(DLL_SUFFIX)
+dummy: dummy.$(OBJ_SUFFIX)
 	$(CC) -o $@ $^ $(LDFLAGS)
 
 libs:: dummy
 	# Will either crash or return exit code 1 if elfhack is broken
-	LD_LIBRARY_PATH=$(CURDIR) $(CURDIR)/dummy
+	LD_PRELOAD=$(CURDIR)/test-array$(DLL_SUFFIX) $(CURDIR)/dummy
+	LD_PRELOAD=$(CURDIR)/test-ctors$(DLL_SUFFIX) $(CURDIR)/dummy
 
 GARBAGE += dummy
 endif
 
 inject:
 	$(NSINSTALL) -D $@
 
-inject/%.c: inject.c | inject
+inject/%.c: inject.c $(call mkdir_deps,inject)
 	cp $< $@
 
 GARBAGE_DIRS += inject
 
 inject/%.$(OBJ_SUFFIX): DEFINES += -DBITS=$(if $(HAVE_64BIT_OS),64,32)
 inject/%.$(OBJ_SUFFIX): CFLAGS := -O2 -fno-stack-protector $(filter -m% -I%,$(CFLAGS))
 inject/$(CPU)-noinit.$(OBJ_SUFFIX): DEFINES += -DNOINIT
 test.$(OBJ_SUFFIX): CFLAGS := -O0
--- a/build/unix/elfhack/dummy.c
+++ b/build/unix/elfhack/dummy.c
@@ -1,9 +1,9 @@
 /* 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/. */
 
-extern __attribute__((visibility("default"))) int print_status();
+extern __attribute__((visibility("default"), weak)) int print_status();
 
 int main() {
     return print_status();
 }
--- a/build/unix/elfhack/elf.cpp
+++ b/build/unix/elfhack/elf.cpp
@@ -342,17 +342,17 @@ ElfDynamic_Section *Elf::getDynSection()
     for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++)
         if (((*seg)->getType() == PT_DYNAMIC) && ((*seg)->getFirstSection() != NULL) &&
             (*seg)->getFirstSection()->getType() == SHT_DYNAMIC)
             return (ElfDynamic_Section *)(*seg)->getFirstSection();
 
     return NULL;
 }
 
-void Elf::write(std::ofstream &file)
+void Elf::normalize()
 {
     // fixup section headers sh_name; TODO: that should be done by sections
     // themselves
     for (ElfSection *section = ehdr; section != NULL; section = section->getNext()) {
         if (section->getIndex() == 0)
             continue;
         else
             ehdr->e_shnum = section->getIndex() + 1;
@@ -382,16 +382,21 @@ void Elf::write(std::ofstream &file)
         phdr_section->getNext()->markDirty();
     }
     // fixup shdr before writing
     if (ehdr->e_shnum != shdr_section->getSize() / shdr_section->getEntSize())
         shdr_section->getShdr().sh_size = ehdr->e_shnum * Elf_Shdr::size(ehdr->e_ident[EI_CLASS]);
     ehdr->e_shoff = shdr_section->getOffset();
     ehdr->e_entry = eh_entry.getValue();
     ehdr->e_shstrndx = eh_shstrndx->getIndex();
+}
+
+void Elf::write(std::ofstream &file)
+{
+    normalize();
     for (ElfSection *section = ehdr;
          section != NULL; section = section->getNext()) {
         file.seekp(section->getOffset());
         if (section == phdr_section) {
             for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++) {
                 Elf_Phdr phdr;
                 phdr.p_type = (*seg)->getType();
                 phdr.p_flags = (*seg)->getFlags();
@@ -637,44 +642,35 @@ ElfValue *ElfDynamic_Section::getValueFo
 }
 
 ElfSection *ElfDynamic_Section::getSectionForType(unsigned int tag)
 {
     ElfValue *value = getValueForType(tag);
     return value ? value->getSection() : NULL;
 }
 
-void ElfDynamic_Section::setValueForType(unsigned int tag, ElfValue *val)
+bool ElfDynamic_Section::setValueForType(unsigned int tag, ElfValue *val)
 {
     unsigned int i;
-    for (i = 0; (i < shdr.sh_size / shdr.sh_entsize) && (dyns[i].tag != DT_NULL); i++)
+    unsigned int shnum = shdr.sh_size / shdr.sh_entsize;
+    for (i = 0; (i < shnum) && (dyns[i].tag != DT_NULL); i++)
         if (dyns[i].tag == tag) {
             delete dyns[i].value;
             dyns[i].value = val;
-            return;
+            return true;
         }
-    // This should never happen, as the last entry is always tagged DT_NULL
-    assert(i < shdr.sh_size / shdr.sh_entsize);
     // If we get here, this means we didn't match for the given tag
-    dyns[i].tag = tag;
-    dyns[i++].value = val;
+    // Most of the time, there are a few DT_NULL entries, that we can
+    // use to add our value, but if we are on the last entry, we can't.
+    if (i >= shnum - 1)
+        return false;
 
-    // If we were on the last entry, we need to grow the section.
-    // Most of the time, though, there are a few DT_NULL entries.
-    if (i < shdr.sh_size / shdr.sh_entsize)
-        return;
-
-    Elf_DynValue value;
-    value.tag = DT_NULL;
-    value.value = NULL;
-    dyns.push_back(value);
-    // Resize the section accordingly
-    shdr.sh_size += shdr.sh_entsize;
-    if (getNext() != NULL)
-        getNext()->markDirty();
+    dyns[i].tag = tag;
+    dyns[i].value = val;
+    return true;
 }
 
 ElfDynamic_Section::ElfDynamic_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent)
 : ElfSection(s, file, parent)
 {
     int pos = file->tellg();
     dyns.resize(s.sh_size / s.sh_entsize);
     file->seekg(shdr.sh_offset);
--- a/build/unix/elfhack/elfhack.cpp
+++ b/build/unix/elfhack/elfhack.cpp
@@ -33,16 +33,18 @@ public:
     typedef wrapped<Elf64_Addr> Type64;
 
     template <class endian, typename R, typename T>
     static inline void swap(T &t, R &r) {
         r.value = endian::swap(t.value);
     }
 };
 
+typedef serializable<Elf_Addr_Traits> Elf_Addr;
+
 class Elf_RelHack_Traits {
 public:
     typedef Elf32_Rel Type32;
     typedef Elf32_Rel Type64;
 
     template <class endian, typename R, typename T>
     static inline void swap(T &t, R &r) {
         r.r_offset = endian::swap(t.r_offset);
@@ -76,35 +78,34 @@ public:
         shdr.sh_size = rels.size() * shdr.sh_entsize;
     }
 private:
     std::vector<Elf_RelHack> rels;
 };
 
 class ElfRelHackCode_Section: public ElfSection {
 public:
-    ElfRelHackCode_Section(Elf_Shdr &s, Elf &e)
-    : ElfSection(s, NULL, NULL), parent(e) {
+    ElfRelHackCode_Section(Elf_Shdr &s, Elf &e, unsigned int init)
+    : ElfSection(s, NULL, NULL), parent(e), init(init) {
         std::string file(rundir);
-        init = parent.getDynSection()->getSectionForType(DT_INIT);
         file += "/inject/";
         switch (parent.getMachine()) {
         case EM_386:
             file += "x86";
             break;
         case EM_X86_64:
             file += "x86_64";
             break;
         case EM_ARM:
             file += "arm";
             break;
         default:
             throw std::runtime_error("unsupported architecture");
         }
-        if (init == NULL)
+        if (!init)
             file += "-noinit";
         file += ".o";
         std::ifstream inject(file.c_str(), std::ios::in|std::ios::binary);
         elf = new Elf(inject);
         if (elf->getType() != ET_REL)
             throw std::runtime_error("object for injected code is not ET_REL");
         if (elf->getMachine() != parent.getMachine())
             throw std::runtime_error("architecture of object for injected code doesn't match");
@@ -219,42 +220,42 @@ private:
         }
     };
 
     class arm_thm_jump24_relocation {
     public:
         Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
                               Elf32_Word addend, unsigned int addr)
         {
-            /* Follows description of b.w instructions as per
+            /* Follows description of b.w and bl instructions as per
                ARM Architecture Reference Manual ARMĀ® v7-A and ARMĀ® v7-R edition, A8.6.16
-               We limit ourselves to Encoding T3.
+               We limit ourselves to Encoding T4 of b.w and Encoding T1 of bl.
                We don't care about sign_extend because the only case where this is
                going to be used only jumps forward. */
             Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr);
             unsigned int word0 = addend & 0xffff,
                          word1 = addend >> 16;
 
-            if (((word0 & 0xf800) != 0xf000) || ((word1 & 0xd000) != 0x9000))
-                throw std::runtime_error("R_ARM_THM_JUMP24 relocation only supported for B.W <label>");
+            if (((word0 & 0xf800) != 0xf000) || ((word1 & 0x9000) != 0x9000))
+                throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for B.W <label> and BL <label>");
 
             unsigned int s = (word0 & (1 << 10)) >> 10;
             unsigned int j1 = (word1 & (1 << 13)) >> 13;
             unsigned int j2 = (word1 & (1 << 11)) >> 11;
             unsigned int i1 = j1 ^ s ? 0 : 1;
             unsigned int i2 = j2 ^ s ? 0 : 1;
 
             tmp += ((s << 24) | (i1 << 23) | (i2 << 22) | ((word0 & 0x3ff) << 12) | ((word1 & 0x7ff) << 1));
 
             s = (tmp & (1 << 24)) >> 24;
             j1 = ((tmp & (1 << 23)) >> 23) ^ !s;
             j2 = ((tmp & (1 << 22)) >> 22) ^ !s;
 
-            return 0xf000 | (s << 10) | ((tmp & (0x3ff << 12)) >> 12) | 
-                   (0x9000 << 16) | (j1 << 29) | (j2 << 27) | ((tmp & 0xffe) << 15);
+            return 0xf000 | (s << 10) | ((tmp & (0x3ff << 12)) >> 12) |
+                   ((word1 & 0xd000) << 16) | (j1 << 29) | (j2 << 27) | ((tmp & 0xffe) << 15);
         }
     };
 
     class gotoff_relocation {
     public:
         Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
                               Elf32_Word addend, unsigned int addr)
         {
@@ -294,17 +295,17 @@ private:
             if (symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection() == NULL) {
                 if (strcmp(name, "relhack") == 0) {
                     addr = getNext()->getAddr();
                 } else if (strcmp(name, "elf_header") == 0) {
                     // TODO: change this ungly hack to something better
                     ElfSection *ehdr = parent.getSection(1)->getPrevious()->getPrevious();
                     addr = ehdr->getAddr();
                 } else if (strcmp(name, "original_init") == 0) {
-                    addr = init->getAddr();
+                    addr = init;
                 } else if (strcmp(name, "_GLOBAL_OFFSET_TABLE_") == 0) {
                     // We actually don't need a GOT, but need it as a reference for
                     // GOTOFF relocations. We'll just use the start of the ELF file
                     addr = 0;
                 } else if (strcmp(name, "") == 0) {
                     // This is for R_ARM_V4BX, until we find something better
                     addr = -1;
                 } else {
@@ -323,16 +324,17 @@ private:
             case REL(386, GOTPC):
             case REL(ARM, GOTPC):
             case REL(ARM, REL32):
                 apply_relocation<pc32_relocation>(the_code, buf, &*r, addr);
                 break;
             case REL(ARM, PLT32):
                 apply_relocation<arm_plt32_relocation>(the_code, buf, &*r, addr);
                 break;
+            case REL(ARM, THM_PC22 /* THM_CALL */):
             case REL(ARM, THM_JUMP24):
                 apply_relocation<arm_thm_jump24_relocation>(the_code, buf, &*r, addr);
                 break;
             case REL(386, GOTOFF):
             case REL(ARM, GOTOFF):
                 apply_relocation<gotoff_relocation>(the_code, buf, &*r, addr);
                 break;
             case REL(ARM, V4BX):
@@ -341,22 +343,46 @@ private:
             default:
                 throw std::runtime_error("Unsupported relocation type");
             }
         }
     }
 
     Elf *elf, &parent;
     std::vector<ElfSection *> code;
-    ElfSection *init;
+    unsigned int init;
     int entry_point;
 };
 
+unsigned int get_addend(Elf_Rel *rel, Elf *elf) {
+    ElfLocation loc(rel->r_offset, elf);
+    Elf_Addr addr(loc.getBuffer(), Elf_Addr::size(elf->getClass()), elf->getClass(), elf->getData());
+    return addr.value;
+}
+
+unsigned int get_addend(Elf_Rela *rel, Elf *elf) {
+    return rel->r_addend;
+}
+
+void set_relative_reloc(Elf_Rel *rel, Elf *elf, unsigned int value) {
+    ElfLocation loc(rel->r_offset, elf);
+    Elf_Addr addr;
+    addr.value = value;
+    addr.serialize(const_cast<char *>(loc.getBuffer()), Elf_Addr::size(elf->getClass()), elf->getClass(), elf->getData());
+}
+
+void set_relative_reloc(Elf_Rela *rel, Elf *elf, unsigned int value) {
+    // ld puts the value of relocated relocations both in the addend and
+    // at r_offset. For consistency, keep it that way.
+    set_relative_reloc((Elf_Rel *)rel, elf, value);
+    rel->r_addend = value;
+}
+
 template <typename Rel_Type>
-int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type2)
+int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type2, bool force)
 {
     ElfDynamic_Section *dyn = elf->getDynSection();
     if (dyn ==NULL) {
         fprintf(stderr, "Couldn't find SHT_DYNAMIC section\n");
         return -1;
     }
 
     ElfSegment *relro = elf->getSegmentByType(PT_GNU_RELRO);
@@ -365,71 +391,104 @@ int do_relocation_section(Elf *elf, unsi
     assert(section->getType() == Rel_Type::sh_type);
 
     Elf32_Shdr relhack32_section =
         { 0, SHT_PROGBITS, SHF_ALLOC, 0, (Elf32_Off)-1, 0, SHN_UNDEF, 0,
           Elf_RelHack::size(elf->getClass()), Elf_RelHack::size(elf->getClass()) }; // TODO: sh_addralign should be an alignment, not size
     Elf32_Shdr relhackcode32_section =
         { 0, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, 0, (Elf32_Off)-1, 0,
           SHN_UNDEF, 0, 1, 0 };
+
+    unsigned int entry_sz = Elf_Addr::size(elf->getClass());
+
+    // The injected code needs to be executed before any init code in the
+    // binary. There are three possible cases:
+    // - The binary has no init code at all. In this case, we will add a
+    //   DT_INIT entry pointing to the injected code.
+    // - The binary has a DT_INIT entry. In this case, we will interpose:
+    //   we change DT_INIT to point to the injected code, and have the
+    //   injected code call the original DT_INIT entry point.
+    // - The binary has no DT_INIT entry, but has a DT_INIT_ARRAY. In this
+    //   case, we interpose as well, by replacing the first entry in the
+    //   array to point to the injected code, and have the injected code
+    //   call the original first entry.
+    // The binary may have .ctors instead of DT_INIT_ARRAY, for its init
+    // functions, but this falls into the second case above, since .ctors
+    // are actually run by DT_INIT code.
+    ElfValue *value = dyn->getValueForType(DT_INIT);
+    unsigned int original_init = value ? value->getValue() : 0;
+    ElfSection *init_array = NULL;
+    if (!value || !value->getValue()) {
+        value = dyn->getValueForType(DT_INIT_ARRAYSZ);
+        if (value && value->getValue() >= entry_sz)
+            init_array = dyn->getSectionForType(DT_INIT_ARRAY);
+    }
+
     Elf_Shdr relhack_section(relhack32_section);
     Elf_Shdr relhackcode_section(relhackcode32_section);
     ElfRelHack_Section *relhack = new ElfRelHack_Section(relhack_section);
-    ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf);
 
     ElfSymtab_Section *symtab = (ElfSymtab_Section *) section->getLink();
     Elf_SymValue *sym = symtab->lookup("__cxa_pure_virtual");
 
     std::vector<Rel_Type> new_rels;
     Elf_RelHack relhack_entry;
     relhack_entry.r_offset = relhack_entry.r_info = 0;
-    int entry_sz = (elf->getClass() == ELFCLASS32) ? 4 : 8;
+    size_t init_array_reloc = 0;
     for (typename std::vector<Rel_Type>::iterator i = section->rels.begin();
          i != section->rels.end(); i++) {
         // We don't need to keep R_*_NONE relocations
         if (!ELF32_R_TYPE(i->r_info))
             continue;
-        ElfSection *section = elf->getSectionAt(i->r_offset);
+        ElfLocation loc(i->r_offset, elf);
         // __cxa_pure_virtual is a function used in vtables to point at pure
         // virtual methods. The __cxa_pure_virtual function usually abort()s.
         // These functions are however normally never called. In the case
         // where they would, jumping to the NULL address instead of calling
         // __cxa_pure_virtual is going to work just as well. So we can remove
         // relocations for the __cxa_pure_virtual symbol and NULL out the
         // content at the offset pointed by the relocation.
         if (sym) {
             if (sym->defined) {
                 // If we are statically linked to libstdc++, the
                 // __cxa_pure_virtual symbol is defined in our lib, and we
                 // have relative relocations (rel_type) for it.
                 if (ELF32_R_TYPE(i->r_info) == rel_type) {
-                    serializable<Elf_Addr_Traits> addr(&section->getData()[i->r_offset - section->getAddr()], entry_sz, elf->getClass(), elf->getData());
+                    Elf_Addr addr(loc.getBuffer(), entry_sz, elf->getClass(), elf->getData());
                     if (addr.value == sym->value.getValue()) {
-                        memset((char *)&section->getData()[i->r_offset - section->getAddr()], 0, entry_sz);
+                        memset((char *)loc.getBuffer(), 0, entry_sz);
                         continue;
                     }
                 }
             } else {
                 // If we are dynamically linked to libstdc++, the
                 // __cxa_pure_virtual symbol is undefined in our lib, and we
                 // have absolute relocations (rel_type2) for it.
                 if ((ELF32_R_TYPE(i->r_info) == rel_type2) &&
                     (sym == &symtab->syms[ELF32_R_SYM(i->r_info)])) {
-                    memset((char *)&section->getData()[i->r_offset - section->getAddr()], 0, entry_sz);
+                    memset((char *)loc.getBuffer(), 0, entry_sz);
                     continue;
                 }
             }
         }
-        // Don't pack relocations happening in non writable sections.
-        // Our injected code is likely not to be allowed to write there.
-        if (!(section->getFlags() & SHF_WRITE) || (ELF32_R_TYPE(i->r_info) != rel_type) ||
-            (relro && (i->r_offset >= relro->getAddr()) &&
-                      (i->r_offset < relro->getAddr() + relro->getMemSize())))
+        // Keep track of the relocation associated with the first init_array entry.
+        if (init_array && i->r_offset == init_array->getAddr()) {
+            if (init_array_reloc) {
+                fprintf(stderr, "Found multiple relocations for the first init_array entry. Skipping\n");
+                return -1;
+            }
             new_rels.push_back(*i);
-        else {
+            init_array_reloc = new_rels.size();
+        } else if (!(loc.getSection()->getFlags() & SHF_WRITE) || (ELF32_R_TYPE(i->r_info) != rel_type) ||
+                   (relro && (i->r_offset >= relro->getAddr()) &&
+                   (i->r_offset < relro->getAddr() + relro->getMemSize()))) {
+            // Don't pack relocations happening in non writable sections.
+            // Our injected code is likely not to be allowed to write there.
+            new_rels.push_back(*i);
+        } else {
             // TODO: check that i->r_addend == *i->r_offset
             if (i->r_offset == relhack_entry.r_offset + relhack_entry.r_info * entry_sz) {
                 relhack_entry.r_info++;
             } else {
                 if (relhack_entry.r_offset)
                     relhack->push_back(relhack_entry);
                 relhack_entry.r_offset = i->r_offset;
                 relhack_entry.r_info = 1;
@@ -439,31 +498,64 @@ int do_relocation_section(Elf *elf, unsi
     if (relhack_entry.r_offset)
         relhack->push_back(relhack_entry);
     // Last entry must be NULL
     relhack_entry.r_offset = relhack_entry.r_info = 0;
     relhack->push_back(relhack_entry);
 
     unsigned int old_end = section->getOffset() + section->getSize();
 
+    if (init_array) {
+        if (! init_array_reloc) {
+            fprintf(stderr, "Didn't find relocation for DT_INIT_ARRAY's first entry. Skipping\n");
+            return -1;
+        }
+        Rel_Type *rel = &new_rels[init_array_reloc - 1];
+        unsigned int addend = get_addend(rel, elf);
+        // Use relocated value of DT_INIT_ARRAY's first entry for the
+        // function to be called by the injected code.
+        if (ELF32_R_TYPE(rel->r_info) == rel_type) {
+            original_init = addend;
+        } else if (ELF32_R_TYPE(rel->r_info) == rel_type2) {
+            ElfSymtab_Section *symtab = (ElfSymtab_Section *)section->getLink();
+            original_init = symtab->syms[ELF32_R_SYM(rel->r_info)].value.getValue() + addend;
+        } else {
+            fprintf(stderr, "Unsupported relocation type for DT_INIT_ARRAY's first entry. Skipping\n");
+            return -1;
+        }
+    }
+
+    ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf, original_init);
     relhackcode->insertBefore(section);
     relhack->insertAfter(relhackcode);
 
     section->rels.assign(new_rels.begin(), new_rels.end());
     section->shrink(new_rels.size() * section->getEntSize());
+    if (section->getOffset() + section->getSize() >= old_end) {
+        fprintf(stderr, "No gain. Skipping\n");
+        return -1;
+    }
+    // Ensure Elf sections will be at their final location.
+    elf->normalize();
     ElfLocation *init = new ElfLocation(relhackcode, relhackcode->getEntryPoint());
-    dyn->setValueForType(DT_INIT, init);
+    if (init_array) {
+        // Adjust the first DT_INIT_ARRAY entry to point at the injected code
+        // by transforming its relocation into a relative one pointing to the
+        // address of the injected code.
+        Rel_Type *rel = &section->rels[init_array_reloc - 1];
+        rel->r_info = ELF32_R_INFO(0, rel_type); // Set as a relative relocation
+        set_relative_reloc(&section->rels[init_array_reloc - 1], elf, init->getValue());
+    } else if (!dyn->setValueForType(DT_INIT, init)) {
+        fprintf(stderr, "Can't grow .dynamic section to set DT_INIT. Skipping\n");
+        return -1;
+    }
     // TODO: adjust the value according to the remaining number of relative relocations
     if (dyn->getValueForType(Rel_Type::d_tag_count))
         dyn->setValueForType(Rel_Type::d_tag_count, new ElfPlainValue(0));
 
-    if (section->getOffset() + section->getSize() >= old_end) {
-        fprintf(stderr, "No gain. Skipping\n");
-        return -1;
-    }
     return 0;
 }
 
 static inline int backup_file(const char *name)
 {
     std::string fname(name);
     fname += ".bak";
     return rename(name, fname.c_str());
@@ -489,23 +581,23 @@ void do_file(const char *name, bool back
             delete elf;
             return;
         }
     }
 
     int exit = -1;
     switch (elf->getMachine()) {
     case EM_386:
-        exit = do_relocation_section<Elf_Rel>(elf, R_386_RELATIVE, R_386_32);
+        exit = do_relocation_section<Elf_Rel>(elf, R_386_RELATIVE, R_386_32, force);
         break;
     case EM_X86_64:
-        exit = do_relocation_section<Elf_Rela>(elf, R_X86_64_RELATIVE, R_X86_64_64);
+        exit = do_relocation_section<Elf_Rela>(elf, R_X86_64_RELATIVE, R_X86_64_64, force);
         break;
     case EM_ARM:
-        exit = do_relocation_section<Elf_Rel>(elf, R_ARM_RELATIVE, R_ARM_ABS32);
+        exit = do_relocation_section<Elf_Rel>(elf, R_ARM_RELATIVE, R_ARM_ABS32, force);
         break;
     }
     if (exit == 0) {
         if (!force && (elf->getSize() >= size)) {
             fprintf(stderr, "No gain. Skipping\n");
         } else if (backup && backup_file(name) != 0) {
             fprintf(stderr, "Couln't create backup file\n");
         } else {
--- a/build/unix/elfhack/elfxx.h
+++ b/build/unix/elfhack/elfxx.h
@@ -130,16 +130,17 @@ class ElfLocation: public ElfValue {
     unsigned int offset;
 public:
     enum position { ABSOLUTE, RELATIVE };
     ElfLocation(): section(NULL), offset(0) {};
     ElfLocation(ElfSection *section, unsigned int off, enum position pos = RELATIVE);
     ElfLocation(unsigned int location, Elf *elf);
     unsigned int getValue();
     ElfSection *getSection() { return section; }
+    const char *getBuffer();
 };
 
 class ElfSize: public ElfValue {
     ElfSection *section;
 public:
     ElfSize(ElfSection *s): section(s) {};
     unsigned int getValue();
     ElfSection *getSection() { return section; }
@@ -159,28 +160,42 @@ public:
     serializable() {};
     serializable(const typename T::Type32 &p): T::Type32(p) {};
 
 private:
     template <typename R>
     void init(const char *buf, size_t len, char ei_data)
     {
         R e;
-        assert(len <= sizeof(e));
+        assert(len >= sizeof(e));
         memcpy(&e, buf, sizeof(e));
         if (ei_data == ELFDATA2LSB) {
             T::template swap<little_endian>(e, *this);
             return;
         } else if (ei_data == ELFDATA2MSB) {
             T::template swap<big_endian>(e, *this);
             return;
         }
         throw std::runtime_error("Unsupported ELF data encoding");
     }
 
+    template <typename R>
+    void serialize(const char *buf, size_t len, char ei_data)
+    {
+        assert(len >= sizeof(R));
+        if (ei_data == ELFDATA2LSB) {
+            T::template swap<little_endian>(*this, *(R *)buf);
+            return;
+        } else if (ei_data == ELFDATA2MSB) {
+            T::template swap<big_endian>(*this, *(R *)buf);
+            return;
+        }
+        throw std::runtime_error("Unsupported ELF data encoding");
+    }
+
 public:
     serializable(const char *buf, size_t len, char ei_class, char ei_data)
     {
         if (ei_class == ELFCLASS32) {
             init<typename T::Type32>(buf, len, ei_data);
             return;
         } else if (ei_class == ELFCLASS64) {
             init<typename T::Type64>(buf, len, ei_data);
@@ -189,65 +204,55 @@ public:
         throw std::runtime_error("Unsupported ELF class");
     }
 
     serializable(std::ifstream &file, char ei_class, char ei_data)
     {
         if (ei_class == ELFCLASS32) {
             typename T::Type32 e;
             file.read((char *)&e, sizeof(e));
-            if (ei_data == ELFDATA2LSB) {
-                T::template swap<little_endian>(e, *this);
-                return;
-            } else if (ei_data == ELFDATA2MSB) {
-                T::template swap<big_endian>(e, *this);
-                return;
-            }
+            init<typename T::Type32>((char *)&e, sizeof(e), ei_data);
+            return;
         } else if (ei_class == ELFCLASS64) {
             typename T::Type64 e;
             file.read((char *)&e, sizeof(e));
-            if (ei_data == ELFDATA2LSB) {
-                T::template swap<little_endian>(e, *this);
-                return;
-            } else if (ei_data == ELFDATA2MSB) {
-                T::template swap<big_endian>(e, *this);
-                return;
-            }
+            init<typename T::Type64>((char *)&e, sizeof(e), ei_data);
+            return;
         }
         throw std::runtime_error("Unsupported ELF class or data encoding");
     }
 
     void serialize(std::ofstream &file, char ei_class, char ei_data)
     {
         if (ei_class == ELFCLASS32) {
             typename T::Type32 e;
-            if (ei_data == ELFDATA2LSB) {
-                T::template swap<little_endian>(*this, e);
-                file.write((char *)&e, sizeof(e));
-                return;
-            } else if (ei_data == ELFDATA2MSB) {
-                T::template swap<big_endian>(*this, e);
-                file.write((char *)&e, sizeof(e));
-                return;
-            }
+            serialize<typename T::Type32>((char *)&e, sizeof(e), ei_data);
+            file.write((char *)&e, sizeof(e));
+            return;
         } else if (ei_class == ELFCLASS64) {
             typename T::Type64 e;
-            if (ei_data == ELFDATA2LSB) {
-                T::template swap<little_endian>(*this, e);
-                file.write((char *)&e, sizeof(e));
-                return;
-            } else if (ei_data == ELFDATA2MSB) {
-                T::template swap<big_endian>(*this, e);
-                file.write((char *)&e, sizeof(e));
-                return;
-            }
+            serialize<typename T::Type64>((char *)&e, sizeof(e), ei_data);
+            file.write((char *)&e, sizeof(e));
+            return;
         }
         throw std::runtime_error("Unsupported ELF class or data encoding");
     }
 
+    void serialize(char *buf, size_t len, char ei_class, char ei_data)
+    {
+        if (ei_class == ELFCLASS32) {
+            serialize<typename T::Type32>(buf, len, ei_data);
+            return;
+        } else if (ei_class == ELFCLASS64) {
+            serialize<typename T::Type64>(buf, len, ei_data);
+            return;
+        }
+        throw std::runtime_error("Unsupported ELF class");
+    }
+
     static inline unsigned int size(char ei_class)
     {
         if (ei_class == ELFCLASS32)
             return sizeof(typename T::Type32);
         else if (ei_class == ELFCLASS64)
             return sizeof(typename T::Type64);
         return 0;
     }
@@ -264,16 +269,17 @@ public:
     ElfSection *getSection(int index);
 
     ElfSection *getSectionAt(unsigned int offset);
 
     ElfSegment *getSegmentByType(unsigned int type);
 
     ElfDynamic_Section *getDynSection();
 
+    void normalize();
     void write(std::ofstream &file);
 
     char getClass();
     char getData();
     char getType();
     char getMachine();
     unsigned int getSize();
 private:
@@ -500,17 +506,17 @@ class ElfDynamic_Section: public ElfSect
 public:
     ElfDynamic_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent);
     ~ElfDynamic_Section();
 
     void serialize(std::ofstream &file, char ei_class, char ei_data);
 
     ElfValue *getValueForType(unsigned int tag);
     ElfSection *getSectionForType(unsigned int tag);
-    void setValueForType(unsigned int tag, ElfValue *val);
+    bool setValueForType(unsigned int tag, ElfValue *val);
 private:
     std::vector<Elf_DynValue> dyns;
 };
 
 typedef serializable<Elf_Sym_Traits> Elf_Sym;
 
 struct Elf_SymValue {
     const char *name;
@@ -656,15 +662,19 @@ inline ElfLocation::ElfLocation(unsigned
     section = elf->getSectionAt(location);
     offset = location - (section ? section->getAddr() : 0);
 }
 
 inline unsigned int ElfLocation::getValue() {
     return (section ? section->getAddr() : 0) + offset;
 }
 
+inline const char *ElfLocation::getBuffer() {
+    return section ? section->getData() + offset : NULL;
+}
+
 inline unsigned int ElfSize::getValue() {
     return section->getSize();
 }
 
 inline unsigned int ElfEntSize::getValue() {
     return section->getEntSize();
 }
new file mode 100644
--- /dev/null
+++ b/build/unix/elfhack/test-array.c
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "test.c"
+
+__attribute__((section(".init_array"), used))
+static void (*init_array[])() = { end_test, test };
new file mode 100644
--- /dev/null
+++ b/build/unix/elfhack/test-ctors.c
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "test.c"
+
+/* Recent binutils would put .ctors content into a .init_array section */
+__attribute__((section(".manual_ctors"), used))
+static void (*ctors[])() = { (void (*)())-1, end_test, test, NULL };
+
+__attribute__((section(".init")))
+void _init() {
+    void (**func)() = &ctors[sizeof(ctors) / sizeof(void (*)()) - 1];
+    while (*(--func) != (void (*)())-1) {
+        (*func)();
+    }
+}
--- a/build/unix/elfhack/test.c
+++ b/build/unix/elfhack/test.c
@@ -120,24 +120,24 @@ int print_status() {
 }
 
 /* On ARM, this creates a .tbss section before .init_array, which
  * elfhack could then pick instead of .init_array.
  * Also, when .tbss is big enough, elfhack may wrongfully consider
  * following sections as part of the PT_TLS segment. */
 __thread int foo[1024];
 
-__attribute__((constructor)) void end_test() {
+void end_test() {
     static int count = 0;
     /* Only exit when both constructors have been called */
     if (++count == 2)
         ret = 0;
 }
 
-__attribute__((constructor)) void test() {
+void test() {
     int i = 0, j = 0;
 #define DEF_(a,i,w) \
     if (a[i++] != str_ ## w) return;
 #define DEF(w) DEF_(strings,i,w)
 #include "test.c"
 #include "test.c"
 #include "test.c"
 #undef DEF
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -86,31 +86,30 @@ public:
   {
     MOZ_COUNT_CTOR(ControlMessage);
   }
   // All these run on the graph thread
   virtual ~ControlMessage()
   {
     MOZ_COUNT_DTOR(ControlMessage);
   }
-  // Executed before we know what the action time for this message will be.
-  // Call NoteStreamAffected on the stream whose output will be
-  // modified by this message. Default implementation calls
-  // NoteStreamAffected(mStream).
-  virtual void UpdateAffectedStream();
-  // Executed after we know what the action time for this message will be.
-  virtual void Process() {}
+  // Do the action of this message on the MediaStreamGraph thread. Any actions
+  // affecting graph processing should take effect at mStateComputedTime.
+  // All stream data for times < mStateComputedTime has already been
+  // computed.
+  virtual void Run() = 0;
   // When we're shutting down the application, most messages are ignored but
   // some cleanup messages should still be processed (on the main thread).
-  virtual void ProcessDuringShutdown() {}
+  virtual void RunDuringShutdown() {}
+  MediaStream* GetStream() { return mStream; }
 
 protected:
-  // We do not hold a reference to mStream. The main thread will be holding
-  // a reference to the stream while this message is in flight. The last message
-  // referencing a stream is the Destroy message for that stream.
+  // We do not hold a reference to mStream. The graph will be holding
+  // a reference to the stream until the Destroy message is processed. The
+  // last message referencing a stream is the Destroy message for that stream.
   MediaStream* mStream;
 };
 
 }
 
 /**
  * The implementation of a media stream graph. This class is private to this
  * file. It's not in the anonymous namespace because MediaStream needs to
@@ -197,39 +196,34 @@ public:
   void PrepareUpdatesToMainThreadState();
   // The following methods are the various stages of RunThread processing.
   /**
    * Compute a new current time for the graph and advance all on-graph-thread
    * state to the new current time.
    */
   void UpdateCurrentTime();
   /**
-   * Update mLastActionTime to the time at which the current set of messages
-   * will take effect.
-   */
-  void ChooseActionTime();
-  /**
    * Update the consumption state of aStream to reflect whether its data
    * is needed or not.
    */
   void UpdateConsumptionState(SourceMediaStream* aStream);
   /**
    * Extract any state updates pending in aStream, and apply them.
    */
   void ExtractPendingInput(SourceMediaStream* aStream,
                            GraphTime aDesiredUpToTime,
                            bool* aEnsureNextIteration);
   /**
    * Update "have enough data" flags in aStream.
    */
   void UpdateBufferSufficiencyState(SourceMediaStream* aStream);
   /**
-   * Compute the blocking states of streams from mBlockingDecisionsMadeUntilTime
+   * Compute the blocking states of streams from mStateComputedTime
    * until the desired future time aEndBlockingDecisions.
-   * Updates mBlockingDecisionsMadeUntilTime and sets MediaStream::mBlocked
+   * Updates mStateComputedTime and sets MediaStream::mBlocked
    * for all streams.
    */
   void RecomputeBlocking(GraphTime aEndBlockingDecisions);
   // The following methods are used to help RecomputeBlocking.
   /**
    * Mark a stream blocked at time aTime. If this results in decisions that need
    * to be revisited at some point in the future, *aEnd will be reduced to the
    * first time in the future to recompute those decisions.
@@ -262,17 +256,17 @@ public:
    */
   StreamTime GraphTimeToStreamTime(MediaStream* aStream, GraphTime aTime);
   enum {
     INCLUDE_TRAILING_BLOCKED_INTERVAL = 0x01
   };
   /**
    * Given a stream time aTime, convert it to a graph time taking into
    * account the time during which aStream is scheduled to be blocked.
-   * aTime must be <= mBlockingDecisionsMadeUntilTime since blocking decisions
+   * aTime must be <= mStateComputedTime since blocking decisions
    * are only known up to that point.
    * If aTime is exactly at the start of a blocked interval, then the blocked
    * interval is included in the time returned if and only if
    * aFlags includes INCLUDE_TRAILING_BLOCKED_INTERVAL.
    */
   GraphTime StreamTimeToGraphTime(MediaStream* aStream, StreamTime aTime,
                                   PRUint32 aFlags = 0);
   /**
@@ -314,78 +308,49 @@ public:
   bool IsEmpty() { return mStreams.IsEmpty(); }
 
   // For use by control messages
   /**
    * Identify which graph update index we are currently processing.
    */
   PRInt64 GetProcessingGraphUpdateIndex() { return mProcessingGraphUpdateIndex; }
   /**
-   * Marks aStream as affected by a change in its output at desired time aTime
-   * (in the timeline of aStream). The change may not actually happen at this time,
-   * it may be delayed until later if there is buffered data we can't change.
-   */
-  void NoteStreamAffected(MediaStream* aStream, double aTime);
-  /**
-   * Marks aStream as affected by a change in its output at the earliest
-   * possible time.
-   */
-  void NoteStreamAffected(MediaStream* aStream);
-  /**
    * Add aStream to the graph and initializes its graph-specific state.
    */
   void AddStream(MediaStream* aStream);
   /**
    * Remove aStream from the graph. Ensures that pending messages about the
    * stream back to the main thread are flushed.
    */
   void RemoveStream(MediaStream* aStream);
 
-  /**
-   * Compute the earliest time at which an action be allowed to occur on any
-   * stream. Actions cannot be earlier than the previous action time, and
-   * cannot affect already-committed blocking decisions (and associated
-   * buffered audio).
-   */
-  GraphTime GetEarliestActionTime()
-  {
-    return NS_MAX(mCurrentTime, NS_MAX(mLastActionTime, mBlockingDecisionsMadeUntilTime));
-  }
-
   // Data members
 
   /**
    * Media graph thread.
    * Readonly after initialization on the main thread.
    */
   nsCOMPtr<nsIThread> mThread;
 
   // The following state is managed on the graph thread only, unless
   // mLifecycleState > LIFECYCLE_RUNNING in which case the graph thread
   // is not running and this state can be used from the main thread.
 
   nsTArray<nsRefPtr<MediaStream> > mStreams;
   /**
-   * The time the last action was deemed to have occurred. This could be
-   * later than mCurrentTime if actions have to be delayed during data
-   * buffering, or before mCurrentTime if mCurrentTime has advanced since
-   * the last action happened. In ControlMessage::Process calls,
-   * mLastActionTime has always been updated to be >= mCurrentTime.
-   */
-  GraphTime mLastActionTime;
-  /**
    * The current graph time for the current iteration of the RunThread control
    * loop.
    */
   GraphTime mCurrentTime;
   /**
-   * Blocking decisions have been made up to this time. We also buffer audio
-   * up to this time.
+   * Blocking decisions and all stream contents have been computed up to this
+   * time. The next batch of updates from the main thread will be processed
+   * at this time. Always >= mCurrentTime.
    */
-  GraphTime mBlockingDecisionsMadeUntilTime;
+  GraphTime mStateComputedTime;
   /**
    * This is only used for logging.
    */
   TimeStamp mInitialTimeStamp;
   /**
    * The real timestamp of the latest run of UpdateCurrentTime.
    */
   TimeStamp mCurrentTimeStamp;
@@ -556,45 +521,19 @@ MediaStreamGraphImpl::FinishStream(Media
   aStream->mFinished = true;
   // Force at least one more iteration of the control loop, since we rely
   // on UpdateCurrentTime to notify our listeners once the stream end
   // has been reached.
   EnsureNextIteration();
 }
 
 void
-MediaStreamGraphImpl::NoteStreamAffected(MediaStream* aStream, double aTime)
-{
-  NS_ASSERTION(aTime >= 0, "Bad time");
-  GraphTime t =
-      NS_MAX(GetEarliestActionTime(),
-          StreamTimeToGraphTime(aStream, SecondsToMediaTime(aTime),
-                                INCLUDE_TRAILING_BLOCKED_INTERVAL));
-  aStream->mMessageAffectedTime = NS_MIN(aStream->mMessageAffectedTime, t);
-}
-
-void
-MediaStreamGraphImpl::NoteStreamAffected(MediaStream* aStream)
-{
-  GraphTime t = GetEarliestActionTime();
-  aStream->mMessageAffectedTime = NS_MIN(aStream->mMessageAffectedTime, t);
-}
-
-void
-ControlMessage::UpdateAffectedStream()
-{
-  NS_ASSERTION(mStream, "Must have stream for default UpdateAffectedStream");
-  mStream->GraphImpl()->NoteStreamAffected(mStream);
-}
-
-void
 MediaStreamGraphImpl::AddStream(MediaStream* aStream)
 {
   aStream->mBufferStartTime = mCurrentTime;
-  aStream->mMessageAffectedTime = GetEarliestActionTime();
   *mStreams.AppendElement() = already_AddRefed<MediaStream>(aStream);
   LOG(PR_LOG_DEBUG, ("Adding media stream %p to the graph", aStream));
 }
 
 void
 MediaStreamGraphImpl::RemoveStream(MediaStream* aStream)
 {
   // Remove references in mStreamUpdates before we allow aStream to die.
@@ -611,22 +550,16 @@ MediaStreamGraphImpl::RemoveStream(Media
 
   // This unrefs the stream, probably destroying it
   mStreams.RemoveElement(aStream);
 
   LOG(PR_LOG_DEBUG, ("Removing media stream %p from the graph", aStream));
 }
 
 void
-MediaStreamGraphImpl::ChooseActionTime()
-{
-  mLastActionTime = GetEarliestActionTime();
-}
-
-void
 MediaStreamGraphImpl::UpdateConsumptionState(SourceMediaStream* aStream)
 {
   bool isConsumed = !aStream->mAudioOutputs.IsEmpty() ||
     !aStream->mVideoOutputs.IsEmpty();
   MediaStreamListener::Consumption state = isConsumed ? MediaStreamListener::CONSUMED
     : MediaStreamListener::NOT_CONSUMED;
   if (state != aStream->mLastConsumptionState) {
     aStream->mLastConsumptionState = state;
@@ -648,18 +581,18 @@ MediaStreamGraphImpl::ExtractPendingInpu
     if (aStream->mPullEnabled) {
       for (PRUint32 j = 0; j < aStream->mListeners.Length(); ++j) {
         MediaStreamListener* l = aStream->mListeners[j];
         {
           // Compute how much stream time we'll need assuming we don't block
           // the stream at all between mBlockingDecisionsMadeUntilTime and
           // aDesiredUpToTime.
           StreamTime t =
-            GraphTimeToStreamTime(aStream, mBlockingDecisionsMadeUntilTime) +
-            (aDesiredUpToTime - mBlockingDecisionsMadeUntilTime);
+            GraphTimeToStreamTime(aStream, mStateComputedTime) +
+            (aDesiredUpToTime - mStateComputedTime);
           MutexAutoUnlock unlock(aStream->mMutex);
           l->NotifyPull(this, t);
           *aEnsureNextIteration = true;
         }
       }
     }
     finished = aStream->mUpdateFinished;
     for (PRInt32 i = aStream->mUpdateTracks.Length() - 1; i >= 0; --i) {
@@ -739,17 +672,17 @@ MediaStreamGraphImpl::UpdateBufferSuffic
   }
 }
 
 
 StreamTime
 MediaStreamGraphImpl::GraphTimeToStreamTime(MediaStream* aStream,
                                             GraphTime aTime)
 {
-  NS_ASSERTION(aTime <= mBlockingDecisionsMadeUntilTime,
+  NS_ASSERTION(aTime <= mStateComputedTime,
                "Don't ask about times where we haven't made blocking decisions yet");
   if (aTime <= mCurrentTime) {
     return NS_MAX<StreamTime>(0, aTime - aStream->mBufferStartTime);
   }
   GraphTime t = mCurrentTime;
   StreamTime s = t - aStream->mBufferStartTime;
   while (t < aTime) {
     GraphTime end;
@@ -776,19 +709,19 @@ MediaStreamGraphImpl::StreamTimeToGraphT
 
   MediaTime streamAmount = aTime - bufferElapsedToCurrentTime;
   NS_ASSERTION(streamAmount >= 0, "Can't answer queries before current time");
 
   GraphTime t = mCurrentTime;
   while (t < GRAPH_TIME_MAX) {
     bool blocked;
     GraphTime end;
-    if (t < mBlockingDecisionsMadeUntilTime) {
+    if (t < mStateComputedTime) {
       blocked = aStream->mBlocked.GetAt(t, &end);
-      end = NS_MIN(end, mBlockingDecisionsMadeUntilTime);
+      end = NS_MIN(end, mStateComputedTime);
     } else {
       blocked = false;
       end = GRAPH_TIME_MAX;
     }
     if (blocked) {
       t = end;
     } else {
       if (streamAmount == 0) {
@@ -820,34 +753,34 @@ MediaStreamGraphImpl::GetAudioPosition(M
 
 void
 MediaStreamGraphImpl::UpdateCurrentTime()
 {
   GraphTime prevCurrentTime = mCurrentTime;
   TimeStamp now = TimeStamp::Now();
   GraphTime nextCurrentTime =
     SecondsToMediaTime((now - mCurrentTimeStamp).ToSeconds()) + mCurrentTime;
-  if (mBlockingDecisionsMadeUntilTime < nextCurrentTime) {
+  if (mStateComputedTime < nextCurrentTime) {
     LOG(PR_LOG_WARNING, ("Media graph global underrun detected"));
-    LOG(PR_LOG_DEBUG, ("Advancing mBlockingDecisionsMadeUntilTime from %f to %f",
-                       MediaTimeToSeconds(mBlockingDecisionsMadeUntilTime),
+    LOG(PR_LOG_DEBUG, ("Advancing mStateComputedTime from %f to %f",
+                       MediaTimeToSeconds(mStateComputedTime),
                        MediaTimeToSeconds(nextCurrentTime)));
-    // Advance mBlockingDecisionsMadeUntilTime to nextCurrentTime by
-    // adding blocked time to all streams starting at mBlockingDecisionsMadeUntilTime
+    // Advance mStateComputedTime to nextCurrentTime by
+    // adding blocked time to all streams starting at mStateComputedTime
     for (PRUint32 i = 0; i < mStreams.Length(); ++i) {
-      mStreams[i]->mBlocked.SetAtAndAfter(mBlockingDecisionsMadeUntilTime, true);
+      mStreams[i]->mBlocked.SetAtAndAfter(mStateComputedTime, true);
     }
-    mBlockingDecisionsMadeUntilTime = nextCurrentTime;
+    mStateComputedTime = nextCurrentTime;
   }
   mCurrentTimeStamp = now;
 
-  LOG(PR_LOG_DEBUG, ("Updating current time to %f (real %f, mBlockingDecisionsMadeUntilTime %f)",
+  LOG(PR_LOG_DEBUG, ("Updating current time to %f (real %f, mStateComputedTime %f)",
                      MediaTimeToSeconds(nextCurrentTime),
                      (now - mInitialTimeStamp).ToSeconds(),
-                     MediaTimeToSeconds(mBlockingDecisionsMadeUntilTime)));
+                     MediaTimeToSeconds(mStateComputedTime)));
 
   if (prevCurrentTime >= nextCurrentTime) {
     NS_ASSERTION(prevCurrentTime == nextCurrentTime, "Time can't go backwards!");
     // This could happen due to low clock resolution, maybe?
     LOG(PR_LOG_DEBUG, ("Time did not advance"));
     // There's not much left to do here, but the code below that notifies
     // listeners that streams have ended still needs to run.
   }
@@ -950,31 +883,30 @@ MediaStreamGraphImpl::WillUnderrun(Media
   *aEnd = NS_MIN(*aEnd, bufferEnd);
   return false;
 }
 
 void
 MediaStreamGraphImpl::RecomputeBlocking(GraphTime aEndBlockingDecisions)
 {
   bool blockingDecisionsWillChange = false;
-  // mBlockingDecisionsMadeUntilTime has been set in UpdateCurrentTime
-  while (mBlockingDecisionsMadeUntilTime < aEndBlockingDecisions) {
+  while (mStateComputedTime < aEndBlockingDecisions) {
     LOG(PR_LOG_DEBUG, ("Media graph %p computing blocking for time %f",
-                       this, MediaTimeToSeconds(mBlockingDecisionsMadeUntilTime)));
+                       this, MediaTimeToSeconds(mStateComputedTime)));
     GraphTime end = GRAPH_TIME_MAX;
-    RecomputeBlockingAt(mBlockingDecisionsMadeUntilTime, aEndBlockingDecisions, &end);
+    RecomputeBlockingAt(mStateComputedTime, aEndBlockingDecisions, &end);
     LOG(PR_LOG_DEBUG, ("Media graph %p computed blocking for interval %f to %f",
-                       this, MediaTimeToSeconds(mBlockingDecisionsMadeUntilTime),
-                       MediaTimeToSeconds(end)));
-    mBlockingDecisionsMadeUntilTime = end;
+                       this, MediaTimeToSeconds(mStateComputedTime),
+                       MediaTimeToSeconds(end)));                       
+    mStateComputedTime = end;
     if (end < GRAPH_TIME_MAX) {
       blockingDecisionsWillChange = true;
     }
   }
-  mBlockingDecisionsMadeUntilTime = aEndBlockingDecisions;
+  mStateComputedTime = aEndBlockingDecisions;
 
   for (PRUint32 i = 0; i < mStreams.Length(); ++i) {
     MediaStream* stream = mStreams[i];
     GraphTime end;
     stream->mBlocked.GetAt(mCurrentTime, &end);
     if (end < GRAPH_TIME_MAX) {
       blockingDecisionsWillChange = true;
     }
@@ -1028,23 +960,16 @@ MediaStreamGraphImpl::RecomputeBlockingA
       continue;
     }
 
     bool underrun = WillUnderrun(stream, aTime, aEndBlockingDecisions, aEnd);
     if (underrun) {
       MarkStreamBlocked(stream, aTime, aEnd);
       continue;
     }
-
-    if (stream->mAudioOutputs.IsEmpty() && stream->mVideoOutputs.IsEmpty()) {
-      // See if the stream is being consumed anywhere. If not, it should block.
-      LOG(PR_LOG_DEBUG, ("MediaStream %p is blocked due to having no consumers", stream));
-      MarkStreamBlocked(stream, aTime, aEnd);
-      continue;
-    }
   }
 
   NS_ASSERTION(*aEnd > aTime, "Failed to advance!");
 }
 
 void
 MediaStreamGraphImpl::UpdateFirstActiveTracks(MediaStream* aStream)
 {
@@ -1082,17 +1007,17 @@ MediaStreamGraphImpl::CreateOrDestroyAud
 
   if (aStream->mAudioOutput)
     return;
 
   // No output stream created yet. Check if it's time to create one.
   GraphTime startTime =
     StreamTimeToGraphTime(aStream, track->GetStartTimeRoundDown(),
                           INCLUDE_TRAILING_BLOCKED_INTERVAL);
-  if (startTime >= mBlockingDecisionsMadeUntilTime) {
+  if (startTime >= mStateComputedTime) {
     // The stream wants to play audio, but nothing will play for the forseeable
     // future, so don't create the stream.
     return;
   }
 
   // Don't bother destroying the nsAudioStream for ended tracks yet.
 
   // XXX allocating a nsAudioStream could be slow so we're going to have to do
@@ -1281,27 +1206,18 @@ MediaStreamGraphImpl::RunThread()
 
     // Calculate independent action times for each batch of messages (each
     // batch corresponding to an event loop task). This isolates the performance
     // of different scripts to some extent.
     for (PRUint32 i = 0; i < messageQueue.Length(); ++i) {
       mProcessingGraphUpdateIndex = messageQueue[i].mGraphUpdateIndex;
       nsTArray<nsAutoPtr<ControlMessage> >& messages = messageQueue[i].mMessages;
 
-      for (PRUint32 j = 0; j < mStreams.Length(); ++j) {
-        mStreams[j]->mMessageAffectedTime = GRAPH_TIME_MAX;
-      }
       for (PRUint32 j = 0; j < messages.Length(); ++j) {
-        messages[j]->UpdateAffectedStream();
-      }
-
-      ChooseActionTime();
-
-      for (PRUint32 j = 0; j < messages.Length(); ++j) {
-        messages[j]->Process();
+        messages[j]->Run();
       }
     }
     messageQueue.Clear();
 
     PRInt32 writeAudioUpTo = AUDIO_TARGET_MS;
     GraphTime endBlockingDecisions =
       mCurrentTime + MillisecondsToMediaTime(writeAudioUpTo);
 
@@ -1310,28 +1226,27 @@ MediaStreamGraphImpl::RunThread()
     for (PRUint32 i = 0; i < mStreams.Length(); ++i) {
       SourceMediaStream* is = mStreams[i]->AsSourceStream();
       if (is) {
         UpdateConsumptionState(is);
         ExtractPendingInput(is, endBlockingDecisions, &ensureNextIteration);
       }
     }
 
-    GraphTime prevBlockingDecisionsMadeUntilTime = mBlockingDecisionsMadeUntilTime;
+    GraphTime prevComputedTime = mStateComputedTime;
     RecomputeBlocking(endBlockingDecisions);
 
     PRUint32 audioStreamsActive = 0;
     bool allBlockedForever = true;
     // Figure out what each stream wants to do
     for (PRUint32 i = 0; i < mStreams.Length(); ++i) {
       MediaStream* stream = mStreams[i];
       UpdateFirstActiveTracks(stream);
-      CreateOrDestroyAudioStream(prevBlockingDecisionsMadeUntilTime, stream);
-      PlayAudio(stream, prevBlockingDecisionsMadeUntilTime,
-                mBlockingDecisionsMadeUntilTime);
+      CreateOrDestroyAudioStream(prevComputedTime, stream);
+      PlayAudio(stream, prevComputedTime, mStateComputedTime);
       if (stream->mAudioOutput) {
         ++audioStreamsActive;
       }
       PlayVideo(stream);
       SourceMediaStream* is = stream->AsSourceStream();
       if (is) {
         UpdateBufferSufficiencyState(is);
       }
@@ -1342,36 +1257,26 @@ MediaStreamGraphImpl::RunThread()
     }
     if (ensureNextIteration || !allBlockedForever || audioStreamsActive > 0) {
       EnsureNextIteration();
     }
 
     {
       // Not using MonitorAutoLock since we need to unlock in a way
       // that doesn't match lexical scopes.
-      mMonitor.Lock();
+      MonitorAutoLock lock(mMonitor);
       PrepareUpdatesToMainThreadState();
       if (mForceShutDown || (IsEmpty() && mMessageQueue.IsEmpty())) {
         // Enter shutdown mode. The stable-state handler will detect this
         // and complete shutdown. Destroy any streams immediately.
         LOG(PR_LOG_DEBUG, ("MediaStreamGraph %p waiting for main thread cleanup", this));
         // Commit to shutting down this graph object.
         mLifecycleState = LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP;
-        // Move mStreams to a temporary array, because after we unlock
-        // mMonitor, 'this' may be deleted by the main thread.
-        nsTArray<nsRefPtr<MediaStream> > streams;
-        mStreams.SwapElements(streams);
-
-        mMonitor.Unlock();
-        // Unlock mMonitor while destroying our streams, since
-        // SourceMediaStream::DestroyImpl needs to take its lock while
-        // we're not holding mMonitor.
-        for (PRUint32 i = 0; i < streams.Length(); ++i) {
-          streams[i]->DestroyImpl();
-        }
+        // No need to Destroy streams here. The main-thread owner of each
+        // stream is responsible for calling Destroy them.
         return;
       }
 
       PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
       TimeStamp now = TimeStamp::Now();
       if (mNeedAnotherIteration) {
         PRInt64 timeoutMS = MEDIA_GRAPH_TARGET_PERIOD_MS -
           PRInt64((now - mCurrentTimeStamp).ToMilliseconds());
@@ -1389,18 +1294,16 @@ MediaStreamGraphImpl::RunThread()
         mMonitor.Wait(timeout);
         LOG(PR_LOG_DEBUG, ("Resuming after timeout; at %f, elapsed=%f",
                            (TimeStamp::Now() - mInitialTimeStamp).ToSeconds(),
                            (TimeStamp::Now() - now).ToSeconds()));
       }
       mWaitState = WAITSTATE_RUNNING;
       mNeedAnotherIteration = false;
       messageQueue.SwapElements(mMessageQueue);
-
-      mMonitor.Unlock();
     }
   }
 }
 
 void
 MediaStreamGraphImpl::ApplyStreamUpdate(StreamUpdate* aUpdate)
 {
   mMonitor.AssertCurrentThreadOwns();
@@ -1484,22 +1387,19 @@ public:
 };
 
 /*
  * Control messages forwarded from main thread to graph manager thread
  */
 class CreateMessage : public ControlMessage {
 public:
   CreateMessage(MediaStream* aStream) : ControlMessage(aStream) {}
-  virtual void UpdateAffectedStream()
+  virtual void Run()
   {
     mStream->GraphImpl()->AddStream(mStream);
-  }
-  virtual void Process()
-  {
     mStream->Init();
   }
 };
 
 class MediaStreamGraphShutdownObserver MOZ_FINAL : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
@@ -1509,42 +1409,42 @@ public:
 }
 
 void
 MediaStreamGraphImpl::RunInStableState()
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
 
   nsTArray<nsCOMPtr<nsIRunnable> > runnables;
+  // When we're doing a forced shutdown, pending control messages may be
+  // run on the main thread via RunDuringShutdown. Those messages must
+  // run without the graph monitor being held. So, we collect them here.
+  nsTArray<nsAutoPtr<ControlMessage> > controlMessagesToRunDuringShutdown;
 
   {
     MonitorAutoLock lock(mMonitor);
     mPostedRunInStableStateEvent = false;
 
     runnables.SwapElements(mUpdateRunnables);
     for (PRUint32 i = 0; i < mStreamUpdates.Length(); ++i) {
       StreamUpdate* update = &mStreamUpdates[i];
       if (update->mStream) {
         ApplyStreamUpdate(update);
       }
     }
     mStreamUpdates.Clear();
 
     if (mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP && mForceShutDown) {
+      // Defer calls to RunDuringShutdown() to happen while mMonitor is not held.
       for (PRUint32 i = 0; i < mMessageQueue.Length(); ++i) {
         MessageBlock& mb = mMessageQueue[i];
-        for (PRUint32 j = 0; j < mb.mMessages.Length(); ++j) {
-          mb.mMessages[j]->ProcessDuringShutdown();
-        }
+        controlMessagesToRunDuringShutdown.MoveElementsFrom(mb.mMessages);
       }
       mMessageQueue.Clear();
-      for (PRUint32 i = 0; i < mCurrentTaskMessageQueue.Length(); ++i) {
-        mCurrentTaskMessageQueue[i]->ProcessDuringShutdown();
-      }
-      mCurrentTaskMessageQueue.Clear();
+      controlMessagesToRunDuringShutdown.MoveElementsFrom(mCurrentTaskMessageQueue);
       // Stop MediaStreamGraph threads. Do not clear gGraph since
       // we have outstanding DOM objects that may need it.
       mLifecycleState = LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN;
       nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutDownRunnable(this);
       NS_DispatchToMainThread(event);
     }
 
     if (mLifecycleState == LIFECYCLE_THREAD_NOT_STARTED) {
@@ -1593,16 +1493,19 @@ MediaStreamGraphImpl::RunInStableState()
   }
 
   // Make sure we get a new current time in the next event loop task
   mPostedRunInStableState = false;
 
   for (PRUint32 i = 0; i < runnables.Length(); ++i) {
     runnables[i]->Run();
   }
+  for (PRUint32 i = 0; i < controlMessagesToRunDuringShutdown.Length(); ++i) {
+    controlMessagesToRunDuringShutdown[i]->RunDuringShutdown();
+  }
 }
 
 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 void
 MediaStreamGraphImpl::EnsureRunInStableState()
 {
   NS_ASSERTION(NS_IsMainThread(), "main thread only");
@@ -1630,25 +1533,28 @@ MediaStreamGraphImpl::EnsureStableStateE
   nsCOMPtr<nsIRunnable> event = new MediaStreamGraphStableStateRunnable();
   NS_DispatchToMainThread(event);
 }
 
 void
 MediaStreamGraphImpl::AppendMessage(ControlMessage* aMessage)
 {
   NS_ASSERTION(NS_IsMainThread(), "main thread only");
+  NS_ASSERTION(!aMessage->GetStream() ||
+               !aMessage->GetStream()->IsDestroyed(),
+               "Stream already destroyed");
 
   if (mDetectedNotRunning &&
       mLifecycleState > LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
     // The graph control loop is not running and main thread cleanup has
     // happened. From now on we can't append messages to mCurrentTaskMessageQueue,
-    // because that will never be processed again, so just ProcessDuringShutdown
+    // because that will never be processed again, so just RunDuringShutdown
     // this message.
     // This should only happen during forced shutdown.
-    aMessage->ProcessDuringShutdown();
+    aMessage->RunDuringShutdown();
     delete aMessage;
     if (IsEmpty()) {
       NS_ASSERTION(gGraph == this, "Switched managers during forced shutdown?");
       gGraph = nullptr;
       delete this;
     }
     return;
   }
@@ -1658,17 +1564,17 @@ MediaStreamGraphImpl::AppendMessage(Cont
 }
 
 void
 MediaStream::Init()
 {
   MediaStreamGraphImpl* graph = GraphImpl();
   mBlocked.SetAtAndAfter(graph->mCurrentTime, true);
   mExplicitBlockerCount.SetAtAndAfter(graph->mCurrentTime, true);
-  mExplicitBlockerCount.SetAtAndAfter(graph->mLastActionTime, false);
+  mExplicitBlockerCount.SetAtAndAfter(graph->mStateComputedTime, false);
 }
 
 MediaStreamGraphImpl*
 MediaStream::GraphImpl()
 {
   return gGraph;
 }
 
@@ -1682,35 +1588,36 @@ MediaStream::DestroyImpl()
 }
 
 void
 MediaStream::Destroy()
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream) : ControlMessage(aStream) {}
-    virtual void UpdateAffectedStream()
+    virtual void Run()
     {
       mStream->DestroyImpl();
       mStream->GraphImpl()->RemoveStream(mStream);
     }
-    virtual void ProcessDuringShutdown()
-    { UpdateAffectedStream(); }
+    virtual void RunDuringShutdown()
+    { Run(); }
   };
   mWrapper = nullptr;
   GraphImpl()->AppendMessage(new Message(this));
+  mMainThreadDestroyed = true;
 }
 
 void
 MediaStream::AddAudioOutput(void* aKey)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, void* aKey) : ControlMessage(aStream), mKey(aKey) {}
-    virtual void UpdateAffectedStream()
+    virtual void Run()
     {
       mStream->AddAudioOutputImpl(mKey);
     }
     void* mKey;
   };
   GraphImpl()->AppendMessage(new Message(this, aKey));
 }
 
@@ -1728,17 +1635,17 @@ MediaStream::SetAudioOutputVolumeImpl(vo
 
 void
 MediaStream::SetAudioOutputVolume(void* aKey, float aVolume)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, void* aKey, float aVolume) :
       ControlMessage(aStream), mKey(aKey), mVolume(aVolume) {}
-    virtual void UpdateAffectedStream()
+    virtual void Run()
     {
       mStream->SetAudioOutputVolumeImpl(mKey, mVolume);
     }
     void* mKey;
     float mVolume;
   };
   GraphImpl()->AppendMessage(new Message(this, aKey, aVolume));
 }
@@ -1757,68 +1664,68 @@ MediaStream::RemoveAudioOutputImpl(void*
 
 void
 MediaStream::RemoveAudioOutput(void* aKey)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, void* aKey) :
       ControlMessage(aStream), mKey(aKey) {}
-    virtual void UpdateAffectedStream()
+    virtual void Run()
     {
       mStream->RemoveAudioOutputImpl(mKey);
     }
     void* mKey;
   };
   GraphImpl()->AppendMessage(new Message(this, aKey));
 }
 
 void
 MediaStream::AddVideoOutput(VideoFrameContainer* aContainer)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, VideoFrameContainer* aContainer) :
       ControlMessage(aStream), mContainer(aContainer) {}
-    virtual void UpdateAffectedStream()
+    virtual void Run()
     {
       mStream->AddVideoOutputImpl(mContainer.forget());
     }
     nsRefPtr<VideoFrameContainer> mContainer;
   };
   GraphImpl()->AppendMessage(new Message(this, aContainer));
 }
 
 void
 MediaStream::RemoveVideoOutput(VideoFrameContainer* aContainer)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, VideoFrameContainer* aContainer) :
       ControlMessage(aStream), mContainer(aContainer) {}
-    virtual void UpdateAffectedStream()
+    virtual void Run()
     {
       mStream->RemoveVideoOutputImpl(mContainer);
     }
     nsRefPtr<VideoFrameContainer> mContainer;
   };
   GraphImpl()->AppendMessage(new Message(this, aContainer));
 }
 
 void
 MediaStream::ChangeExplicitBlockerCount(PRInt32 aDelta)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, PRInt32 aDelta) :
       ControlMessage(aStream), mDelta(aDelta) {}
-    virtual void UpdateAffectedStream()
+    virtual void Run()
     {
       mStream->ChangeExplicitBlockerCountImpl(
-          mStream->GraphImpl()->mLastActionTime, mDelta);
+          mStream->GraphImpl()->mStateComputedTime, mDelta);
     }
     PRInt32 mDelta;
   };
   GraphImpl()->AppendMessage(new Message(this, aDelta));
 }
 
 void
 MediaStream::AddListenerImpl(already_AddRefed<MediaStreamListener> aListener)
@@ -1833,33 +1740,33 @@ MediaStream::AddListenerImpl(already_Add
 
 void
 MediaStream::AddListener(MediaStreamListener* aListener)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, MediaStreamListener* aListener) :
       ControlMessage(aStream), mListener(aListener) {}
-    virtual void UpdateAffectedStream()
+    virtual void Run()
     {
       mStream->AddListenerImpl(mListener.forget());
     }
     nsRefPtr<MediaStreamListener> mListener;
   };
   GraphImpl()->AppendMessage(new Message(this, aListener));
 }
 
 void
 MediaStream::RemoveListener(MediaStreamListener* aListener)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, MediaStreamListener* aListener) :
       ControlMessage(aStream), mListener(aListener) {}
-    virtual void UpdateAffectedStream()
+    virtual void Run()
     {
       mStream->RemoveListenerImpl(mListener);
     }
     nsRefPtr<MediaStreamListener> mListener;
   };
   GraphImpl()->AppendMessage(new Message(this, aListener));
 }
 
@@ -1986,19 +1893,18 @@ static const PRUint32 kIdleThreadTimeout
 
 /**
  * We make the initial mCurrentTime nonzero so that zero times can have
  * special meaning if necessary.
  */
 static const PRInt32 INITIAL_CURRENT_TIME = 1;
 
 MediaStreamGraphImpl::MediaStreamGraphImpl()
-  : mLastActionTime(INITIAL_CURRENT_TIME)
-  , mCurrentTime(INITIAL_CURRENT_TIME)
-  , mBlockingDecisionsMadeUntilTime(1)
+  : mCurrentTime(INITIAL_CURRENT_TIME)
+  , mStateComputedTime(INITIAL_CURRENT_TIME)
   , mProcessingGraphUpdateIndex(0)
   , mMonitor("MediaStreamGraphImpl")
   , mLifecycleState(LIFECYCLE_THREAD_NOT_STARTED)
   , mWaitState(WAITSTATE_RUNNING)
   , mNeedAnotherIteration(false)
   , mForceShutDown(false)
   , mPostedRunInStableStateEvent(false)
   , mDetectedNotRunning(false)
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -197,34 +197,53 @@ class SourceMediaStream;
  * MediaStreamGraph::CreateInput/ProcessedMediaStream, and releases them by calling
  * Destroy() when no longer needed (actual destruction will be deferred).
  * The actual object is owned by the MediaStreamGraph. The basic idea is that
  * main thread objects will keep Streams alive as long as necessary (using the
  * cycle collector to clean up whenever needed).
  *
  * We make them refcounted only so that stream-related messages with MediaStream*
  * pointers can be sent to the main thread safely.
+ *
+ * The lifetimes of MediaStreams are controlled from the main thread.
+ * For MediaStreams exposed to the DOM, the lifetime is controlled by the DOM
+ * wrapper; the DOM wrappers own their associated MediaStreams. When a DOM
+ * wrapper is destroyed, it sends a Destroy message for the associated
+ * MediaStream and clears its reference (the last main-thread reference to
+ * the object). When the Destroy message is processed on the graph
+ * manager thread we immediately release the affected objects (disentangling them
+ * from other objects as necessary).
+ *
+ * This could cause problems for media processing if a MediaStream is
+ * destroyed while a downstream MediaStream is still using it. Therefore
+ * the DOM wrappers must keep upstream MediaStreams alive as long as they
+ * could be being used in the media graph.
+ *
+ * At any time, however, a set of MediaStream wrappers could be
+ * collected via cycle collection. Destroy messages will be sent
+ * for those objects in arbitrary order and the MediaStreamGraph has to be able
+ * to handle this.
  */
 class MediaStream {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStream)
 
   MediaStream(nsDOMMediaStream* aWrapper)
     : mBufferStartTime(0)
     , mExplicitBlockerCount(0)
     , mBlocked(false)
     , mGraphUpdateIndices(0)
     , mFinished(false)
     , mNotifiedFinished(false)
     , mAudioPlaybackStartTime(0)
     , mBlockedAudioTime(0)
-    , mMessageAffectedTime(0)
     , mWrapper(aWrapper)
     , mMainThreadCurrentTime(0)
     , mMainThreadFinished(false)
+    , mMainThreadDestroyed(false)
   {
     for (PRUint32 i = 0; i < ArrayLength(mFirstActiveTracks); ++i) {
       mFirstActiveTracks[i] = TRACK_NONE;
     }
   }
   virtual ~MediaStream() {}
 
   /**
@@ -252,19 +271,32 @@ public:
   void ChangeExplicitBlockerCount(PRInt32 aDelta);
   // Events will be dispatched by calling methods of aListener.
   void AddListener(MediaStreamListener* aListener);
   void RemoveListener(MediaStreamListener* aListener);
   // Signal that the client is done with this MediaStream. It will be deleted later.
   void Destroy();
   // Returns the main-thread's view of how much data has been processed by
   // this stream.
-  StreamTime GetCurrentTime() { return mMainThreadCurrentTime; }
+  StreamTime GetCurrentTime()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
+    return mMainThreadCurrentTime;
+  }
   // Return the main thread's view of whether this stream has finished.
-  bool IsFinished() { return mMainThreadFinished; }
+  bool IsFinished()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
+    return mMainThreadFinished;
+  }
+  bool IsDestroyed()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
+    return mMainThreadDestroyed;
+  }
 
   friend class MediaStreamGraphImpl;
 
   virtual SourceMediaStream* AsSourceStream() { return nullptr; }
 
   // media graph thread only
   void Init();
   // These Impl methods perform the core functionality of the control methods
@@ -339,17 +371,17 @@ protected:
   VideoFrame mLastPlayedVideoFrame;
   // The number of times this stream has been explicitly blocked by the control
   // API, minus the number of times it has been explicitly unblocked.
   TimeVarying<GraphTime,PRUint32> mExplicitBlockerCount;
   nsTArray<nsRefPtr<MediaStreamListener> > mListeners;
 
   // Precomputed blocking status (over GraphTime).
   // This is only valid between the graph's mCurrentTime and
-  // mBlockingDecisionsMadeUntilTime. The stream is considered to have
+  // mStateComputedTime. The stream is considered to have
   // not been blocked before mCurrentTime (its mBufferStartTime is increased
   // as necessary to account for that time instead) --- this avoids us having to
   // record the entire history of the stream's blocking-ness in mBlocked.
   TimeVarying<GraphTime,bool> mBlocked;
   // Maps graph time to the graph update that affected this stream at that time
   TimeVarying<GraphTime,PRInt64> mGraphUpdateIndices;
 
   /**
@@ -372,25 +404,22 @@ protected:
   // blocking.
   MediaTime mBlockedAudioTime;
 
   // For each track type, this is the first active track found for that type.
   // The first active track is the track that started earliest; if multiple
   // tracks start at the same time, the one with the lowest ID.
   TrackID mFirstActiveTracks[MediaSegment::TYPE_COUNT];
 
-  // Temporary data used by MediaStreamGraph on the graph thread
-  // The earliest time for which we would like to change this stream's output.
-  GraphTime mMessageAffectedTime;
-
   // This state is only used on the main thread.
   nsDOMMediaStream* mWrapper;
   // Main-thread views of state
   StreamTime mMainThreadCurrentTime;
   bool mMainThreadFinished;
+  bool mMainThreadDestroyed;
 };
 
 /**
  * This is a stream into which a decoder can write audio and video.
  *
  * Audio and video can be written on any thread, but you probably want to
  * always write from the same thread to avoid unexpected interleavings.
  */
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -119,17 +119,17 @@ let DOMApplicationRegistry = {
         "name": activity,
         "title": manifest.name,
         "icon": manifest.iconURLForSize(128),
         "description": description
       }
       cpmm.sendAsyncMessage("Activities:Register", json);
 
       let launchPath =
-        Services.io.newURI(manifest.fullLaunchPath(description.href), null, null);
+        Services.io.newURI(manifest.resolveFromOrigin(description.href), null, null);
       let manifestURL = Services.io.newURI(aApp.manifestURL, null, null);
       msgmgr.registerPage("activity", launchPath, manifestURL);
     }
   },
 
   _unregisterActivities: function(aManifest, aApp) {
     if (!aManifest.activities) {
       return;
@@ -997,15 +997,19 @@ DOMApplicationManifest.prototype = {
   },
 
   fullLaunchPath: function(aStartPoint) {
     let startPoint = aStartPoint || "";
     let launchPath = this._localeProp("launch_path") || "";
     return this._origin.resolve(launchPath + startPoint);
   },
 
+  resolveFromOrigin: function(aURI) {
+    return this._origin.resolve(aURI);
+  },
+
   fullAppcachePath: function() {
     let appcachePath = this._localeProp("appcache_path");
     return this._origin.resolve(appcachePath ? appcachePath : "");
   }
 };
 
 DOMApplicationRegistry.init();
--- a/dom/bluetooth/linux/BluetoothDBusService.cpp
+++ b/dom/bluetooth/linux/BluetoothDBusService.cpp
@@ -974,16 +974,17 @@ BluetoothDBusService::GetDeviceServiceCh
 {
   // This is a blocking call, should not be run on main thread.
   MOZ_ASSERT(!NS_IsMainThread());
 
   const char* deviceObjectPath = NS_ConvertUTF16toUTF8(aObjectPath).get();
   const char* pattern = NS_ConvertUTF16toUTF8(aPattern).get();
 
   DBusMessage *reply =
-    dbus_func_args(mConnection, deviceObjectPath,
+    dbus_func_args(gThreadConnection->GetConnection(),
+                   deviceObjectPath,
                    DBUS_DEVICE_IFACE, "GetServiceAttributeValue",
                    DBUS_TYPE_STRING, &pattern,
                    DBUS_TYPE_UINT16, &aAttributeId,
                    DBUS_TYPE_INVALID);
 
   return reply ? dbus_returns_int32(reply) : -1;
 }
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -445,17 +445,17 @@ PluginInstanceParent::AnswerNPN_SetValue
         ImageContainer *container = GetImageContainer();
         if (!container) {
             *result = NPERR_GENERIC_ERROR;
             return true;
         }
 
         mDrawingModel = drawingModel;
         *result = mNPNIface->setvalue(mNPP, NPPVpluginDrawingModel,
-                                      (void*)drawingModel);
+                                      (void*)(intptr_t)drawingModel);
 
         if (mRemoteImageDataShmem.IsWritable()) {
             container->SetRemoteImageData(nullptr, nullptr);
             container->SetCompositionNotifySink(nullptr);
             DeallocShmem(mRemoteImageDataShmem);
             mRemoteImageDataMutex = NULL;
         }
     } else {
--- a/editor/libeditor/base/nsEditRules.h
+++ b/editor/libeditor/base/nsEditRules.h
@@ -19,39 +19,39 @@ class nsISelection;
 /***************************************************************************
  * base for an object to encapsulate any additional info needed to be passed
  * to rules system by the editor
  */
 class nsRulesInfo
 {
   public:
   
-  nsRulesInfo(nsEditor::OperationID aAction) : action(aAction) {}
+  nsRulesInfo(OperationID aAction) : action(aAction) {}
   virtual ~nsRulesInfo() {}
   
-  nsEditor::OperationID action;
+  OperationID action;
 };
 
 /***************************************************************************
  * Interface of editing rules.
  *  
  */
 class nsIEditRules : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IEDITRULES_IID)
   
 //Interfaces for addref and release and queryinterface
 //NOTE: Use   NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsIEditRules
 
   NS_IMETHOD Init(nsPlaintextEditor *aEditor)=0;
   NS_IMETHOD DetachEditor()=0;
-  NS_IMETHOD BeforeEdit(nsEditor::OperationID action,
+  NS_IMETHOD BeforeEdit(OperationID action,
                         nsIEditor::EDirection aDirection) = 0;
-  NS_IMETHOD AfterEdit(nsEditor::OperationID action,
+  NS_IMETHOD AfterEdit(OperationID action,
                        nsIEditor::EDirection aDirection) = 0;
   NS_IMETHOD WillDoAction(mozilla::Selection* aSelection, nsRulesInfo* aInfo,
                           bool* aCancel, bool* aHandled) = 0;
   NS_IMETHOD DidDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, nsresult aResult)=0;
   NS_IMETHOD DocumentIsEmpty(bool *aDocumentIsEmpty)=0;
   NS_IMETHOD DocumentModified()=0;
 };
 
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -138,17 +138,17 @@ extern nsIParserService *sParserService;
 nsEditor::nsEditor()
 :  mPlaceHolderName(nullptr)
 ,  mSelState(nullptr)
 ,  mPhonetic(nullptr)
 ,  mModCount(0)
 ,  mFlags(0)
 ,  mUpdateCount(0)
 ,  mPlaceHolderBatch(0)
-,  mAction(kOpNone)
+,  mAction(OperationID::none)
 ,  mHandlingActionCount(0)
 ,  mIMETextOffset(0)
 ,  mIMEBufferLength(0)
 ,  mDirection(eNone)
 ,  mDocDirtyState(-1)
 ,  mSpellcheckCheckboxState(eTriUnset)
 ,  mInIMEMode(false)
 ,  mIsIMEComposing(false)
@@ -780,17 +780,17 @@ nsEditor::Undo(PRUint32 aCount)
 #endif
 
   ForceCompositionEnd();
 
   bool hasTxnMgr, hasTransaction = false;
   CanUndo(&hasTxnMgr, &hasTransaction);
   NS_ENSURE_TRUE(hasTransaction, NS_OK);
 
-  nsAutoRules beginRulesSniffing(this, kOpUndo, nsIEditor::eNone);
+  nsAutoRules beginRulesSniffing(this, OperationID::undo, nsIEditor::eNone);
 
   if (!mTxnMgr) {
     return NS_OK;
   }
 
   for (PRUint32 i = 0; i < aCount; ++i) {
     nsresult rv = mTxnMgr->UndoTransaction();
     NS_ENSURE_SUCCESS(rv, rv);
@@ -823,17 +823,17 @@ nsEditor::Redo(PRUint32 aCount)
 #ifdef NS_DEBUG_EDITOR
   if (gNoisy) { printf("Editor::Redo ----------\n"); }
 #endif
 
   bool hasTxnMgr, hasTransaction = false;
   CanRedo(&hasTxnMgr, &hasTransaction);
   NS_ENSURE_TRUE(hasTransaction, NS_OK);
 
-  nsAutoRules beginRulesSniffing(this, kOpRedo, nsIEditor::eNone);
+  nsAutoRules beginRulesSniffing(this, OperationID::redo, nsIEditor::eNone);
 
   if (!mTxnMgr) {
     return NS_OK;
   }
 
   for (PRUint32 i = 0; i < aCount; ++i) {
     nsresult rv = mTxnMgr->RedoTransaction();
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1342,17 +1342,17 @@ NS_IMETHODIMP nsEditor::SetSpellcheckUse
 
 NS_IMETHODIMP nsEditor::CreateNode(const nsAString& aTag,
                                    nsIDOMNode *    aParent,
                                    PRInt32         aPosition,
                                    nsIDOMNode **   aNewNode)
 {
   PRInt32 i;
 
-  nsAutoRules beginRulesSniffing(this, kOpCreateNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::createNode, nsIEditor::eNext);
   
   for (i = 0; i < mActionListeners.Count(); i++)
     mActionListeners[i]->WillCreateNode(aTag, aParent, aPosition);
 
   nsRefPtr<CreateElementTxn> txn;
   nsresult result = CreateTxnForCreateElement(aTag, aParent, aPosition,
                                               getter_AddRefs(txn));
   if (NS_SUCCEEDED(result)) 
@@ -1374,17 +1374,17 @@ NS_IMETHODIMP nsEditor::CreateNode(const
 }
 
 
 NS_IMETHODIMP nsEditor::InsertNode(nsIDOMNode * aNode,
                                    nsIDOMNode * aParent,
                                    PRInt32      aPosition)
 {
   PRInt32 i;
-  nsAutoRules beginRulesSniffing(this, kOpInsertNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertNode, nsIEditor::eNext);
 
   for (i = 0; i < mActionListeners.Count(); i++)
     mActionListeners[i]->WillInsertNode(aNode, aParent, aPosition);
 
   nsRefPtr<InsertElementTxn> txn;
   nsresult result = CreateTxnForInsertElement(aNode, aParent, aPosition,
                                               getter_AddRefs(txn));
   if (NS_SUCCEEDED(result))  {
@@ -1401,17 +1401,17 @@ NS_IMETHODIMP nsEditor::InsertNode(nsIDO
 
 
 NS_IMETHODIMP 
 nsEditor::SplitNode(nsIDOMNode * aNode,
                     PRInt32      aOffset,
                     nsIDOMNode **aNewLeftNode)
 {
   PRInt32 i;
-  nsAutoRules beginRulesSniffing(this, kOpSplitNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::splitNode, nsIEditor::eNext);
 
   for (i = 0; i < mActionListeners.Count(); i++)
     mActionListeners[i]->WillSplitNode(aNode, aOffset);
 
   nsRefPtr<SplitElementTxn> txn;
   nsresult result = CreateTxnForSplitNode(aNode, aOffset, getter_AddRefs(txn));
   if (NS_SUCCEEDED(result))  
   {
@@ -1450,17 +1450,17 @@ nsEditor::JoinNodes(nsINode* aNodeToKeep
 }
 
 NS_IMETHODIMP
 nsEditor::JoinNodes(nsIDOMNode * aLeftNode,
                     nsIDOMNode * aRightNode,
                     nsIDOMNode * aParent)
 {
   PRInt32 i;
-  nsAutoRules beginRulesSniffing(this, kOpJoinNode, nsIEditor::ePrevious);
+  nsAutoRules beginRulesSniffing(this, OperationID::joinNode, nsIEditor::ePrevious);
 
   // remember some values; later used for saved selection updating.
   // find the offset between the nodes to be joined.
   PRInt32 offset = GetChildOffset(aRightNode, aParent);
   // find the number of children of the lefthand node
   PRUint32 oldLeftNodeLen;
   nsresult result = GetLengthOfDOMNode(aLeftNode, oldLeftNodeLen);
   NS_ENSURE_SUCCESS(result, result);
@@ -1489,17 +1489,17 @@ nsEditor::DeleteNode(nsIDOMNode* aNode)
   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   NS_ENSURE_STATE(node);
   return DeleteNode(node);
 }
 
 nsresult
 nsEditor::DeleteNode(nsINode* aNode)
 {
-  nsAutoRules beginRulesSniffing(this, kOpCreateNode, nsIEditor::ePrevious);
+  nsAutoRules beginRulesSniffing(this, OperationID::createNode, nsIEditor::ePrevious);
 
   // save node location for selection updating code.
   for (PRInt32 i = 0; i < mActionListeners.Count(); i++) {
     mActionListeners[i]->WillDeleteNode(aNode->AsDOMNode());
   }
 
   nsRefPtr<DeleteNodeTxn> txn;
   nsresult res = CreateTxnForDeleteNode(aNode, getter_AddRefs(txn));
@@ -2190,17 +2190,17 @@ nsEditor::StartOperation(OperationID opI
 }
 
 
 /** All editor operations which alter the doc should be followed
  *  with a call to EndOperation */
 NS_IMETHODIMP
 nsEditor::EndOperation()
 {
-  mAction = kOpNone;
+  mAction = OperationID::none;
   mDirection = eNone;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsEditor::CloneAttribute(const nsAString & aAttribute,
                          nsIDOMNode *aDestNode, nsIDOMNode *aSourceNode)
 {
@@ -2700,17 +2700,17 @@ NS_IMETHODIMP nsEditor::CreateTxnForInse
 
 NS_IMETHODIMP nsEditor::DeleteText(nsIDOMCharacterData *aElement,
                               PRUint32             aOffset,
                               PRUint32             aLength)
 {
   nsRefPtr<DeleteTextTxn> txn;
   nsresult result = CreateTxnForDeleteText(aElement, aOffset, aLength,
                                            getter_AddRefs(txn));
-  nsAutoRules beginRulesSniffing(this, kOpDeleteText, nsIEditor::ePrevious);
+  nsAutoRules beginRulesSniffing(this, OperationID::deleteText, nsIEditor::ePrevious);
   if (NS_SUCCEEDED(result))  
   {
     // let listeners know what's up
     PRInt32 i;
     for (i = 0; i < mActionListeners.Count(); i++)
       mActionListeners[i]->WillDeleteText(aElement, aOffset, aLength);
     
     result = DoTransaction(txn); 
@@ -4306,17 +4306,17 @@ nsEditor::DeleteSelectionImpl(EDirection
   PRInt32 deleteCharOffset = 0, deleteCharLength = 0;
   res = CreateTxnForDeleteSelection(aAction, getter_AddRefs(txn),
                                     getter_AddRefs(deleteNode),
                                     &deleteCharOffset, &deleteCharLength);
   nsCOMPtr<nsIDOMCharacterData> deleteCharData(do_QueryInterface(deleteNode));
 
   if (NS_SUCCEEDED(res))  
   {
-    nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction);
+    nsAutoRules beginRulesSniffing(this, OperationID::deleteSelection, aAction);
     PRInt32 i;
     // Notify nsIEditActionListener::WillDelete[Selection|Text|Node]
     if (!deleteNode)
       for (i = 0; i < mActionListeners.Count(); i++)
         mActionListeners[i]->WillDeleteSelection(selection);
     else if (deleteCharData)
       for (i = 0; i < mActionListeners.Count(); i++)
         mActionListeners[i]->WillDeleteText(deleteCharData, deleteCharOffset, 1);
@@ -5038,24 +5038,23 @@ nsEditor::HandleInlineSpellCheck(Operati
                                    nsISelection *aSelection,
                                    nsIDOMNode *previousSelectedNode,
                                    PRInt32 previousSelectedOffset,
                                    nsIDOMNode *aStartNode,
                                    PRInt32 aStartOffset,
                                    nsIDOMNode *aEndNode,
                                    PRInt32 aEndOffset)
 {
-  return mInlineSpellChecker ? mInlineSpellChecker->SpellCheckAfterEditorChange(action,
-                                                       aSelection,
-                                                       previousSelectedNode,
-                                                       previousSelectedOffset,
-                                                       aStartNode,
-                                                       aStartOffset,
-                                                       aEndNode,
-                                                       aEndOffset) : NS_OK;
+  // Have to cast action here because this method is from an IDL
+  return mInlineSpellChecker ? mInlineSpellChecker->SpellCheckAfterEditorChange(
+                                 (PRInt32)action, aSelection,
+                                 previousSelectedNode, previousSelectedOffset,
+                                 aStartNode, aStartOffset, aEndNode,
+                                 aEndOffset)
+                             : NS_OK;
 }
 
 already_AddRefed<nsIContent>
 nsEditor::FindSelectionRoot(nsINode *aNode)
 {
   nsCOMPtr<nsIContent> rootContent = GetRoot();
   return rootContent.forget();
 }
--- a/editor/libeditor/base/nsEditor.h
+++ b/editor/libeditor/base/nsEditor.h
@@ -80,16 +80,63 @@ namespace mozilla {
 namespace widget {
 struct IMEState;
 } // namespace widget
 } // namespace mozilla
 
 #define kMOZEditorBogusNodeAttrAtom nsEditProperty::mozEditorBogusNode
 #define kMOZEditorBogusNodeValue NS_LITERAL_STRING("TRUE")
 
+// This is PRInt32 instead of PRInt16 because nsIInlineSpellChecker.idl's
+// spellCheckAfterEditorChange is defined to take it as a long.
+MOZ_BEGIN_ENUM_CLASS(OperationID, PRInt32)
+  ignore = -1,
+  none = 0,
+  undo,
+  redo,
+  insertNode,
+  createNode,
+  deleteNode,
+  splitNode,
+  joinNode,
+  deleteText = 1003,
+
+  // text commands
+  insertText         = 2000,
+  insertIMEText      = 2001,
+  deleteSelection    = 2002,
+  setTextProperty    = 2003,
+  removeTextProperty = 2004,
+  outputText         = 2005,
+
+  // html only action
+  insertBreak         = 3000,
+  makeList            = 3001,
+  indent              = 3002,
+  outdent             = 3003,
+  align               = 3004,
+  makeBasicBlock      = 3005,
+  removeList          = 3006,
+  makeDefListItem     = 3007,
+  insertElement       = 3008,
+  insertQuotation     = 3009,
+  htmlPaste           = 3012,
+  loadHTML            = 3013,
+  resetTextProperties = 3014,
+  setAbsolutePosition = 3015,
+  removeAbsolutePosition = 3016,
+  decreaseZIndex      = 3017,
+  increaseZIndex      = 3018
+MOZ_END_ENUM_CLASS(OperationID)
+
+inline bool operator!(const OperationID& aOp)
+{
+  return aOp == OperationID::none;
+}
+
 /** implementation of an editor object.  it will be the controller/focal point 
  *  for the main editor services. i.e. the GUIManager, publishing, transaction 
  *  manager, event interfaces. the idea for the event interfaces is to have them 
  *  delegate the actual commands to the editor independent of the XPFE implementation.
  */
 class nsEditor : public nsIEditor,
                  public nsIEditorIMESupport,
                  public nsSupportsWeakReference,
@@ -99,57 +146,16 @@ class nsEditor : public nsIEditor,
 public:
 
   enum IterDirection
   {
     kIterForward,
     kIterBackward
   };
 
-  enum OperationID
-  {
-    kOpIgnore = -1,
-    kOpNone = 0,
-    kOpUndo,
-    kOpRedo,
-    kOpInsertNode,
-    kOpCreateNode,
-    kOpDeleteNode,
-    kOpSplitNode,
-    kOpJoinNode,
-    kOpDeleteText = 1003,
-
-    // text commands
-    kOpInsertText         = 2000,
-    kOpInsertIMEText      = 2001,
-    kOpDeleteSelection    = 2002,
-    kOpSetTextProperty    = 2003,
-    kOpRemoveTextProperty = 2004,
-    kOpOutputText         = 2005,
-
-    // html only action
-    kOpInsertBreak         = 3000,
-    kOpMakeList            = 3001,
-    kOpIndent              = 3002,
-    kOpOutdent             = 3003,
-    kOpAlign               = 3004,
-    kOpMakeBasicBlock      = 3005,
-    kOpRemoveList          = 3006,
-    kOpMakeDefListItem     = 3007,
-    kOpInsertElement       = 3008,
-    kOpInsertQuotation     = 3009,
-    kOpHTMLPaste           = 3012,
-    kOpLoadHTML            = 3013,
-    kOpResetTextProperties = 3014,
-    kOpSetAbsolutePosition = 3015,
-    kOpRemoveAbsolutePosition = 3016,
-    kOpDecreaseZIndex      = 3017,
-    kOpIncreaseZIndex      = 3018
-  };
-
   /** The default constructor. This should suffice. the setting of the interfaces is done
    *  after the construction of the editor class.
    */
   nsEditor();
   /** The default destructor. This should suffice. Should this be pure virtual 
    *  for someone to derive from the nsEditor later? I don't believe so.
    */
   virtual ~nsEditor();
--- a/editor/libeditor/base/nsEditorUtils.h
+++ b/editor/libeditor/base/nsEditorUtils.h
@@ -73,17 +73,17 @@ class NS_STACK_CLASS nsAutoSelectionRese
 
 /***************************************************************************
  * stack based helper class for StartOperation()/EndOperation() sandwich
  */
 class NS_STACK_CLASS nsAutoRules
 {
   public:
   
-  nsAutoRules(nsEditor *ed, nsEditor::OperationID action,
+  nsAutoRules(nsEditor *ed, OperationID action,
               nsIEditor::EDirection aDirection) :
          mEd(ed), mDoNothing(false)
   { 
     if (mEd && !mEd->mAction) // mAction will already be set if this is nested call
     {
       mEd->StartOperation(action, aDirection);
     }
     else mDoNothing = true; // nested calls will end up here
--- a/editor/libeditor/html/nsHTMLAbsPosition.cpp
+++ b/editor/libeditor/html/nsHTMLAbsPosition.cpp
@@ -56,27 +56,27 @@ using namespace mozilla;
 
 #define  BLACK_BG_RGB_TRIGGER 0xd0
 
 NS_IMETHODIMP
 nsHTMLEditor::AbsolutePositionSelection(bool aEnabled)
 {
   nsAutoEditBatch beginBatching(this);
   nsAutoRules beginRulesSniffing(this,
-                                 aEnabled ? kOpSetAbsolutePosition :
-                                            kOpRemoveAbsolutePosition,
+                                 aEnabled ? OperationID::setAbsolutePosition :
+                                            OperationID::removeAbsolutePosition,
                                  nsIEditor::eNext);
   
   // the line below does not match the code; should it be removed?
   // Find out if the selection is collapsed:
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
-  nsTextRulesInfo ruleInfo(aEnabled ? kOpSetAbsolutePosition :
-                                      kOpRemoveAbsolutePosition);
+  nsTextRulesInfo ruleInfo(aEnabled ? OperationID::setAbsolutePosition :
+                                      OperationID::removeAbsolutePosition);
   bool cancel, handled;
   nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (NS_FAILED(res) || cancel)
     return res;
   
   return mRules->DidDoAction(selection, &ruleInfo, res);
 }
 
@@ -169,26 +169,26 @@ nsHTMLEditor::SetElementZIndex(nsIDOMEle
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLEditor::RelativeChangeZIndex(PRInt32 aChange)
 {
   nsAutoEditBatch beginBatching(this);
   nsAutoRules beginRulesSniffing(this,
-                                 (aChange < 0) ? kOpDecreaseZIndex :
-                                                 kOpIncreaseZIndex,
+                                 (aChange < 0) ? OperationID::decreaseZIndex :
+                                                 OperationID::increaseZIndex,
                                  nsIEditor::eNext);
   
   // brade: can we get rid of this comment?
   // Find out if the selection is collapsed:
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
-  nsTextRulesInfo ruleInfo(aChange < 0 ? kOpDecreaseZIndex :
-                                         kOpIncreaseZIndex);
+  nsTextRulesInfo ruleInfo(aChange < 0 ? OperationID::decreaseZIndex :
+                                         OperationID::increaseZIndex);
   bool cancel, handled;
   nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(res))
     return res;
   
   return mRules->DidDoAction(selection, &ruleInfo, res);
 }
 
--- a/editor/libeditor/html/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp
@@ -150,23 +150,23 @@ static nsCOMPtr<nsIDOMNode> GetTablePare
 
 NS_IMETHODIMP nsHTMLEditor::LoadHTML(const nsAString & aInputString)
 {
   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
 
   // force IME commit; set up rules sniffing and batching
   ForceCompositionEnd();
   nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpLoadHTML, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::loadHTML, nsIEditor::eNext);
 
   // Get selection
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_STATE(selection);
 
-  nsTextRulesInfo ruleInfo(kOpLoadHTML);
+  nsTextRulesInfo ruleInfo(OperationID::loadHTML);
   bool cancel, handled;
   nsresult rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel) {
     return NS_OK; // rules canceled the operation
   }
 
   if (!handled)
@@ -247,17 +247,17 @@ nsHTMLEditor::DoInsertHTMLWithContext(co
                                       bool aDeleteSelection,
                                       bool aTrustedInput)
 {
   NS_ENSURE_TRUE(mRules, NS_ERROR_NOT_INITIALIZED);
 
   // force IME commit; set up rules sniffing and batching
   ForceCompositionEnd();
   nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpHTMLPaste, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::htmlPaste, nsIEditor::eNext);
 
   // Get selection
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_STATE(selection);
 
   // create a dom document fragment that represents the structure to paste
   nsCOMPtr<nsIDOMNode> fragmentAsNode, streamStartParent, streamEndParent;
   PRInt32 streamStartOffset = 0, streamEndOffset = 0;
@@ -386,17 +386,17 @@ nsHTMLEditor::DoInsertHTMLWithContext(co
       rv = DeleteTableCell(1);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     // collapse selection to beginning of deleted table content
     selection->CollapseToStart();
   }
 
   // give rules a chance to handle or cancel
-  nsTextRulesInfo ruleInfo(kOpInsertElement);
+  nsTextRulesInfo ruleInfo(OperationID::insertElement);
   bool cancel, handled;
   rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel) {
     return NS_OK; // rules canceled the operation
   }
 
   if (!handled)
@@ -1741,24 +1741,24 @@ NS_IMETHODIMP nsHTMLEditor::PasteAsQuota
   nsAutoString citation;
   return PasteAsCitedQuotation(citation, aSelectionType);
 }
 
 NS_IMETHODIMP nsHTMLEditor::PasteAsCitedQuotation(const nsAString & aCitation,
                                                   PRInt32 aSelectionType)
 {
   nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertQuotation, nsIEditor::eNext);
 
   // get selection
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   // give rules a chance to handle or cancel
-  nsTextRulesInfo ruleInfo(kOpInsertElement);
+  nsTextRulesInfo ruleInfo(OperationID::insertElement);
   bool cancel, handled;
   nsresult rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel || handled) {
     return NS_OK; // rules canceled the operation
   }
 
   nsCOMPtr<nsIDOMNode> newNode;
@@ -1949,20 +1949,20 @@ nsHTMLEditor::InsertAsPlaintextQuotation
     return nsPlaintextEditor::InsertAsQuotation(aQuotedText, aNodeInserted);
 
   nsCOMPtr<nsIDOMNode> newNode;
   // get selection
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertQuotation, nsIEditor::eNext);
 
   // give rules a chance to handle or cancel
-  nsTextRulesInfo ruleInfo(kOpInsertElement);
+  nsTextRulesInfo ruleInfo(OperationID::insertElement);
   bool cancel, handled;
   nsresult rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel || handled) {
     return NS_OK; // rules canceled the operation
   }
 
   // Wrap the inserted quote in a <span> so it won't be wrapped:
@@ -2042,20 +2042,20 @@ nsHTMLEditor::InsertAsCitedQuotation(con
 
   nsCOMPtr<nsIDOMNode> newNode;
 
   // get selection
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpInsertQuotation, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertQuotation, nsIEditor::eNext);
 
   // give rules a chance to handle or cancel
-  nsTextRulesInfo ruleInfo(kOpInsertElement);
+  nsTextRulesInfo ruleInfo(OperationID::insertElement);
   bool cancel, handled;
   nsresult rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel || handled) {
     return NS_OK; // rules canceled the operation
   }
 
   rv = DeleteSelectionAndCreateNode(NS_LITERAL_STRING("blockquote"), getter_AddRefs(newNode));
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -84,29 +84,29 @@ static bool IsBlockNode(nsIDOMNode* node
 }
 
 static bool IsInlineNode(nsIDOMNode* node)
 {
   return !IsBlockNode(node);
 }
 
 static bool
-IsStyleCachePreservingAction(nsEditor::OperationID action)
-{
-  return action == nsEditor::kOpDeleteSelection ||
-         action == nsEditor::kOpInsertBreak ||
-         action == nsEditor::kOpMakeList ||
-         action == nsEditor::kOpIndent ||
-         action == nsEditor::kOpOutdent ||
-         action == nsEditor::kOpAlign ||
-         action == nsEditor::kOpMakeBasicBlock ||
-         action == nsEditor::kOpRemoveList ||
-         action == nsEditor::kOpMakeDefListItem ||
-         action == nsEditor::kOpInsertElement ||
-         action == nsEditor::kOpInsertQuotation;
+IsStyleCachePreservingAction(OperationID action)
+{
+  return action == OperationID::deleteSelection ||
+         action == OperationID::insertBreak ||
+         action == OperationID::makeList ||
+         action == OperationID::indent ||
+         action == OperationID::outdent ||
+         action == OperationID::align ||
+         action == OperationID::makeBasicBlock ||
+         action == OperationID::removeList ||
+         action == OperationID::makeDefListItem ||
+         action == OperationID::insertElement ||
+         action == OperationID::insertQuotation;
 }
  
 class nsTableCellAndListItemFunctor : public nsBoolDomIterFunctor
 {
   public:
     virtual bool operator()(nsIDOMNode* aNode)  // used to build list of all li's, td's & th's iterator covers
     {
       if (nsHTMLEditUtils::IsTableCell(aNode)) return true;
@@ -272,17 +272,17 @@ nsHTMLEditRules::Init(nsPlaintextEditor 
 NS_IMETHODIMP
 nsHTMLEditRules::DetachEditor()
 {
   mHTMLEditor = nullptr;
   return nsTextEditRules::DetachEditor();
 }
 
 NS_IMETHODIMP
-nsHTMLEditRules::BeforeEdit(nsEditor::OperationID action,
+nsHTMLEditRules::BeforeEdit(OperationID action,
                             nsIEditor::EDirection aDirection)
 {
   if (mLockRulesSniffing) return NS_OK;
   
   nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this);
   mDidExplicitlySetInterline = false;
 
   if (!mActionNesting++)
@@ -325,19 +325,19 @@ nsHTMLEditRules::BeforeEdit(nsEditor::Op
     }
     if(mUtilRange)
     {
       // ditto for mUtilRange.
       mUtilRange->Reset(); 
     }
 
     // remember current inline styles for deletion and normal insertion operations
-    if (action == nsEditor::kOpInsertText ||
-        action == nsEditor::kOpInsertIMEText ||
-        action == nsEditor::kOpDeleteSelection ||
+    if (action == OperationID::insertText ||
+        action == OperationID::insertIMEText ||
+        action == OperationID::deleteSelection ||
         IsStyleCachePreservingAction(action)) {
       nsCOMPtr<nsIDOMNode> selNode = selStartNode;
       if (aDirection == nsIEditor::eNext)
         selNode = selEndNode;
       res = CacheInlineStyles(selNode);
       NS_ENSURE_SUCCESS(res, res);
     }
 
@@ -356,17 +356,17 @@ nsHTMLEditRules::BeforeEdit(nsEditor::Op
     // let rules remember the top level action
     mTheAction = action;
   }
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
-nsHTMLEditRules::AfterEdit(nsEditor::OperationID action,
+nsHTMLEditRules::AfterEdit(OperationID action,
                            nsIEditor::EDirection aDirection)
 {
   if (mLockRulesSniffing) return NS_OK;
 
   nsAutoLockRulesSniffing lockIt(this);
 
   NS_PRECONDITION(mActionNesting>0, "bad action nesting!");
   nsresult res = NS_OK;
@@ -391,21 +391,21 @@ nsHTMLEditRules::AfterEdit(nsEditor::Ope
     }
   }
 
   return res;
 }
 
 
 nsresult
-nsHTMLEditRules::AfterEditInner(nsEditor::OperationID action,
+nsHTMLEditRules::AfterEditInner(OperationID action,
                                 nsIEditor::EDirection aDirection)
 {
   ConfirmSelectionInBody();
-  if (action == nsEditor::kOpIgnore) return NS_OK;
+  if (action == OperationID::ignore) return NS_OK;
   
   nsCOMPtr<nsISelection>selection;
   nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   NS_ENSURE_SUCCESS(res, res);
   
   nsCOMPtr<nsIDOMNode> rangeStartParent, rangeEndParent;
   PRInt32 rangeStartOffset = 0, rangeEndOffset = 0;
   // do we have a real range to act on?
@@ -415,59 +415,59 @@ nsHTMLEditRules::AfterEditInner(nsEditor
     mDocChangeRange->GetStartContainer(getter_AddRefs(rangeStartParent));
     mDocChangeRange->GetEndContainer(getter_AddRefs(rangeEndParent));
     mDocChangeRange->GetStartOffset(&rangeStartOffset);
     mDocChangeRange->GetEndOffset(&rangeEndOffset);
     if (rangeStartParent && rangeEndParent) 
       bDamagedRange = true; 
   }
   
-  if (bDamagedRange && !((action == nsEditor::kOpUndo) || (action == nsEditor::kOpRedo)))
+  if (bDamagedRange && !((action == OperationID::undo) || (action == OperationID::redo)))
   {
     // don't let any txns in here move the selection around behind our back.
     // Note that this won't prevent explicit selection setting from working.
     nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
    
     // expand the "changed doc range" as needed
     res = PromoteRange(mDocChangeRange, action);
     NS_ENSURE_SUCCESS(res, res);
 
     // if we did a ranged deletion, make sure we have a place to put caret.
     // Note we only want to do this if the overall operation was deletion,
-    // not if deletion was done along the way for kOpLoadHTML, kOpInsertText, etc.
+    // not if deletion was done along the way for OperationID::loadHTML, OperationID::insertText, etc.
     // That's why this is here rather than DidDeleteSelection().
-    if ((action == nsEditor::kOpDeleteSelection) && mDidRangedDelete)
+    if ((action == OperationID::deleteSelection) && mDidRangedDelete)
     {
       res = InsertBRIfNeeded(selection);
       NS_ENSURE_SUCCESS(res, res);
     }  
     
     // add in any needed <br>s, and remove any unneeded ones.
     res = AdjustSpecialBreaks();
     NS_ENSURE_SUCCESS(res, res);
     
     // merge any adjacent text nodes
-    if ( (action != nsEditor::kOpInsertText &&
-         action != nsEditor::kOpInsertIMEText) )
+    if ( (action != OperationID::insertText &&
+         action != OperationID::insertIMEText) )
     {
       res = mHTMLEditor->CollapseAdjacentTextNodes(mDocChangeRange);
       NS_ENSURE_SUCCESS(res, res);
     }
 
     // clean up any empty nodes in the selection
     res = RemoveEmptyNodes();
     NS_ENSURE_SUCCESS(res, res);
 
     // attempt to transform any unneeded nbsp's into spaces after doing various operations
-    if ((action == nsEditor::kOpInsertText) || 
-        (action == nsEditor::kOpInsertIMEText) ||
-        (action == nsEditor::kOpDeleteSelection) ||
-        (action == nsEditor::kOpInsertBreak) || 
-        (action == nsHTMLEditor::kOpHTMLPaste ||
-        (action == nsHTMLEditor::kOpLoadHTML)))
+    if ((action == OperationID::insertText) || 
+        (action == OperationID::insertIMEText) ||
+        (action == OperationID::deleteSelection) ||
+        (action == OperationID::insertBreak) || 
+        (action == OperationID::htmlPaste ||
+        (action == OperationID::loadHTML)))
     {
       res = AdjustWhitespace(selection);
       NS_ENSURE_SUCCESS(res, res);
       
       // also do this for original selection endpoints. 
       nsWSRunObject(mHTMLEditor, mRangeItem->startNode,
                     mRangeItem->startOffset).AdjustWhitespace();
       // we only need to handle old selection endpoint if it was different from start
@@ -481,31 +481,31 @@ nsHTMLEditRules::AfterEditInner(nsEditor
     // if we created a new block, make sure selection lands in it
     if (mNewBlock)
     {
       res = PinSelectionToNewBlock(selection);
       mNewBlock = 0;
     }
 
     // adjust selection for insert text, html paste, and delete actions
-    if ((action == nsEditor::kOpInsertText) || 
-        (action == nsEditor::kOpInsertIMEText) ||
-        (action == nsEditor::kOpDeleteSelection) ||
-        (action == nsEditor::kOpInsertBreak) || 
-        (action == nsHTMLEditor::kOpHTMLPaste ||
-        (action == nsHTMLEditor::kOpLoadHTML)))
+    if ((action == OperationID::insertText) || 
+        (action == OperationID::insertIMEText) ||
+        (action == OperationID::deleteSelection) ||
+        (action == OperationID::insertBreak) || 
+        (action == OperationID::htmlPaste ||
+        (action == OperationID::loadHTML)))
     {
       res = AdjustSelection(selection, aDirection);
       NS_ENSURE_SUCCESS(res, res);
     }
 
     // check for any styles which were removed inappropriately
-    if (action == nsEditor::kOpInsertText ||
-        action == nsEditor::kOpInsertIMEText ||
-        action == nsEditor::kOpDeleteSelection ||
+    if (action == OperationID::insertText ||
+        action == OperationID::insertIMEText ||
+        action == OperationID::deleteSelection ||
         IsStyleCachePreservingAction(action)) {
       mHTMLEditor->mTypeInState->UpdateSelState(selection);
       res = ReapplyCachedStyles();
       NS_ENSURE_SUCCESS(res, res);
       res = ClearCachedStyles();
       NS_ENSURE_SUCCESS(res, res);
     }    
   }
@@ -543,19 +543,19 @@ nsHTMLEditRules::WillDoAction(Selection*
   *aCancel = false;
   *aHandled = false;
 
   // my kingdom for dynamic cast
   nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);
 
   // Deal with actions for which we don't need to check whether the selection is
   // editable.
-  if (info->action == nsEditor::kOpOutputText ||
-      info->action == nsEditor::kOpUndo ||
-      info->action == nsEditor::kOpRedo) {
+  if (info->action == OperationID::outputText ||
+      info->action == OperationID::undo ||
+      info->action == OperationID::redo) {
     return nsTextEditRules::WillDoAction(aSelection, aInfo, aCancel, aHandled);
   }
 
   // Nothing to do if there's no selection to act on
   if (!aSelection) {
     return NS_OK;
   }
   NS_ENSURE_TRUE(aSelection->GetRangeCount(), NS_OK);
@@ -578,77 +578,77 @@ nsHTMLEditRules::WillDoAction(Selection*
 
     if (!mHTMLEditor->IsModifiableNode(range->GetCommonAncestor())) {
       *aCancel = true;
       return NS_OK;
     }
   }
 
   switch (info->action) {
-    case nsEditor::kOpInsertText:
-    case nsEditor::kOpInsertIMEText:
+    case OperationID::insertText:
+    case OperationID::insertIMEText:
       return WillInsertText(info->action, aSelection, aCancel, aHandled,
                             info->inString, info->outString, info->maxLength);
-    case nsEditor::kOpLoadHTML:
+    case OperationID::loadHTML:
       return WillLoadHTML(aSelection, aCancel);
-    case nsEditor::kOpInsertBreak:
+    case OperationID::insertBreak:
       return WillInsertBreak(aSelection, aCancel, aHandled);
-    case nsEditor::kOpDeleteSelection:
+    case OperationID::deleteSelection:
       return WillDeleteSelection(aSelection, info->collapsedAction,
                                  info->stripWrappers, aCancel, aHandled);
-    case nsEditor::kOpMakeList:
+    case OperationID::makeList:
       return WillMakeList(aSelection, info->blockType, info->entireList,
                           info->bulletType, aCancel, aHandled);
-    case nsEditor::kOpIndent:
+    case OperationID::indent:
       return WillIndent(aSelection, aCancel, aHandled);
-    case nsEditor::kOpOutdent:
+    case OperationID::outdent:
       return WillOutdent(aSelection, aCancel, aHandled);
-    case nsEditor::kOpSetAbsolutePosition:
+    case OperationID::setAbsolutePosition:
       return WillAbsolutePosition(aSelection, aCancel, aHandled);
-    case nsEditor::kOpRemoveAbsolutePosition:
+    case OperationID::removeAbsolutePosition:
       return WillRemoveAbsolutePosition(aSelection, aCancel, aHandled);
-    case nsEditor::kOpAlign:
+    case OperationID::align:
       return WillAlign(aSelection, info->alignType, aCancel, aHandled);
-    case nsEditor::kOpMakeBasicBlock:
+    case OperationID::makeBasicBlock:
       return WillMakeBasicBlock(aSelection, info->blockType, aCancel, aHandled);
-    case nsEditor::kOpRemoveList:
+    case OperationID::removeList:
       return WillRemoveList(aSelection, info->bOrdered, aCancel, aHandled);
-    case nsEditor::kOpMakeDefListItem:
+    case OperationID::makeDefListItem:
       return WillMakeDefListItem(aSelection, info->blockType, info->entireList,
                                  aCancel, aHandled);
-    case nsEditor::kOpInsertElement:
+    case OperationID::insertElement:
       return WillInsert(aSelection, aCancel);
-    case nsEditor::kOpDecreaseZIndex:
+    case OperationID::decreaseZIndex:
       return WillRelativeChangeZIndex(aSelection, -1, aCancel, aHandled);
-    case nsEditor::kOpIncreaseZIndex:
+    case OperationID::increaseZIndex:
       return WillRelativeChangeZIndex(aSelection, 1, aCancel, aHandled);
     default:
       return nsTextEditRules::WillDoAction(aSelection, aInfo,
                                            aCancel, aHandled);
   }
 }
 
 
 NS_IMETHODIMP 
 nsHTMLEditRules::DidDoAction(nsISelection *aSelection,
                              nsRulesInfo *aInfo, nsresult aResult)
 {
   nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);
   switch (info->action)
   {
-    case nsEditor::kOpInsertBreak:
+    case OperationID::insertBreak:
       return DidInsertBreak(aSelection, aResult);
-    case nsEditor::kOpDeleteSelection:
+    case OperationID::deleteSelection:
       return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
-    case nsEditor::kOpMakeBasicBlock:
-    case nsEditor::kOpIndent:
-    case nsEditor::kOpOutdent:
-    case nsEditor::kOpAlign:
+    case OperationID::makeBasicBlock:
+    case OperationID::indent:
+    case OperationID::outdent:
+    case OperationID::align:
       return DidMakeBasicBlock(aSelection, aInfo, aResult);
-    case nsEditor::kOpSetAbsolutePosition: {
+    case OperationID::setAbsolutePosition: {
       nsresult rv = DidMakeBasicBlock(aSelection, aInfo, aResult);
       NS_ENSURE_SUCCESS(rv, rv);
       return DidAbsolutePosition();
     }
     default:
       // pass thru to nsTextEditRules
       return nsTextEditRules::DidDoAction(aSelection, aInfo, aResult);
   }
@@ -797,23 +797,23 @@ nsHTMLEditRules::GetAlignment(bool *aMix
            offset == rootOffset)
   {
     // if we have selected the body, let's look at the first editable node
     mHTMLEditor->GetNextNode(parent, offset, true, address_of(nodeToExamine));
   }
   else
   {
     nsCOMArray<nsIDOMRange> arrayOfRanges;
-    res = GetPromotedRanges(selection, arrayOfRanges, nsEditor::kOpAlign);
+    res = GetPromotedRanges(selection, arrayOfRanges, OperationID::align);
     NS_ENSURE_SUCCESS(res, res);
 
     // use these ranges to construct a list of nodes to act on.
     nsCOMArray<nsIDOMNode> arrayOfNodes;
     res = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
-                               nsEditor::kOpAlign, true);
+                               OperationID::align, true);
     NS_ENSURE_SUCCESS(res, res);                                 
     nodeToExamine = arrayOfNodes.SafeObjectAt(0);
   }
 
   NS_ENSURE_TRUE(nodeToExamine, NS_ERROR_NULL_POINTER);
 
   NS_NAMED_LITERAL_STRING(typeAttrName, "align");
   nsIAtom  *dummyProperty = nullptr;
@@ -922,17 +922,17 @@ nsHTMLEditRules::GetIndentState(bool *aC
   nsCOMPtr<nsISelection>selection;
   nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   NS_ENSURE_SUCCESS(res, res);
   nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
   NS_ENSURE_TRUE(selPriv, NS_ERROR_FAILURE);
 
   // contruct a list of nodes to act on.
   nsCOMArray<nsIDOMNode> arrayOfNodes;
-  res = GetNodesFromSelection(selection, nsEditor::kOpIndent,
+  res = GetNodesFromSelection(selection, OperationID::indent,
                               arrayOfNodes, true);
   NS_ENSURE_SUCCESS(res, res);
 
   // examine nodes in selection for blockquotes or list elements;
   // these we can outdent.  Note that we return true for canOutdent
   // if *any* of the selection is outdentable, rather than all of it.
   PRInt32 listCount = arrayOfNodes.Count();
   PRInt32 i;
@@ -1221,44 +1221,44 @@ nsHTMLEditRules::WillInsert(nsISelection
       // the selection start to be before the mozBR.
       selNode = nsEditor::GetNodeLocation(priorNode, &selOffset);
       res = aSelection->Collapse(selNode,selOffset);
       NS_ENSURE_SUCCESS(res, res);
     }
   }
 
   if (mDidDeleteSelection &&
-      (mTheAction == nsEditor::kOpInsertText ||
-       mTheAction == nsEditor::kOpInsertIMEText ||
-       mTheAction == nsEditor::kOpDeleteSelection)) {
+      (mTheAction == OperationID::insertText ||
+       mTheAction == OperationID::insertIMEText ||
+       mTheAction == OperationID::deleteSelection)) {
     res = ReapplyCachedStyles();
     NS_ENSURE_SUCCESS(res, res);
   }
   // For most actions we want to clear the cached styles, but there are
   // exceptions
   if (!IsStyleCachePreservingAction(mTheAction)) {
     res = ClearCachedStyles();
     NS_ENSURE_SUCCESS(res, res);
   }
 
   return NS_OK;
 }    
 
 nsresult
-nsHTMLEditRules::WillInsertText(nsEditor::OperationID aAction,
+nsHTMLEditRules::WillInsertText(OperationID aAction,
                                 Selection*       aSelection,
                                 bool            *aCancel,
                                 bool            *aHandled,
                                 const nsAString *inString,
                                 nsAString       *outString,
                                 PRInt32          aMaxLength)
 {  
   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
 
-  if (inString->IsEmpty() && aAction != nsEditor::kOpInsertIMEText) {
+  if (inString->IsEmpty() && aAction != OperationID::insertIMEText) {
     // HACK: this is a fix for bug 19395
     // I can't outlaw all empty insertions
     // because IME transaction depend on them
     // There is more work to do to make the 
     // world safe for IME.
     *aCancel = true;
     *aHandled = false;
     return NS_OK;
@@ -1296,17 +1296,17 @@ nsHTMLEditRules::WillInsertText(nsEditor
   NS_ENSURE_SUCCESS(res, res);
 
   // dont put text in places that can't have it
   if (!mHTMLEditor->IsTextNode(selNode) &&
       !mHTMLEditor->CanContainTag(selNode, nsGkAtoms::textTagName)) {
     return NS_ERROR_FAILURE;
   }
     
-  if (aAction == nsEditor::kOpInsertIMEText) {
+  if (aAction == OperationID::insertIMEText) {
     // Right now the nsWSRunObject code bails on empty strings, but IME needs 
     // the InsertTextImpl() call to still happen since empty strings are meaningful there.
     if (inString->IsEmpty())
     {
       res = mHTMLEditor->InsertTextImpl(*inString, address_of(selNode), &selOffset, doc);
     }
     else
     {
@@ -2716,17 +2716,17 @@ nsHTMLEditRules::JoinBlocks(nsCOMPtr<nsI
 */
 nsresult
 nsHTMLEditRules::MoveBlock(nsIDOMNode *aLeftBlock, nsIDOMNode *aRightBlock, PRInt32 aLeftOffset, PRInt32 aRightOffset)
 {
   nsCOMArray<nsIDOMNode> arrayOfNodes;
   nsCOMPtr<nsISupports> isupports;
   // GetNodesFromPoint is the workhorse that figures out what we wnat to move.
   nsresult res = GetNodesFromPoint(DOMPoint(aRightBlock,aRightOffset),
-                                   nsEditor::kOpMakeList, arrayOfNodes, true);
+                                   OperationID::makeList, arrayOfNodes, true);
   NS_ENSURE_SUCCESS(res, res);
   PRInt32 listCount = arrayOfNodes.Count();
   PRInt32 i;
   for (i=0; i<listCount; i++)
   {
     // get the node to act on
     nsIDOMNode* curNode = arrayOfNodes[i];
     if (IsBlockNode(curNode))
@@ -3160,17 +3160,17 @@ nsHTMLEditRules::WillRemoveList(Selectio
   *aCancel = false;
   *aHandled = true;
   
   nsresult res = NormalizeSelection(aSelection);
   NS_ENSURE_SUCCESS(res, res);
   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
   
   nsCOMArray<nsIDOMRange> arrayOfRanges;
-  res = GetPromotedRanges(aSelection, arrayOfRanges, nsEditor::kOpMakeList);
+  res = GetPromotedRanges(aSelection, arrayOfRanges, OperationID::makeList);
   NS_ENSURE_SUCCESS(res, res);
   
   // use these ranges to contruct a list of nodes to act on.
   nsCOMArray<nsIDOMNode> arrayOfNodes;
   res = GetListActionNodes(arrayOfNodes, false);
   NS_ENSURE_SUCCESS(res, res);                                 
                                      
   // Remove all non-editable nodes.  Leave them be.
@@ -3248,17 +3248,17 @@ nsHTMLEditRules::WillMakeBasicBlock(Sele
   NS_ENSURE_SUCCESS(res, res);
   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
   nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
   *aHandled = true;
   nsString tString(*aBlockType);
 
   // contruct a list of nodes to act on.
   nsCOMArray<nsIDOMNode> arrayOfNodes;
-  res = GetNodesFromSelection(aSelection, nsEditor::kOpMakeBasicBlock,
+  res = GetNodesFromSelection(aSelection, OperationID::makeBasicBlock,
                               arrayOfNodes);
   NS_ENSURE_SUCCESS(res, res);
 
   // Remove all non-editable nodes.  Leave them be.
   PRInt32 listCount = arrayOfNodes.Count();
   PRInt32 i;
   for (i=listCount-1; i>=0; i--)
   {
@@ -3440,17 +3440,17 @@ nsHTMLEditRules::WillCSSIndent(Selection
     arrayOfNodes.AppendObject(liNode);
   }
   else
   {
     // convert the selection ranges into "promoted" selection ranges:
     // this basically just expands the range to include the immediate
     // block parent, and then further expands to include any ancestors
     // whose children are all in the range
-    res = GetNodesFromSelection(aSelection, nsEditor::kOpIndent, arrayOfNodes);
+    res = GetNodesFromSelection(aSelection, OperationID::indent, arrayOfNodes);
     NS_ENSURE_SUCCESS(res, res);
   }
   
   NS_NAMED_LITERAL_STRING(quoteType, "blockquote");
   // if nothing visible in list, make an empty block
   if (ListIsEmptyLine(arrayOfNodes))
   {
     nsCOMPtr<nsIDOMNode> parent, theBlock;
@@ -3622,22 +3622,22 @@ nsHTMLEditRules::WillHTMLIndent(Selectio
   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
   
   // convert the selection ranges into "promoted" selection ranges:
   // this basically just expands the range to include the immediate
   // block parent, and then further expands to include any ancestors
   // whose children are all in the range
   
   nsCOMArray<nsIDOMRange> arrayOfRanges;
-  res = GetPromotedRanges(aSelection, arrayOfRanges, nsEditor::kOpIndent);
+  res = GetPromotedRanges(aSelection, arrayOfRanges, OperationID::indent);
   NS_ENSURE_SUCCESS(res, res);
   
   // use these ranges to contruct a list of nodes to act on.
   nsCOMArray<nsIDOMNode> arrayOfNodes;
-  res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, nsEditor::kOpIndent);
+  res = GetNodesForOperation(arrayOfRanges, arrayOfNodes, OperationID::indent);
   NS_ENSURE_SUCCESS(res, res);                                 
                                      
   NS_NAMED_LITERAL_STRING(quoteType, "blockquote");
 
   // if nothing visible in list, make an empty block
   if (ListIsEmptyLine(arrayOfNodes))
   {
     nsCOMPtr<nsIDOMNode> parent, theBlock;
@@ -3845,17 +3845,17 @@ nsHTMLEditRules::WillOutdent(Selection* 
   {
     nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
     
     // convert the selection ranges into "promoted" selection ranges:
     // this basically just expands the range to include the immediate
     // block parent, and then further expands to include any ancestors
     // whose children are all in the range
     nsCOMArray<nsIDOMNode> arrayOfNodes;
-    res = GetNodesFromSelection(aSelection, nsEditor::kOpOutdent,
+    res = GetNodesFromSelection(aSelection, OperationID::outdent,
                                 arrayOfNodes);
     NS_ENSURE_SUCCESS(res, res);
 
     // Ok, now go through all the nodes and remove a level of blockquoting, 
     // or whatever is appropriate.  Wohoo!
 
     nsCOMPtr<nsIDOMNode> curBlockQuote, firstBQChild, lastBQChild;
     bool curBlockQuoteIsIndentedWithCSS = false;
@@ -4419,17 +4419,17 @@ nsHTMLEditRules::WillAlign(Selection* aS
   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
 
   // convert the selection ranges into "promoted" selection ranges:
   // this basically just expands the range to include the immediate
   // block parent, and then further expands to include any ancestors
   // whose children are all in the range
   *aHandled = true;
   nsCOMArray<nsIDOMNode> arrayOfNodes;
-  res = GetNodesFromSelection(aSelection, nsEditor::kOpAlign, arrayOfNodes);
+  res = GetNodesFromSelection(aSelection, OperationID::align, arrayOfNodes);
   NS_ENSURE_SUCCESS(res, res);
 
   // if we don't have any nodes, or we have only a single br, then we are
   // creating an empty alignment div.  We have to do some different things for these.
   bool emptyDiv = false;
   PRInt32 listCount = arrayOfNodes.Count();
   if (!listCount) emptyDiv = true;
   if (listCount == 1)
@@ -5204,33 +5204,33 @@ nsHTMLEditRules::NormalizeSelection(nsIS
 
 
 ///////////////////////////////////////////////////////////////////////////
 // GetPromotedPoint: figure out where a start or end point for a block
 //                   operation really is
 void
 nsHTMLEditRules::GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode* aNode,
                                   PRInt32 aOffset,
-                                  nsEditor::OperationID actionID,
+                                  OperationID actionID,
                                   nsCOMPtr<nsIDOMNode>* outNode,
                                   PRInt32* outOffset)
 {
   nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
   MOZ_ASSERT(node && outNode && outOffset);
 
   // default values
   *outNode = node->AsDOMNode();
   *outOffset = aOffset;
 
   // we do one thing for text actions, something else entirely for other
   // actions
-  if (actionID == nsEditor::kOpInsertText ||
-      actionID == nsEditor::kOpInsertIMEText ||
-      actionID == nsEditor::kOpInsertBreak ||
-      actionID == nsEditor::kOpDeleteText) {
+  if (actionID == OperationID::insertText ||
+      actionID == OperationID::insertIMEText ||
+      actionID == OperationID::insertBreak ||
+      actionID == OperationID::deleteText) {
     bool isSpace, isNBSP;
     nsCOMPtr<nsIContent> content = do_QueryInterface(node), temp;
     // for text actions, we want to look backwards (or forwards, as
     // appropriate) for additional whitespace or nbsp's.  We may have to act on
     // these later even though they are outside of the initial selection.  Even
     // if they are in another node!
     while (content) {
       PRInt32 offset;
@@ -5289,32 +5289,32 @@ nsHTMLEditRules::GetPromotedPoint(RulesE
     nsCOMPtr<nsIContent> nearNode =
       mHTMLEditor->GetPriorHTMLNode(node, offset, true);
     while (!nearNode && node->Tag() != nsGkAtoms::body &&
            node->GetNodeParent()) {
       // some cutoffs are here: we don't need to also include them in the
       // aWhere == kEnd case.  as long as they are in one or the other it will
       // work.  special case for outdent: don't keep looking up if we have
       // found a blockquote element to act on
-      if (actionID == nsHTMLEditor::kOpOutdent &&
+      if (actionID == OperationID::outdent &&
           node->Tag() == nsGkAtoms::blockquote) {
         break;
       }
 
       PRInt32 parentOffset = node->GetNodeParent()->IndexOf(node);
       nsCOMPtr<nsINode> parent = node->GetNodeParent();
 
       // Don't walk past the editable section. Note that we need to check
       // before walking up to a parent because we need to return the parent
       // object, so the parent itself might not be in the editable area, but
       // it's OK if we're not performing a block-level action.
-      bool blockLevelAction = actionID == nsHTMLEditor::kOpIndent ||
-                              actionID == nsHTMLEditor::kOpOutdent ||
-                              actionID == nsHTMLEditor::kOpAlign ||
-                              actionID == nsHTMLEditor::kOpMakeBasicBlock;
+      bool blockLevelAction = actionID == OperationID::indent ||
+                              actionID == OperationID::outdent ||
+                              actionID == OperationID::align ||
+                              actionID == OperationID::makeBasicBlock;
       if (!mHTMLEditor->IsDescendantOfEditorRoot(parent) &&
           (blockLevelAction || !mHTMLEditor->IsDescendantOfEditorRoot(node))) {
         break;
       }
 
       node = parent;
       offset = parentOffset;
       nearNode = mHTMLEditor->GetPriorHTMLNode(node, offset, true);
@@ -5381,17 +5381,17 @@ nsHTMLEditRules::GetPromotedPoint(RulesE
 
 ///////////////////////////////////////////////////////////////////////////
 // GetPromotedRanges: run all the selection range endpoint through 
 //                    GetPromotedPoint()
 //                       
 nsresult 
 nsHTMLEditRules::GetPromotedRanges(nsISelection *inSelection, 
                                    nsCOMArray<nsIDOMRange> &outArrayOfRanges, 
-                                   nsEditor::OperationID inOperationType)
+                                   OperationID inOperationType)
 {
   NS_ENSURE_TRUE(inSelection, NS_ERROR_NULL_POINTER);
 
   PRInt32 rangeCount;
   nsresult res = inSelection->GetRangeCount(&rangeCount);
   NS_ENSURE_SUCCESS(res, res);
   
   PRInt32 i;
@@ -5422,17 +5422,17 @@ nsHTMLEditRules::GetPromotedRanges(nsISe
 
 
 ///////////////////////////////////////////////////////////////////////////
 // PromoteRange: expand a range to include any parents for which all
 //               editable children are already in range. 
 //                       
 nsresult 
 nsHTMLEditRules::PromoteRange(nsIDOMRange *inRange, 
-                              nsEditor::OperationID inOperationType)
+                              OperationID inOperationType)
 {
   NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
   nsresult res;
   nsCOMPtr<nsIDOMNode> startNode, endNode;
   PRInt32 startOffset, endOffset;
   
   res = inRange->GetStartContainer(getter_AddRefs(startNode));
   NS_ENSURE_SUCCESS(res, res);
@@ -5522,17 +5522,17 @@ private:
 
 ///////////////////////////////////////////////////////////////////////////
 // GetNodesForOperation: run through the ranges in the array and construct 
 //                       a new array of nodes to be acted on.
 //                       
 nsresult 
 nsHTMLEditRules::GetNodesForOperation(nsCOMArray<nsIDOMRange>& inArrayOfRanges, 
                                       nsCOMArray<nsIDOMNode>& outArrayOfNodes, 
-                                      nsEditor::OperationID inOperationType,
+                                      OperationID inOperationType,
                                       bool aDontTouchContent)
 {
   PRInt32 rangeCount = inArrayOfRanges.Count();
   
   PRInt32 i;
   nsCOMPtr<nsIDOMRange> opRange;
 
   nsresult res = NS_OK;
@@ -5605,50 +5605,50 @@ nsHTMLEditRules::GetNodesForOperation(ns
       NS_ENSURE_SUCCESS(res, res);
       if (!outArrayOfNodes.AppendObjects(nodes))
         return NS_ERROR_OUT_OF_MEMORY;
     }
   }    
 
   // certain operations should not act on li's and td's, but rather inside 
   // them.  alter the list as needed
-  if (inOperationType == nsEditor::kOpMakeBasicBlock) {
+  if (inOperationType == OperationID::makeBasicBlock) {
     PRInt32 listCount = outArrayOfNodes.Count();
     for (i=listCount-1; i>=0; i--)
     {
       nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
       if (nsHTMLEditUtils::IsListItem(node))
       {
         PRInt32 j=i;
         outArrayOfNodes.RemoveObjectAt(i);
         res = GetInnerContent(node, outArrayOfNodes, &j);
         NS_ENSURE_SUCCESS(res, res);
       }
     }
   }
   // indent/outdent already do something special for list items, but
   // we still need to make sure we don't act on table elements
-  else if (inOperationType == nsEditor::kOpOutdent ||
-           inOperationType == nsEditor::kOpIndent ||
-           inOperationType == nsEditor::kOpSetAbsolutePosition) {
+  else if (inOperationType == OperationID::outdent ||
+           inOperationType == OperationID::indent ||
+           inOperationType == OperationID::setAbsolutePosition) {
     PRInt32 listCount = outArrayOfNodes.Count();
     for (i=listCount-1; i>=0; i--)
     {
       nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
       if (nsHTMLEditUtils::IsTableElementButNotTable(node))
       {
         PRInt32 j=i;
         outArrayOfNodes.RemoveObjectAt(i);
         res = GetInnerContent(node, outArrayOfNodes, &j);
         NS_ENSURE_SUCCESS(res, res);
       }
     }
   }
   // outdent should look inside of divs.
-  if (inOperationType == nsEditor::kOpOutdent &&
+  if (inOperationType == OperationID::outdent &&
       !mHTMLEditor->IsCSSEnabled()) {
     PRInt32 listCount = outArrayOfNodes.Count();
     for (i=listCount-1; i>=0; i--)
     {
       nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
       if (nsHTMLEditUtils::IsDiv(node))
       {
         PRInt32 j=i;
@@ -5657,22 +5657,22 @@ nsHTMLEditRules::GetNodesForOperation(ns
         NS_ENSURE_SUCCESS(res, res);
       }
     }
   }
 
 
   // post process the list to break up inline containers that contain br's.
   // but only for operations that might care, like making lists or para's...
-  if (inOperationType == nsEditor::kOpMakeBasicBlock ||
-      inOperationType == nsEditor::kOpMakeList ||
-      inOperationType == nsEditor::kOpAlign ||
-      inOperationType == nsEditor::kOpSetAbsolutePosition ||
-      inOperationType == nsEditor::kOpIndent ||
-      inOperationType == nsEditor::kOpOutdent) {
+  if (inOperationType == OperationID::makeBasicBlock ||
+      inOperationType == OperationID::makeList ||
+      inOperationType == OperationID::align ||
+      inOperationType == OperationID::setAbsolutePosition ||
+      inOperationType == OperationID::indent ||
+      inOperationType == OperationID::outdent) {
     PRInt32 listCount = outArrayOfNodes.Count();
     for (i=listCount-1; i>=0; i--)
     {
       nsCOMPtr<nsIDOMNode> node = outArrayOfNodes[i];
       if (!aDontTouchContent && IsInlineNode(node) 
            && mHTMLEditor->IsContainer(node) && !mHTMLEditor->IsTextNode(node))
       {
         nsCOMArray<nsIDOMNode> arrayOfInlines;
@@ -5766,17 +5766,17 @@ nsHTMLEditRules::GetListActionNodes(nsCO
     if (outArrayOfNodes.Count()) return NS_OK;
   }
 
   {
     // We don't like other people messing with our selection!
     nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
 
     // contruct a list of nodes to act on.
-    res = GetNodesFromSelection(selection, nsEditor::kOpMakeList,
+    res = GetNodesFromSelection(selection, OperationID::makeList,
                                 outArrayOfNodes, aDontTouchContent);
     NS_ENSURE_SUCCESS(res, res);
   }
                
   // pre process our list of nodes...                      
   PRInt32 listCount = outArrayOfNodes.Count();
   PRInt32 i;
   for (i=listCount-1; i>=0; i--)
@@ -5896,17 +5896,17 @@ nsresult
 nsHTMLEditRules::GetParagraphFormatNodes(nsCOMArray<nsIDOMNode>& outArrayOfNodes,
                                          bool aDontTouchContent)
 {  
   nsCOMPtr<nsISelection>selection;
   nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
   NS_ENSURE_SUCCESS(res, res);
 
   // contruct a list of nodes to act on.
-  res = GetNodesFromSelection(selection, nsEditor::kOpMakeBasicBlock,
+  res = GetNodesFromSelection(selection, OperationID::makeBasicBlock,
                               outArrayOfNodes, aDontTouchContent);
   NS_ENSURE_SUCCESS(res, res);
 
   // pre process our list of nodes...                      
   PRInt32 listCount = outArrayOfNodes.Count();
   PRInt32 i;
   for (i=listCount-1; i>=0; i--)
   {
@@ -6069,17 +6069,17 @@ nsHTMLEditRules::GetHighestInlineParent(
 
 
 ///////////////////////////////////////////////////////////////////////////
 // GetNodesFromPoint: given a particular operation, construct a list  
 //                     of nodes from a point that will be operated on. 
 //                       
 nsresult 
 nsHTMLEditRules::GetNodesFromPoint(DOMPoint point,
-                                   nsEditor::OperationID operation,
+                                   OperationID operation,
                                    nsCOMArray<nsIDOMNode> &arrayOfNodes,
                                    bool dontTouchContent)
 {
   nsresult res;
 
   // get our point
   nsCOMPtr<nsIDOMNode> node;
   PRInt32 offset;
@@ -6110,17 +6110,17 @@ nsHTMLEditRules::GetNodesFromPoint(DOMPo
 
 
 ///////////////////////////////////////////////////////////////////////////
 // GetNodesFromSelection: given a particular operation, construct a list  
 //                     of nodes from the selection that will be operated on. 
 //                       
 nsresult 
 nsHTMLEditRules::GetNodesFromSelection(nsISelection *selection,
-                                       nsEditor::OperationID operation,
+                                       OperationID operation,
                                        nsCOMArray<nsIDOMNode>& arrayOfNodes,
                                        bool dontTouchContent)
 {
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   nsresult res;
   
   // promote selection ranges
   nsCOMArray<nsIDOMRange> arrayOfRanges;
@@ -8578,23 +8578,23 @@ nsHTMLEditRules::WillAbsolutePosition(Se
   
   // convert the selection ranges into "promoted" selection ranges:
   // this basically just expands the range to include the immediate
   // block parent, and then further expands to include any ancestors
   // whose children are all in the range
   
   nsCOMArray<nsIDOMRange> arrayOfRanges;
   res = GetPromotedRanges(aSelection, arrayOfRanges,
-                          nsEditor::kOpSetAbsolutePosition);
+                          OperationID::setAbsolutePosition);
   NS_ENSURE_SUCCESS(res, res);
   
   // use these ranges to contruct a list of nodes to act on.
   nsCOMArray<nsIDOMNode> arrayOfNodes;
   res = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
-                             nsEditor::kOpSetAbsolutePosition);
+                             OperationID::setAbsolutePosition);
   NS_ENSURE_SUCCESS(res, res);                                 
                                      
   NS_NAMED_LITERAL_STRING(divType, "div");
 
 
   // if nothing visible in list, make an empty block
   if (ListIsEmptyLine(arrayOfNodes))
   {
--- a/editor/libeditor/html/nsHTMLEditRules.h
+++ b/editor/libeditor/html/nsHTMLEditRules.h
@@ -72,19 +72,19 @@ public:
   
             nsHTMLEditRules();
   virtual   ~nsHTMLEditRules();
 
 
   // nsIEditRules methods
   NS_IMETHOD Init(nsPlaintextEditor *aEditor);
   NS_IMETHOD DetachEditor();
-  NS_IMETHOD BeforeEdit(nsEditor::OperationID action,
+  NS_IMETHOD BeforeEdit(OperationID action,
                         nsIEditor::EDirection aDirection);
-  NS_IMETHOD AfterEdit(nsEditor::OperationID action,
+  NS_IMETHOD AfterEdit(OperationID action,
                        nsIEditor::EDirection aDirection);
   NS_IMETHOD WillDoAction(mozilla::Selection* aSelection, nsRulesInfo* aInfo,
                           bool* aCancel, bool* aHandled);
   NS_IMETHOD DidDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, nsresult aResult);
   NS_IMETHOD DocumentModified();
 
   nsresult GetListState(bool *aMixed, bool *aOL, bool *aUL, bool *aDL);
   nsresult GetListItemState(bool *aMixed, bool *aLI, bool *aDT, bool *aDD);
@@ -123,17 +123,17 @@ protected:
   enum BRLocation
   {
     kBeforeBlock,
     kBlockEnd
   };
 
   // nsHTMLEditRules implementation methods
   nsresult WillInsert(nsISelection *aSelection, bool *aCancel);
-  nsresult WillInsertText(  nsEditor::OperationID aAction,
+  nsresult WillInsertText(  OperationID aAction,
                             mozilla::Selection* aSelection,
                             bool            *aCancel,
                             bool            *aHandled,
                             const nsAString *inString,
                             nsAString       *outString,
                             PRInt32          aMaxLength);
   nsresult WillLoadHTML(nsISelection *aSelection, bool *aCancel);
   nsresult WillInsertBreak(mozilla::Selection* aSelection,
@@ -203,17 +203,17 @@ protected:
   nsresult ReturnInHeader(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
   nsresult ReturnInParagraph(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset, bool *aCancel, bool *aHandled);
   nsresult SplitParagraph(nsIDOMNode *aPara,
                           nsIDOMNode *aBRNode, 
                           nsISelection *aSelection,
                           nsCOMPtr<nsIDOMNode> *aSelNode, 
                           PRInt32 *aOffset);
   nsresult ReturnInListItem(nsISelection *aSelection, nsIDOMNode *aHeader, nsIDOMNode *aTextNode, PRInt32 aOffset);
-  nsresult AfterEditInner(nsEditor::OperationID action,
+  nsresult AfterEditInner(OperationID action,
                           nsIEditor::EDirection aDirection);
   nsresult RemovePartOfBlock(nsIDOMNode *aBlock, 
                              nsIDOMNode *aStartChild, 
                              nsIDOMNode *aEndChild,
                              nsCOMPtr<nsIDOMNode> *aLeftNode = 0,
                              nsCOMPtr<nsIDOMNode> *aRightNode = 0);
   nsresult SplitBlock(nsIDOMNode *aBlock, 
                       nsIDOMNode *aStartChild, 
@@ -248,35 +248,35 @@ protected:
                               bool *aHandled);
   nsresult CheckForInvisibleBR(nsIDOMNode *aBlock, nsHTMLEditRules::BRLocation aWhere, 
                                nsCOMPtr<nsIDOMNode> *outBRNode, PRInt32 aOffset=0);
   nsresult ExpandSelectionForDeletion(nsISelection *aSelection);
   bool IsFirstNode(nsIDOMNode *aNode);
   bool IsLastNode(nsIDOMNode *aNode);
   nsresult NormalizeSelection(nsISelection *inSelection);
   void GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode* aNode,
-                        PRInt32 aOffset, nsEditor::OperationID actionID,
+                        PRInt32 aOffset, OperationID actionID,
                         nsCOMPtr<nsIDOMNode>* outNode, PRInt32* outOffset);
   nsresult GetPromotedRanges(nsISelection *inSelection, 
                              nsCOMArray<nsIDOMRange> &outArrayOfRanges, 
-                             nsEditor::OperationID inOperationType);
+                             OperationID inOperationType);
   nsresult PromoteRange(nsIDOMRange *inRange,
-                        nsEditor::OperationID inOperationType);
+                        OperationID inOperationType);
   nsresult GetNodesForOperation(nsCOMArray<nsIDOMRange>& inArrayOfRanges, 
                                 nsCOMArray<nsIDOMNode>& outArrayOfNodes, 
-                                nsEditor::OperationID inOperationType,
+                                OperationID inOperationType,
                                 bool aDontTouchContent=false);
   nsresult GetChildNodesForOperation(nsIDOMNode *inNode, 
                                      nsCOMArray<nsIDOMNode>& outArrayOfNodes);
   nsresult GetNodesFromPoint(DOMPoint point,
-                             nsEditor::OperationID operation,
+                             OperationID operation,
                              nsCOMArray<nsIDOMNode>& arrayOfNodes,
                              bool dontTouchContent);
   nsresult GetNodesFromSelection(nsISelection *selection,
-                                 nsEditor::OperationID operation,
+                                 OperationID operation,
                                  nsCOMArray<nsIDOMNode>& arrayOfNodes,
                                  bool aDontTouchContent=false);
   nsresult GetListActionNodes(nsCOMArray<nsIDOMNode> &outArrayOfNodes, bool aEntireList, bool aDontTouchContent=false);
   void GetDefinitionListItemTypes(mozilla::dom::Element* aElement, bool* aDT, bool* aDD);
   nsresult GetParagraphFormatNodes(nsCOMArray<nsIDOMNode>& outArrayOfNodes, bool aDontTouchContent=false);
   nsresult LookInsideDivBQandList(nsCOMArray<nsIDOMNode>& aNodeArray);
   nsresult BustUpInlinesAtRangeEndpoints(nsRangeStore &inRange);
   nsresult BustUpInlinesAtBRs(nsIDOMNode *inNode, 
--- a/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/editor/libeditor/html/nsHTMLEditor.cpp
@@ -1162,17 +1162,17 @@ nsHTMLEditor::CollapseSelectionToDeepest
 
 
 // This is mostly like InsertHTMLWithCharsetAndContext, 
 //  but we can't use that because it is selection-based and 
 //  the rules code won't let us edit under the <head> node
 NS_IMETHODIMP
 nsHTMLEditor::ReplaceHeadContentsWithHTML(const nsAString& aSourceToInsert)
 {
-  nsAutoRules beginRulesSniffing(this, kOpIgnore, nsIEditor::eNone); // don't do any post processing, rules get confused
+  nsAutoRules beginRulesSniffing(this, OperationID::ignore, nsIEditor::eNone); // don't do any post processing, rules get confused
   nsCOMPtr<nsISelection> selection;
   nsresult res = GetSelection(getter_AddRefs(selection));
   NS_ENSURE_SUCCESS(res, res);
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   ForceCompositionEnd();
 
   // Do not use nsAutoRules -- rules code won't let us insert in <head>
@@ -1484,26 +1484,26 @@ nsHTMLEditor::InsertElementAtSelection(n
   nsresult res = NS_ERROR_NOT_INITIALIZED;
   
   NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
   
   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
   
   ForceCompositionEnd();
   nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertElement, nsIEditor::eNext);
 
   nsRefPtr<Selection> selection = GetSelection();
   if (!selection) {
     return NS_ERROR_FAILURE;
   }
 
   // hand off to the rules system, see if it has anything to say about this
   bool cancel, handled;
-  nsTextRulesInfo ruleInfo(kOpInsertElement);
+  nsTextRulesInfo ruleInfo(OperationID::insertElement);
   ruleInfo.insertElement = aElement;
   res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || (NS_FAILED(res))) return res;
 
   if (!handled)
   {
     if (aDeleteSelection)
     {
@@ -1945,23 +1945,23 @@ nsHTMLEditor::MakeOrChangeList(const nsA
   if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
 
   // Protect the edit rules object from dying
   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
 
   bool cancel, handled;
 
   nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpMakeList, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::makeList, nsIEditor::eNext);
   
   // pre-process
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
-  nsTextRulesInfo ruleInfo(kOpMakeList);
+  nsTextRulesInfo ruleInfo(OperationID::makeList);
   ruleInfo.blockType = &aListType;
   ruleInfo.entireList = entireList;
   ruleInfo.bulletType = &aBulletType;
   res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || (NS_FAILED(res))) return res;
 
   if (!handled)
   {
@@ -2021,23 +2021,23 @@ nsHTMLEditor::RemoveList(const nsAString
   if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
 
   // Protect the edit rules object from dying
   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
 
   bool cancel, handled;
 
   nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpRemoveList, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::removeList, nsIEditor::eNext);
   
   // pre-process
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
-  nsTextRulesInfo ruleInfo(kOpRemoveList);
+  nsTextRulesInfo ruleInfo(OperationID::removeList);
   if (aListType.LowerCaseEqualsLiteral("ol"))
     ruleInfo.bOrdered = true;
   else  ruleInfo.bOrdered = false;
   res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || (NS_FAILED(res))) return res;
 
   // no default behavior for this yet.  what would it mean?
 
@@ -2052,22 +2052,22 @@ nsHTMLEditor::MakeDefinitionItem(const n
   if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
 
   // Protect the edit rules object from dying
   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
 
   bool cancel, handled;
 
   nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpMakeDefListItem, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::makeDefListItem, nsIEditor::eNext);
   
   // pre-process
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
-  nsTextRulesInfo ruleInfo(kOpMakeDefListItem);
+  nsTextRulesInfo ruleInfo(OperationID::makeDefListItem);
   ruleInfo.blockType = &aItemType;
   res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || (NS_FAILED(res))) return res;
 
   if (!handled)
   {
     // todo: no default for now.  we count on rules to handle it.
   }
@@ -2083,22 +2083,22 @@ nsHTMLEditor::InsertBasicBlock(const nsA
   if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
 
   // Protect the edit rules object from dying
   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
 
   bool cancel, handled;
 
   nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpMakeBasicBlock, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::makeBasicBlock, nsIEditor::eNext);
   
   // pre-process
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
-  nsTextRulesInfo ruleInfo(kOpMakeBasicBlock);
+  nsTextRulesInfo ruleInfo(OperationID::makeBasicBlock);
   ruleInfo.blockType = &aBlockType;
   res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || (NS_FAILED(res))) return res;
 
   if (!handled)
   {
     // Find out if the selection is collapsed:
     bool isCollapsed = selection->Collapsed();
@@ -2151,20 +2151,20 @@ nsHTMLEditor::Indent(const nsAString& aI
 {
   nsresult res;
   if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
 
   // Protect the edit rules object from dying
   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
 
   bool cancel, handled;
-  OperationID opID = kOpIndent;
+  OperationID opID = OperationID::indent;
   if (aIndent.LowerCaseEqualsLiteral("outdent"))
   {
-    opID = kOpOutdent;
+    opID = OperationID::outdent;
   }
   nsAutoEditBatch beginBatching(this);
   nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext);
   
   // pre-process
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
@@ -2231,25 +2231,25 @@ nsHTMLEditor::Indent(const nsAString& aI
 
 NS_IMETHODIMP
 nsHTMLEditor::Align(const nsAString& aAlignType)
 {
   // Protect the edit rules object from dying
   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
 
   nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpAlign, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::align, nsIEditor::eNext);
 
   nsCOMPtr<nsIDOMNode> node;
   bool cancel, handled;
   
   // Find out if the selection is collapsed:
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
-  nsTextRulesInfo ruleInfo(kOpAlign);
+  nsTextRulesInfo ruleInfo(OperationID::align);
   ruleInfo.alignType = &aAlignType;
   nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(res))
     return res;
   
   res = mRules->DidDoAction(selection, &ruleInfo, res);
   return res;
 }
@@ -4659,22 +4659,22 @@ nsHTMLEditor::SetCSSBackgroundColor(cons
   // Protect the edit rules object from dying
   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
 
   nsRefPtr<Selection> selection = GetSelection();
 
   bool isCollapsed = selection->Collapsed();
 
   nsAutoEditBatch batchIt(this);
-  nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertElement, nsIEditor::eNext);
   nsAutoSelectionReset selectionResetter(selection, this);
   nsAutoTxnsConserveSelection dontSpazMySelection(this);
   
   bool cancel, handled;
-  nsTextRulesInfo ruleInfo(kOpSetTextProperty);
+  nsTextRulesInfo ruleInfo(OperationID::setTextProperty);
   nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(res, res);
   if (!cancel && !handled)
   {
     // get selection range enumerator
     nsCOMPtr<nsIEnumerator> enumerator;
     res = selection->GetEnumerator(getter_AddRefs(enumerator));
     NS_ENSURE_SUCCESS(res, res);
--- a/editor/libeditor/html/nsHTMLEditorStyle.cpp
+++ b/editor/libeditor/html/nsHTMLEditorStyle.cpp
@@ -122,22 +122,22 @@ nsHTMLEditor::SetInlineProperty(nsIAtom 
   if (selection->Collapsed()) {
     // manipulating text attributes on a collapsed selection only sets state
     // for the next text insertion
     mTypeInState->SetProp(aProperty, aAttribute, aValue);
     return NS_OK;
   }
 
   nsAutoEditBatch batchIt(this);
-  nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertElement, nsIEditor::eNext);
   nsAutoSelectionReset selectionResetter(selection, this);
   nsAutoTxnsConserveSelection dontSpazMySelection(this);
 
   bool cancel, handled;
-  nsTextRulesInfo ruleInfo(kOpSetTextProperty);
+  nsTextRulesInfo ruleInfo(OperationID::setTextProperty);
   nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(res, res);
   if (!cancel && !handled) {
     // get selection range enumerator
     nsCOMPtr<nsIEnumerator> enumerator;
     res = selection->GetEnumerator(getter_AddRefs(enumerator));
     NS_ENSURE_SUCCESS(res, res);
     NS_ENSURE_TRUE(enumerator, NS_ERROR_FAILURE);
@@ -1327,17 +1327,17 @@ NS_IMETHODIMP nsHTMLEditor::GetInlinePro
     val = &aValue;
   return GetInlinePropertyBase( aProperty, att, val, aFirst, aAny, aAll, &outValue);
 }
 
 
 NS_IMETHODIMP nsHTMLEditor::RemoveAllInlineProperties()
 {
   nsAutoEditBatch batchIt(this);
-  nsAutoRules beginRulesSniffing(this, kOpResetTextProperties, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::resetTextProperties, nsIEditor::eNext);
 
   nsresult res = RemoveInlinePropertyImpl(nullptr, nullptr);
   NS_ENSURE_SUCCESS(res, res);
   return ApplyDefaultProperties();
 }
 
 NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsAString &aAttribute)
 {
@@ -1367,22 +1367,22 @@ nsresult nsHTMLEditor::RemoveInlinePrope
       mTypeInState->ClearProp(aProperty, *aAttribute);
     } else {
       mTypeInState->ClearAllProps();
     }
     return NS_OK;
   }
 
   nsAutoEditBatch batchIt(this);
-  nsAutoRules beginRulesSniffing(this, kOpRemoveTextProperty, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::removeTextProperty, nsIEditor::eNext);
   nsAutoSelectionReset selectionResetter(selection, this);
   nsAutoTxnsConserveSelection dontSpazMySelection(this);
   
   bool cancel, handled;
-  nsTextRulesInfo ruleInfo(kOpRemoveTextProperty);
+  nsTextRulesInfo ruleInfo(OperationID::removeTextProperty);
   res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(res, res);
   if (!cancel && !handled)
   {
     // get selection range enumerator
     nsCOMPtr<nsIEnumerator> enumerator;
     res = selection->GetEnumerator(getter_AddRefs(enumerator));
     NS_ENSURE_SUCCESS(res, res);
@@ -1561,17 +1561,17 @@ nsHTMLEditor::RelativeFontChange( PRInt3
 
     // manipulating text attributes on a collapsed selection only sets state for the next text insertion
     mTypeInState->SetProp(atom, EmptyString(), EmptyString());
     return NS_OK;
   }
   
   // wrap with txn batching, rules sniffing, and selection preservation code
   nsAutoEditBatch batchIt(this);
-  nsAutoRules beginRulesSniffing(this, kOpSetTextProperty, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::setTextProperty, nsIEditor::eNext);
   nsAutoSelectionReset selectionResetter(selection, this);
   nsAutoTxnsConserveSelection dontSpazMySelection(this);
 
   // get selection range enumerator
   nsCOMPtr<nsIEnumerator> enumerator;
   nsresult res = selection->GetEnumerator(getter_AddRefs(enumerator));
   NS_ENSURE_SUCCESS(res, res);
   NS_ENSURE_TRUE(enumerator, NS_ERROR_FAILURE);
--- a/editor/libeditor/html/nsTableEditor.cpp
+++ b/editor/libeditor/html/nsTableEditor.cpp
@@ -419,17 +419,17 @@ nsHTMLEditor::InsertTableColumn(PRInt32 
                       &curStartRowIndex, &curStartColIndex,
                       &rowSpan, &colSpan, 
                       &actualRowSpan, &actualColSpan, &isSelected);
   NS_ENSURE_SUCCESS(res, res);
   NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE);
 
   nsAutoEditBatch beginBatching(this);
   // Prevent auto insertion of BR in new cell until we're done
-  nsAutoRules beginRulesSniffing(this, kOpInsertNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertNode, nsIEditor::eNext);
 
   // Use column after current cell if requested
   if (aAfter)
   {
     startColIndex += actualColSpan;
     //Detect when user is adding after a COLSPAN=0 case
     // Assume they want to stop the "0" behavior and
     // really add a new column. Thus we set the 
@@ -556,17 +556,17 @@ nsHTMLEditor::InsertTableRow(PRInt32 aNu
   NS_ENSURE_TRUE(curCell, NS_ERROR_FAILURE);
   
   PRInt32 rowCount, colCount;
   res = GetTableSize(table, &rowCount, &colCount);
   NS_ENSURE_SUCCESS(res, res);
 
   nsAutoEditBatch beginBatching(this);
   // Prevent auto insertion of BR in new cell until we're done
-  nsAutoRules beginRulesSniffing(this, kOpInsertNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertNode, nsIEditor::eNext);
 
   if (aAfter)
   {
     // Use row after current cell
     startRowIndex += actualRowSpan;
 
     //Detect when user is adding after a ROWSPAN=0 case
     // Assume they want to stop the "0" behavior and
@@ -756,17 +756,17 @@ nsHTMLEditor::DeleteTableCell(PRInt32 aN
                          &startRowIndex, &startColIndex);
 
   NS_ENSURE_SUCCESS(res, res);
   // Don't fail if we didn't find a table or cell
   NS_ENSURE_TRUE(table && cell, NS_EDITOR_ELEMENT_NOT_FOUND);
 
   nsAutoEditBatch beginBatching(this);
   // Prevent rules testing until we're done
-  nsAutoRules beginRulesSniffing(this, kOpDeleteNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::deleteNode, nsIEditor::eNext);
 
   nsCOMPtr<nsIDOMElement> firstCell;
   nsCOMPtr<nsIDOMRange> range;
   res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
   NS_ENSURE_SUCCESS(res, res);
 
   PRInt32 rangeCount;
   res = selection->GetRangeCount(&rangeCount);
@@ -949,17 +949,17 @@ nsHTMLEditor::DeleteTableCellContents()
                        &startRowIndex, &startColIndex);
   NS_ENSURE_SUCCESS(res, res);
   // Don't fail if no cell found
   NS_ENSURE_TRUE(cell, NS_EDITOR_ELEMENT_NOT_FOUND);
 
 
   nsAutoEditBatch beginBatching(this);
   // Prevent rules testing until we're done
-  nsAutoRules beginRulesSniffing(this, kOpDeleteNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::deleteNode, nsIEditor::eNext);
   //Don't let Rules System change the selection
   nsAutoTxnsConserveSelection dontChangeSelection(this);
 
 
   nsCOMPtr<nsIDOMElement> firstCell;
   nsCOMPtr<nsIDOMRange> range;
   res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
   NS_ENSURE_SUCCESS(res, res);
@@ -990,17 +990,17 @@ nsHTMLEditor::DeleteTableCellContents()
 }
 
 NS_IMETHODIMP
 nsHTMLEditor::DeleteCellContents(nsIDOMElement *aCell)
 {
   NS_ENSURE_TRUE(aCell, NS_ERROR_NULL_POINTER);
 
   // Prevent rules testing until we're done
-  nsAutoRules beginRulesSniffing(this, kOpDeleteNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::deleteNode, nsIEditor::eNext);
 
   nsCOMPtr<nsIDOMNode> child;
   bool hasChild;
   aCell->HasChildNodes(&hasChild);
 
   while (hasChild)
   {
     aCell->GetLastChild(getter_AddRefs(child));
@@ -1034,17 +1034,17 @@ nsHTMLEditor::DeleteTableColumn(PRInt32 
   if(startColIndex == 0 && aNumber >= colCount)
     return DeleteTable2(table, selection);
 
   // Check for counts too high
   aNumber = NS_MIN(aNumber,(colCount-startColIndex));
 
   nsAutoEditBatch beginBatching(this);
   // Prevent rules testing until we're done
-  nsAutoRules beginRulesSniffing(this, kOpDeleteNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::deleteNode, nsIEditor::eNext);
 
   // Test if deletion is controlled by selected cells
   nsCOMPtr<nsIDOMElement> firstCell;
   nsCOMPtr<nsIDOMRange> range;
   res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
   NS_ENSURE_SUCCESS(res, res);
 
   PRInt32 rangeCount;
@@ -1210,17 +1210,17 @@ nsHTMLEditor::DeleteTableRow(PRInt32 aNu
   NS_ENSURE_SUCCESS(res, res);
 
   // Shortcut the case of deleting all rows in table
   if(startRowIndex == 0 && aNumber >= rowCount)
     return DeleteTable2(table, selection);
 
   nsAutoEditBatch beginBatching(this);
   // Prevent rules testing until we're done
-  nsAutoRules beginRulesSniffing(this, kOpDeleteNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::deleteNode, nsIEditor::eNext);
 
   nsCOMPtr<nsIDOMElement> firstCell;
   nsCOMPtr<nsIDOMRange> range;
   res = GetFirstSelectedCell(getter_AddRefs(range), getter_AddRefs(firstCell));
   NS_ENSURE_SUCCESS(res, res);
 
   PRInt32 rangeCount;
   res = selection->GetRangeCount(&rangeCount);
@@ -1297,17 +1297,17 @@ nsHTMLEditor::DeleteRow(nsIDOMElement *a
   nsCOMPtr<nsIDOMElement> cell;
   nsCOMPtr<nsIDOMElement> cellInDeleteRow;
   PRInt32 startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
   bool    isSelected;
   PRInt32 colIndex = 0;
   nsresult res = NS_OK;
    
   // Prevent rules testing until we're done
-  nsAutoRules beginRulesSniffing(this, kOpDeleteNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::deleteNode, nsIEditor::eNext);
 
   // The list of cells we will change rowspan in
   //  and the new rowspan values for each
   nsTArray<nsCOMPtr<nsIDOMElement> > spanCellList;
   nsTArray<PRInt32> newSpanList;
 
   // Scan through cells in row to do rowspan adjustments
   // Note that after we delete row, startRowIndex will point to the
@@ -1722,17 +1722,17 @@ nsHTMLEditor::SplitTableCell()
   NS_ENSURE_SUCCESS(res, res);
 
   // Must have some span to split
   if (actualRowSpan <= 1 && actualColSpan <= 1)
     return NS_OK;
   
   nsAutoEditBatch beginBatching(this);
   // Prevent auto insertion of BR in new cell until we're done
-  nsAutoRules beginRulesSniffing(this, kOpInsertNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertNode, nsIEditor::eNext);
 
   // We reset selection  
   nsSetSelectionAfterTableEdit setCaret(this, table, startRowIndex, startColIndex, ePreviousColumn, false);
   //...so suppress Rules System selection munging
   nsAutoTxnsConserveSelection dontChangeSelection(this);
 
   nsCOMPtr<nsIDOMElement> newCell;
   PRInt32 rowIndex = startRowIndex;
@@ -1940,17 +1940,17 @@ nsHTMLEditor::SplitCellIntoRows(nsIDOMEl
 
 NS_IMETHODIMP
 nsHTMLEditor::SwitchTableCellHeaderType(nsIDOMElement *aSourceCell, nsIDOMElement **aNewCell)
 {
   NS_ENSURE_TRUE(aSourceCell, NS_ERROR_NULL_POINTER);
 
   nsAutoEditBatch beginBatching(this);
   // Prevent auto insertion of BR in new cell created by ReplaceContainer
-  nsAutoRules beginRulesSniffing(this, kOpInsertNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertNode, nsIEditor::eNext);
 
   nsCOMPtr<nsIDOMNode> newNode;
 
   // Save current selection to restore when done
   // This is needed so ReplaceContainer can monitor selection
   //  when replacing nodes
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
@@ -2199,17 +2199,17 @@ nsHTMLEditor::JoinTableCells(bool aMerge
             NS_ENSURE_SUCCESS(res, res);
           }
         }
       }
     }
 
     // All cell contents are merged. Delete the empty cells we accumulated
     // Prevent rules testing until we're done
-    nsAutoRules beginRulesSniffing(this, kOpDeleteNode, nsIEditor::eNext);
+    nsAutoRules beginRulesSniffing(this, OperationID::deleteNode, nsIEditor::eNext);
 
     for (PRUint32 i = 0, n = deleteList.Length(); i < n; i++)
     {
       nsIDOMElement *elementPtr = deleteList[i];
       if (elementPtr)
       {
         nsCOMPtr<nsIDOMNode> node = do_QueryInterface(elementPtr);
         res = DeleteNode(node);
@@ -2325,17 +2325,17 @@ nsHTMLEditor::MergeCells(nsCOMPtr<nsIDOM
                          nsCOMPtr<nsIDOMElement> aCellToMerge,
                          bool aDeleteCellToMerge)
 {
   nsCOMPtr<dom::Element> targetCell = do_QueryInterface(aTargetCell);
   nsCOMPtr<dom::Element> cellToMerge = do_QueryInterface(aCellToMerge);
   NS_ENSURE_TRUE(targetCell && cellToMerge, NS_ERROR_NULL_POINTER);
 
   // Prevent rules testing until we're done
-  nsAutoRules beginRulesSniffing(this, kOpDeleteNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::deleteNode, nsIEditor::eNext);
 
   // Don't need to merge if cell is empty
   if (!IsEmptyCell(cellToMerge)) {
     // Get index of last child in target cell
     // If we fail or don't have children, 
     //  we insert at index 0
     PRInt32 insertIndex = 0;
 
@@ -2503,17 +2503,17 @@ nsHTMLEditor::NormalizeTable(nsIDOMEleme
   res = GetTableSize(table, &rowCount, &colCount);
   NS_ENSURE_SUCCESS(res, res);
 
   // Save current selection
   nsAutoSelectionReset selectionResetter(selection, this);
 
   nsAutoEditBatch beginBatching(this);
   // Prevent auto insertion of BR in new cell until we're done
-  nsAutoRules beginRulesSniffing(this, kOpInsertNode, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertNode, nsIEditor::eNext);
 
   nsCOMPtr<nsIDOMElement> cell;
   PRInt32 startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan;
   bool    isSelected;
 
   // Scan all cells in each row to detect bad rowspan values
   for(rowIndex = 0; rowIndex < rowCount; rowIndex++)
   {
--- a/editor/libeditor/text/nsPlaintextEditor.cpp
+++ b/editor/libeditor/text/nsPlaintextEditor.cpp
@@ -515,17 +515,17 @@ NS_IMETHODIMP nsPlaintextEditor::CreateB
 
 nsresult
 nsPlaintextEditor::InsertBR(nsCOMPtr<nsIDOMNode>* outBRNode)
 {
   NS_ENSURE_TRUE(outBRNode, NS_ERROR_NULL_POINTER);
   *outBRNode = nullptr;
 
   // calling it text insertion to trigger moz br treatment by rules
-  nsAutoRules beginRulesSniffing(this, kOpInsertText, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertText, nsIEditor::eNext);
 
   nsCOMPtr<nsISelection> selection;
   nsresult res = GetSelection(getter_AddRefs(selection));
   NS_ENSURE_SUCCESS(res, res);
 
   if (!selection->Collapsed()) {
     res = DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
     NS_ENSURE_SUCCESS(res, res);
@@ -635,17 +635,17 @@ nsPlaintextEditor::DeleteSelection(EDire
   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
 
   nsresult result;
 
   HandlingTrustedAction trusted(this, aAction != eNone);
 
   // delete placeholder txns merge.
   nsAutoPlaceHolderBatch batch(this, nsGkAtoms::DeleteTxnName);
-  nsAutoRules beginRulesSniffing(this, kOpDeleteSelection, aAction);
+  nsAutoRules beginRulesSniffing(this, OperationID::deleteSelection, aAction);
 
   // pre-process
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   // If there is an existing selection when an extended delete is requested,
   //  platforms that use "caret-style" caret positioning collapse the
   //  selection to the  start and then create a new selection.
@@ -661,17 +661,17 @@ nsPlaintextEditor::DeleteSelection(EDire
       NS_ENSURE_SUCCESS(result, result);
     }
     else
     { 
       aAction = eNone;
     }
   }
 
-  nsTextRulesInfo ruleInfo(kOpDeleteSelection);
+  nsTextRulesInfo ruleInfo(OperationID::deleteSelection);
   ruleInfo.collapsedAction = aAction;
   ruleInfo.stripWrappers = aStripWrappers;
   bool cancel, handled;
   result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(result, result);
   if (!cancel && !handled)
   {
     result = DeleteSelectionImpl(aAction, aStripWrappers);
@@ -687,20 +687,20 @@ nsPlaintextEditor::DeleteSelection(EDire
 
 NS_IMETHODIMP nsPlaintextEditor::InsertText(const nsAString &aStringToInsert)
 {
   if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
 
   // Protect the edit rules object from dying
   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
 
-  OperationID opID = kOpInsertText;
+  OperationID opID = OperationID::insertText;
   if (mInIMEMode) 
   {
-    opID = kOpInsertIMEText;
+    opID = OperationID::insertIMEText;
   }
   nsAutoPlaceHolderBatch batch(this, nullptr); 
   nsAutoRules beginRulesSniffing(this, opID, nsIEditor::eNext);
 
   // pre-process
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
   nsAutoString resultString;
@@ -730,29 +730,29 @@ NS_IMETHODIMP nsPlaintextEditor::InsertT
 NS_IMETHODIMP nsPlaintextEditor::InsertLineBreak()
 {
   if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
 
   // Protect the edit rules object from dying
   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
 
   nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpInsertBreak, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertBreak, nsIEditor::eNext);
 
   // pre-process
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   // Batching the selection and moving nodes out from under the caret causes
   // caret turds. Ask the shell to invalidate the caret now to avoid the turds.
   nsCOMPtr<nsIPresShell> shell = GetPresShell();
   NS_ENSURE_TRUE(shell, NS_ERROR_NOT_INITIALIZED);
   shell->MaybeInvalidateCaretPosition();
 
-  nsTextRulesInfo ruleInfo(kOpInsertBreak);
+  nsTextRulesInfo ruleInfo(OperationID::insertBreak);
   ruleInfo.maxLength = mMaxTextLength;
   bool cancel, handled;
   nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(res, res);
   if (!cancel && !handled)
   {
     // get the (collapsed) selection location
     nsCOMPtr<nsIDOMNode> selNode;
@@ -1077,19 +1077,19 @@ nsPlaintextEditor::Undo(PRUint32 aCount)
   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
 
   HandlingTrustedAction trusted(this);
 
   nsAutoUpdateViewBatch beginViewBatching(this);
 
   ForceCompositionEnd();
 
-  nsAutoRules beginRulesSniffing(this, kOpUndo, nsIEditor::eNone);
+  nsAutoRules beginRulesSniffing(this, OperationID::undo, nsIEditor::eNone);
 
-  nsTextRulesInfo ruleInfo(kOpUndo);
+  nsTextRulesInfo ruleInfo(OperationID::undo);
   nsRefPtr<Selection> selection = GetSelection();
   bool cancel, handled;
   nsresult result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   
   if (!cancel && NS_SUCCEEDED(result))
   {
     result = nsEditor::Undo(aCount);
     result = mRules->DidDoAction(selection, &ruleInfo, result);
@@ -1106,19 +1106,19 @@ nsPlaintextEditor::Redo(PRUint32 aCount)
   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
 
   HandlingTrustedAction trusted(this);
 
   nsAutoUpdateViewBatch beginViewBatching(this);
 
   ForceCompositionEnd();
 
-  nsAutoRules beginRulesSniffing(this, kOpRedo, nsIEditor::eNone);
+  nsAutoRules beginRulesSniffing(this, OperationID::redo, nsIEditor::eNone);
 
-  nsTextRulesInfo ruleInfo(kOpRedo);
+  nsTextRulesInfo ruleInfo(OperationID::redo);
   nsRefPtr<Selection> selection = GetSelection();
   bool cancel, handled;
   nsresult result = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   
   if (!cancel && NS_SUCCEEDED(result))
   {
     result = nsEditor::Redo(aCount);
     result = mRules->DidDoAction(selection, &ruleInfo, result);
@@ -1251,17 +1251,17 @@ NS_IMETHODIMP
 nsPlaintextEditor::OutputToString(const nsAString& aFormatType,
                                   PRUint32 aFlags,
                                   nsAString& aOutputString)
 {
   // Protect the edit rules object from dying
   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
 
   nsString resultString;
-  nsTextRulesInfo ruleInfo(kOpOutputText);
+  nsTextRulesInfo ruleInfo(OperationID::outputText);
   ruleInfo.outString = &resultString;
   // XXX Struct should store a nsAReadable*
   nsAutoString str(aFormatType);
   ruleInfo.outputFormat = &str;
   bool cancel, handled;
   nsresult rv = mRules->WillDoAction(nullptr, &ruleInfo, &cancel, &handled);
   if (cancel || NS_FAILED(rv)) { return rv; }
   if (handled)
@@ -1387,20 +1387,20 @@ nsPlaintextEditor::InsertAsQuotation(con
   if (!aQuotedText.IsEmpty() && (aQuotedText.Last() != PRUnichar('\n')))
     quotedStuff.Append(PRUnichar('\n'));
 
   // get selection
   nsRefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   nsAutoEditBatch beginBatching(this);
-  nsAutoRules beginRulesSniffing(this, kOpInsertText, nsIEditor::eNext);
+  nsAutoRules beginRulesSniffing(this, OperationID::insertText, nsIEditor::eNext);
 
   // give rules a chance to handle or cancel
-  nsTextRulesInfo ruleInfo(kOpInsertElement);
+  nsTextRulesInfo ruleInfo(OperationID::insertElement);
   bool cancel, handled;
   rv = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(rv, rv);
   if (cancel) return NS_OK; // rules canceled the operation
   if (!handled)
   {
     rv = InsertText(quotedStuff);
 
--- a/editor/libeditor/text/nsTextEditRules.cpp
+++ b/editor/libeditor/text/nsTextEditRules.cpp
@@ -58,17 +58,17 @@ using namespace mozilla;
 nsTextEditRules::nsTextEditRules()
 : mEditor(nullptr)
 , mPasswordText()
 , mPasswordIMEText()
 , mPasswordIMEIndex(0)
 , mActionNesting(0)
 , mLockRulesSniffing(false)
 , mDidExplicitlySetInterline(false)
-, mTheAction(nsEditor::kOpNone)
+, mTheAction(OperationID::none)
 , mLastStart(0)
 , mLastLength(0)
 {
 }
 
 nsTextEditRules::~nsTextEditRules()
 {
    // do NOT delete mEditor here.  We do not hold a ref count to mEditor.  mEditor owns our lifespan.
@@ -140,17 +140,17 @@ nsTextEditRules::DetachEditor()
   if (mTimer)
     mTimer->Cancel();
 
   mEditor = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsTextEditRules::BeforeEdit(nsEditor::OperationID action,
+nsTextEditRules::BeforeEdit(OperationID action,
                             nsIEditor::EDirection aDirection)
 {
   if (mLockRulesSniffing) return NS_OK;
   
   nsAutoLockRulesSniffing lockIt(this);
   mDidExplicitlySetInterline = false;
   if (!mActionNesting)
   {
@@ -167,17 +167,17 @@ nsTextEditRules::BeforeEdit(nsEditor::Op
   selection->GetAnchorNode(getter_AddRefs(mCachedSelectionNode));
   selection->GetAnchorOffset(&mCachedSelectionOffset);
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
-nsTextEditRules::AfterEdit(nsEditor::OperationID action,
+nsTextEditRules::AfterEdit(OperationID action,
                            nsIEditor::EDirection aDirection)
 {
   if (mLockRulesSniffing) return NS_OK;
   
   nsAutoLockRulesSniffing lockIt(this);
   
   NS_PRECONDITION(mActionNesting>0, "bad action nesting!");
   nsresult res = NS_OK;
@@ -223,37 +223,37 @@ nsTextEditRules::WillDoAction(Selection*
 
   *aCancel = false;
   *aHandled = false;
 
   // my kingdom for dynamic cast
   nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);
 
   switch (info->action) {
-    case nsEditor::kOpInsertBreak:
+    case OperationID::insertBreak:
       return WillInsertBreak(aSelection, aCancel, aHandled, info->maxLength);
-    case nsEditor::kOpInsertText:
-    case nsEditor::kOpInsertIMEText:
+    case OperationID::insertText:
+    case OperationID::insertIMEText:
       return WillInsertText(info->action, aSelection, aCancel, aHandled,
                             info->inString, info->outString, info->maxLength);
-    case nsEditor::kOpDeleteSelection:
+    case OperationID::deleteSelection:
       return WillDeleteSelection(aSelection, info->collapsedAction,
                                  aCancel, aHandled);
-    case nsEditor::kOpUndo:
+    case OperationID::undo:
       return WillUndo(aSelection, aCancel, aHandled);
-    case nsEditor::kOpRedo:
+    case OperationID::redo:
       return WillRedo(aSelection, aCancel, aHandled);
-    case nsEditor::kOpSetTextProperty:
+    case OperationID::setTextProperty:
       return WillSetTextProperty(aSelection, aCancel, aHandled);
-    case nsEditor::kOpRemoveTextProperty:
+    case OperationID::removeTextProperty:
       return WillRemoveTextProperty(aSelection, aCancel, aHandled);
-    case nsEditor::kOpOutputText:
+    case OperationID::outputText:
       return WillOutputText(aSelection, info->outputFormat, info->outString,
                             aCancel, aHandled);
-    case nsEditor::kOpInsertElement:
+    case OperationID::insertElement:
       // i had thought this would be html rules only.  but we put pre elements
       // into plaintext mail when doing quoting for reply!  doh!
       return WillInsert(aSelection, aCancel);
     default:
       return NS_ERROR_FAILURE;
   }
 }
 
@@ -267,32 +267,32 @@ nsTextEditRules::DidDoAction(nsISelectio
 
   NS_ENSURE_TRUE(aSelection && aInfo, NS_ERROR_NULL_POINTER);
     
   // my kingdom for dynamic cast
   nsTextRulesInfo *info = static_cast<nsTextRulesInfo*>(aInfo);
 
   switch (info->action)
   {
-    case nsEditor::kOpInsertBreak:
+    case OperationID::insertBreak:
       return DidInsertBreak(aSelection, aResult);
-    case nsEditor::kOpInsertText:
-    case nsEditor::kOpInsertIMEText:
+    case OperationID::insertText:
+    case OperationID::insertIMEText:
       return DidInsertText(aSelection, aResult);
-    case nsEditor::kOpDeleteSelection:
+    case OperationID::deleteSelection:
       return DidDeleteSelection(aSelection, info->collapsedAction, aResult);
-    case nsEditor::kOpUndo:
+    case OperationID::undo:
       return DidUndo(aSelection, aResult);
-    case nsEditor::kOpRedo:
+    case OperationID::redo:
       return DidRedo(aSelection, aResult);
-    case nsEditor::kOpSetTextProperty:
+    case OperationID::setTextProperty:
       return DidSetTextProperty(aSelection, aResult);
-    case nsEditor::kOpRemoveTextProperty:
+    case OperationID::removeTextProperty:
       return DidRemoveTextProperty(aSelection, aResult);
-    case nsEditor::kOpOutputText:
+    case OperationID::outputText:
       return DidOutputText(aSelection, aResult);
     default:
       // Don't fail on transactions we don't handle here!
       return NS_OK;
   }
 }
 
 
@@ -535,27 +535,27 @@ nsTextEditRules::HandleNewLines(nsString
   case nsIPlaintextEditor::eNewlinesPasteIntact:
     // even if we're pasting newlines, don't paste leading/trailing ones
     aString.Trim(CRLF, true, true);
     break;
   }
 }
 
 nsresult
-nsTextEditRules::WillInsertText(nsEditor::OperationID aAction,
+nsTextEditRules::WillInsertText(OperationID aAction,
                                 Selection* aSelection,
                                 bool            *aCancel,
                                 bool            *aHandled,
                                 const nsAString *inString,
                                 nsAString *outString,
                                 PRInt32          aMaxLength)
 {  
   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
 
-  if (inString->IsEmpty() && aAction != nsEditor::kOpInsertIMEText) {
+  if (inString->IsEmpty() && aAction != OperationID::insertIMEText) {
     // HACK: this is a fix for bug 19395
     // I can't outlaw all empty insertions
     // because IME transaction depend on them
     // There is more work to do to make the 
     // world safe for IME.
     *aCancel = true;
     *aHandled = false;
     return NS_OK;
@@ -569,17 +569,17 @@ nsTextEditRules::WillInsertText(nsEditor
   // NOTE, this function copies inString into outString for us.
   bool truncated = false;
   nsresult res = TruncateInsertionIfNeeded(aSelection, inString, outString,
                                            aMaxLength, &truncated);
   NS_ENSURE_SUCCESS(res, res);
   // If we're exceeding the maxlength when composing IME, we need to clean up
   // the composing text, so we shouldn't return early.
   if (truncated && outString->IsEmpty() &&
-      aAction != nsEditor::kOpInsertIMEText) {
+      aAction != OperationID::insertIMEText) {
     *aCancel = true;
     return NS_OK;
   }
   
   PRInt32 start = 0;
   PRInt32 end = 0;
 
   // handle password field docs
@@ -604,17 +604,17 @@ nsTextEditRules::WillInsertText(nsEditor
   // we want to ignore result of WillInsert()
   *aCancel = false;
   
   // handle password field data
   // this has the side effect of changing all the characters in aOutString
   // to the replacement character
   if (IsPasswordEditor())
   {
-    if (aAction == nsEditor::kOpInsertIMEText) {
+    if (aAction == OperationID::insertIMEText) {
       RemoveIMETextFromPWBuf(start, outString);
     }
   }
 
   // People have lots of different ideas about what text fields
   // should do with multiline pastes.  See bugs 21032, 23485, 23485, 50935.
   // The six possible options are:
   // 0. paste newlines intact
@@ -671,21 +671,21 @@ nsTextEditRules::WillInsertText(nsEditor
       !mEditor->CanContainTag(selNode, nsGkAtoms::textTagName)) {
     return NS_ERROR_FAILURE;
   }
 
   // we need to get the doc
   nsCOMPtr<nsIDOMDocument> doc = mEditor->GetDOMDocument();
   NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
     
-  if (aAction == nsEditor::kOpInsertIMEText) {
+  if (aAction == OperationID::insertIMEText) {
     res = mEditor->InsertTextImpl(*outString, address_of(selNode), &selOffset, doc);
     NS_ENSURE_SUCCESS(res, res);
   } else {
-    // aAction == nsEditor::kOpInsertText; find where we are
+    // aAction == OperationID::insertText; find where we are
     nsCOMPtr<nsIDOMNode> curNode = selNode;
     PRInt32 curOffset = selOffset;
 
     // don't spaz my selection in subtransactions
     nsAutoTxnsConserveSelection dontSpazMySelection(mEditor);
 
     res = mEditor->InsertTextImpl(*outString, address_of(curNode),
                                   &curOffset, doc);
@@ -1082,17 +1082,17 @@ nsTextEditRules::CreateBogusNodeIfNeeded
   NS_ENSURE_TRUE(mEditor, NS_ERROR_NULL_POINTER);
 
   if (mBogusNode) {
     // Let's not create more than one, ok?
     return NS_OK;
   }
 
   // tell rules system to not do any post-processing
-  nsAutoRules beginRulesSniffing(mEditor, nsEditor::kOpIgnore, nsIEditor::eNone);
+  nsAutoRules beginRulesSniffing(mEditor, OperationID::ignore, nsIEditor::eNone);
 
   nsCOMPtr<dom::Element> body = mEditor->GetRoot();
   if (!body) {
     // We don't even have a body yet, don't insert any bogus nodes at
     // this point.
     return NS_OK;
   }
 
--- a/editor/libeditor/text/nsTextEditRules.h
+++ b/editor/libeditor/text/nsTextEditRules.h
@@ -44,19 +44,19 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTextEditRules, nsIEditRules)
   
               nsTextEditRules();
   virtual     ~nsTextEditRules();
 
   // nsIEditRules methods
   NS_IMETHOD Init(nsPlaintextEditor *aEditor);
   NS_IMETHOD DetachEditor();
-  NS_IMETHOD BeforeEdit(nsEditor::OperationID action,
+  NS_IMETHOD BeforeEdit(OperationID action,
                         nsIEditor::EDirection aDirection);
-  NS_IMETHOD AfterEdit(nsEditor::OperationID action,
+  NS_IMETHOD AfterEdit(OperationID action,
                        nsIEditor::EDirection aDirection);
   NS_IMETHOD WillDoAction(mozilla::Selection* aSelection, nsRulesInfo* aInfo,
                           bool* aCancel, bool* aHandled);
   NS_IMETHOD DidDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, nsresult aResult);
   NS_IMETHOD DocumentIsEmpty(bool *aDocumentIsEmpty);
   NS_IMETHOD DocumentModified();
 
 public:
@@ -96,17 +96,17 @@ public:
    * @param aLength the number of password characters that aOutString should
    *        contain.
    */
   static void FillBufWithPWChars(nsAString *aOutString, PRInt32 aLength);
 
 protected:
 
   // nsTextEditRules implementation methods
-  nsresult WillInsertText(  nsEditor::OperationID aAction,
+  nsresult WillInsertText(  OperationID aAction,
                             mozilla::Selection* aSelection,
                             bool            *aCancel,
                             bool            *aHandled,
                             const nsAString *inString,
                             nsAString       *outString,
                             PRInt32          aMaxLength);
   nsresult DidInsertText(nsISelection *aSelection, nsresult aResult);
   nsresult GetTopEnclosingPre(nsIDOMNode *aNode, nsIDOMNode** aOutPreNode);
@@ -228,32 +228,32 @@ protected:
   PRInt32              mCachedSelectionOffset;  // cached selected offset
   PRUint32             mActionNesting;
   bool                 mLockRulesSniffing;
   bool                 mDidExplicitlySetInterline;
   bool                 mDeleteBidiImmediately; // in bidirectional text, delete
                                                // characters not visually 
                                                // adjacent to the caret without
                                                // moving the caret first.
-  nsEditor::OperationID mTheAction;     // the top level editor action
+  OperationID mTheAction;     // the top level editor action
   nsCOMPtr<nsITimer>   mTimer;
   PRUint32             mLastStart, mLastLength;
 
   // friends
   friend class nsAutoLockRulesSniffing;
 
 };
 
 
 
 class nsTextRulesInfo : public nsRulesInfo
 {
  public:
  
-  nsTextRulesInfo(nsEditor::OperationID aAction) :
+  nsTextRulesInfo(OperationID aAction) :
     nsRulesInfo(aAction),
     inString(0),
     outString(0),
     outputFormat(0),
     maxLength(-1),
     collapsedAction(nsIEditor::eNext),
     stripWrappers(nsIEditor::eStrip),
     bOrdered(false),
--- a/extensions/spellcheck/hunspell/src/Makefile.in
+++ b/extensions/spellcheck/hunspell/src/Makefile.in
@@ -40,8 +40,12 @@ endif
 
 include $(topsrcdir)/config/rules.mk
 
 INCLUDES        += -I$(topsrcdir)/extensions/spellcheck/src
 
 ifdef MOZ_NATIVE_HUNSPELL
 CXXFLAGS += $(MOZ_HUNSPELL_CFLAGS)
 endif
+
+LOCAL_INCLUDES += \
+	-I$(topsrcdir)/editor/libeditor/base \
+	$(NULL)
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -98,34 +98,34 @@ mozInlineSpellStatus::mozInlineSpellStat
 //
 //    This is the most complicated case. For changes, we need to compute the
 //    range of stuff that changed based on the old and new caret positions,
 //    as well as use a range possibly provided by the editor (start and end,
 //    which are usually NULL) to get a range with the union of these.
 
 nsresult
 mozInlineSpellStatus::InitForEditorChange(
-    PRInt32 aAction,
+    OperationID aAction,
     nsIDOMNode* aAnchorNode, PRInt32 aAnchorOffset,
     nsIDOMNode* aPreviousNode, PRInt32 aPreviousOffset,
     nsIDOMNode* aStartNode, PRInt32 aStartOffset,
     nsIDOMNode* aEndNode, PRInt32 aEndOffset)
 {
   nsresult rv;
 
   nsCOMPtr<nsIDOMDocument> doc;
   rv = GetDocument(getter_AddRefs(doc));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // save the anchor point as a range so we can find the current word later
   rv = PositionToCollapsedRange(doc, aAnchorNode, aAnchorOffset,
                                 getter_AddRefs(mAnchorRange));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (aAction == nsEditor::kOpDeleteSelection) {
+  if (aAction == OperationID::deleteSelection) {
     // Deletes are easy, the range is just the current anchor. We set the range
     // to check to be empty, FinishInitOnEvent will fill in the range to be
     // the current word.
     mOp = eOpChangeDelete;
     mRange = nullptr;
     return NS_OK;
   }
 
@@ -148,17 +148,17 @@ mozInlineSpellStatus::InitForEditorChang
     rv = mRange->SetStart(aAnchorNode, aAnchorOffset);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mRange->SetEnd(aPreviousNode, aPreviousOffset);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   // On insert save this range: DoSpellCheck optimizes things in this range.
   // Otherwise, just leave this NULL.
-  if (aAction == nsEditor::kOpInsertText)
+  if (aAction == OperationID::insertText)
     mCreatedRange = mRange;
 
   // if we were given a range, we need to expand our range to encompass it
   if (aStartNode && aEndNode) {
     rv = mRange->ComparePoint(aStartNode, aStartOffset, &cmpResult);
     NS_ENSURE_SUCCESS(rv, rv);
     if (cmpResult < 0) { // given range starts before
       rv = mRange->SetStart(aStartNode, aStartOffset);
@@ -726,17 +726,17 @@ mozInlineSpellChecker::SpellCheckAfterEd
   nsCOMPtr<nsIDOMNode> anchorNode;
   rv = aSelection->GetAnchorNode(getter_AddRefs(anchorNode));
   NS_ENSURE_SUCCESS(rv, rv);
   PRInt32 anchorOffset;
   rv = aSelection->GetAnchorOffset(&anchorOffset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mozInlineSpellStatus status(this);
-  rv = status.InitForEditorChange(aAction,
+  rv = status.InitForEditorChange((OperationID)aAction,
                                   anchorNode, anchorOffset,
                                   aPreviousSelectedNode, aPreviousSelectedOffset,
                                   aStartNode, aStartOffset,
                                   aEndNode, aEndOffset);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = ScheduleSpellCheck(status);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/extensions/spellcheck/src/mozInlineSpellChecker.h
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.h
@@ -9,17 +9,17 @@
 #include "nsAutoPtr.h"
 #include "nsRange.h"
 #include "nsIEditorSpellCheck.h"
 #include "nsIEditActionListener.h"
 #include "nsIInlineSpellChecker.h"
 #include "nsITextServicesDocument.h"
 #include "nsIDOMTreeWalker.h"
 #include "nsWeakReference.h"
-#include "nsIEditor.h"
+#include "nsEditor.h"
 #include "nsIDOMEventListener.h"
 #include "nsWeakReference.h"
 #include "mozISpellI18NUtil.h"
 #include "nsCycleCollectionParticipant.h"
 
 // X.h defines KeyPress
 #ifdef KeyPress
 #undef KeyPress
@@ -30,17 +30,17 @@ class mozInlineSpellWordUtil;
 class mozInlineSpellChecker;
 class mozInlineSpellResume;
 
 class mozInlineSpellStatus
 {
 public:
   mozInlineSpellStatus(mozInlineSpellChecker* aSpellChecker);
 
-  nsresult InitForEditorChange(PRInt32 aAction,
+  nsresult InitForEditorChange(OperationID aAction,
                                nsIDOMNode* aAnchorNode, PRInt32 aAnchorOffset,
                                nsIDOMNode* aPreviousNode, PRInt32 aPreviousOffset,
                                nsIDOMNode* aStartNode, PRInt32 aStartOffset,
                                nsIDOMNode* aEndNode, PRInt32 aEndOffset);
   nsresult InitForNavigation(bool aForceCheck, PRInt32 aNewPositionOffset,
                              nsIDOMNode* aOldAnchorNode, PRInt32 aOldAnchorOffset,
                              nsIDOMNode* aNewAnchorNode, PRInt32 aNewAnchorOffset,
                              bool* aContinue);
@@ -57,18 +57,18 @@ public:
   nsRefPtr<mozInlineSpellChecker> mSpellChecker;
 
   // The total number of words checked in this sequence, using this tally tells
   // us when to stop. This count is preserved as we continue checking in new
   // messages.
   PRInt32 mWordCount;
 
   // what happened?
-  enum Operation { eOpChange,       // for SpellCheckAfterChange except kOpDeleteSelection
-                   eOpChangeDelete, // for SpellCheckAfterChange kOpDeleteSelection
+  enum Operation { eOpChange,       // for SpellCheckAfterChange except deleteSelection
+                   eOpChangeDelete, // for SpellCheckAfterChange deleteSelection
                    eOpNavigation,   // for HandleNavigationEvent
                    eOpSelection,    // re-check all misspelled words
                    eOpResume };     // for resuming a previously started check
   Operation mOp;
 
   // Used for events where we have already computed the range to use. It can
   // also be NULL in these cases where we need to check the entire range.
   nsRefPtr<nsRange> mRange;
--- a/layout/reftests/svg/pattern-scale-01-ref.svg
+++ b/layout/reftests/svg/pattern-scale-01-ref.svg
@@ -4,10 +4,10 @@
 -->
 <svg xmlns="http://www.w3.org/2000/svg">
   <defs>
     <pattern id="pattern" width="200" height="200" patternUnits="userSpaceOnUse">
       <circle cx="100" cy="100" r="100" fill="lime" />
     </pattern>
   </defs>
   <rect width="200" height="200" fill="url(#pattern)" />
-  <circle cx="100" cy="100" r="100" fill="none" stroke="lime" stroke-width="2" shape-rendering="crispEdges" />
+  <circle cx="100" cy="100" r="100" fill="none" stroke="lime" stroke-width="3.5" shape-rendering="crispEdges" />
 </svg>
--- a/layout/reftests/svg/pattern-scale-01a.svg
+++ b/layout/reftests/svg/pattern-scale-01a.svg
@@ -4,10 +4,10 @@
 -->
 <svg xmlns="http://www.w3.org/2000/svg">
   <defs>
     <pattern id="pattern" width="5" height="5" patternUnits="userSpaceOnUse" patternTransform="scale(-40)">
       <circle cx="2.5" cy="2.5" r="2.5" fill="lime" />
     </pattern>
   </defs>
   <rect width="200" height="200" fill="url(#pattern)" />
-  <circle cx="100" cy="100" r="100" fill="none" stroke="lime" stroke-width="2" shape-rendering="crispEdges" />
+  <circle cx="100" cy="100" r="100" fill="none" stroke="lime" stroke-width="3.5" shape-rendering="crispEdges" />
 </svg>
--- a/layout/reftests/svg/pattern-scale-01b.svg
+++ b/layout/reftests/svg/pattern-scale-01b.svg
@@ -6,10 +6,10 @@
   <defs>
     <pattern id="pattern" width="1" height="1" patternUnits="userSpaceOnUse">
       <circle cx="0.5" cy="0.5" r="0.5" fill="lime" />
     </pattern>
   </defs>
   <g transform="scale(200)">
     <rect width="1" height="1" fill="url(#pattern)" />
   </g>
-  <circle cx="100" cy="100" r="100" fill="none" stroke="lime" stroke-width="2" shape-rendering="crispEdges" />
+  <circle cx="100" cy="100" r="100" fill="none" stroke="lime" stroke-width="3.5" shape-rendering="crispEdges" />
 </svg>
copy from layout/reftests/svg/pattern-scale-01a.svg
copy to layout/reftests/svg/pattern-scale-01c.svg
--- a/layout/reftests/svg/pattern-scale-01a.svg
+++ b/layout/reftests/svg/pattern-scale-01c.svg
@@ -1,13 +1,13 @@
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <svg xmlns="http://www.w3.org/2000/svg">
   <defs>
-    <pattern id="pattern" width="5" height="5" patternUnits="userSpaceOnUse" patternTransform="scale(-40)">
+    <pattern id="pattern" width="5" height="5" patternUnits="userSpaceOnUse" patternTransform="rotate(90) scale(-40)">
       <circle cx="2.5" cy="2.5" r="2.5" fill="lime" />
     </pattern>
   </defs>
   <rect width="200" height="200" fill="url(#pattern)" />
-  <circle cx="100" cy="100" r="100" fill="none" stroke="lime" stroke-width="2" shape-rendering="crispEdges" />
+  <circle cx="100" cy="100" r="100" fill="none" stroke="lime" stroke-width="3.5" shape-rendering="crispEdges" />
 </svg>
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -203,16 +203,17 @@ random-if(gtk2Widget) == objectBoundingB
 == pathLength-01.svg pass.svg
 == pathLength-02.svg pass.svg
 == pattern-invalid-01.svg pattern-invalid-01-ref.svg
 == pattern-live-01a.svg pattern-live-01-ref.svg
 == pattern-live-01b.svg pattern-live-01-ref.svg
 == pattern-live-01c.svg pattern-live-01-ref.svg
 == pattern-scale-01a.svg pattern-scale-01-ref.svg
 == pattern-scale-01b.svg pattern-scale-01-ref.svg
+== pattern-scale-01c.svg pattern-scale-01-ref.svg
 == pattern-transform-presence-01.svg pattern-transform-presence-01-ref.svg
 == pattern-transformed-01.svg pattern-transformed-01-ref.svg
 == polygon-marker-01.svg pass.svg
 == polygon-points-negative-01.svg pass.svg
 == polyline-points-invalid-01.svg pass.svg
 == pseudo-classes-01.svg pass.svg
 # This test depends on :visited styles (which are asynchronous), so we run
 # it in layout/style/test/test_visited_reftests.html instead of using the
--- a/layout/svg/base/src/nsSVGPatternFrame.cpp
+++ b/layout/svg/base/src/nsSVGPatternFrame.cpp
@@ -305,33 +305,36 @@ nsSVGPatternFrame::PaintPattern(gfxASurf
   if (aFillOrStroke == &nsStyleSVG::mStroke) {
     patternTransform.Multiply(nsSVGUtils::GetStrokeTransform(aSource).Invert());
   }
 
   // Get the transformation matrix that we will hand to the renderer's pattern
   // routine.
   *patternMatrix = GetPatternMatrix(patternUnits, patternTransform,
                                     bbox, callerBBox, aContextMatrix);
+  if (patternMatrix->IsSingular()) {
+    return NS_ERROR_FAILURE;
+  }
 
   // Now that we have all of the necessary geometries, we can
   // create our surface.
-  gfxFloat patternWidth = bbox.Width();
-  gfxFloat patternHeight = bbox.Height();
+  gfxRect transformedBBox = patternTransform.TransformBounds(bbox);
 
   bool resultOverflows;
   gfxIntSize surfaceSize =
     nsSVGUtils::ConvertToSurfaceSize(
-      gfxSize(patternWidth * fabs(patternTransform.xx),
-              patternHeight * fabs(patternTransform.yy)),
-      &resultOverflows);
+      transformedBBox.Size(), &resultOverflows);
 
   // 0 disables rendering, < 0 is an error
   if (surfaceSize.width <= 0 || surfaceSize.height <= 0)
     return NS_ERROR_FAILURE;
 
+  gfxFloat patternWidth = bbox.Width();
+  gfxFloat patternHeight = bbox.Height();
+
   if (resultOverflows ||
       patternWidth != surfaceSize.width ||
       patternHeight != surfaceSize.height) {
     // scale drawing to pattern surface size
     gfxMatrix tempTM =
       gfxMatrix(surfaceSize.width / patternWidth, 0.0f,
                 0.0f, surfaceSize.height / patternHeight,
                 0.0f, 0.0f);
@@ -688,20 +691,16 @@ nsSVGPatternFrame::GetPaintServerPattern
   gfxMatrix pMatrix;
   nsresult rv = PaintPattern(getter_AddRefs(surface), &pMatrix, aContextMatrix,
                              aSource, aFillOrStroke, aGraphicOpacity, aOverrideBounds);
 
   if (NS_FAILED(rv)) {
     return nullptr;
   }
 
-  if (pMatrix.IsSingular()) {
-    return nullptr;
-  }
-
   pMatrix.Invert();
 
   nsRefPtr<gfxPattern> pattern = new gfxPattern(surface);
 
   if (!pattern || pattern->CairoStatus())
     return nullptr;
 
   pattern->SetMatrix(pMatrix);
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -65,16 +65,20 @@
 #  endif
 #  if __has_extension(cxx_deleted_functions)
 #    define MOZ_HAVE_CXX11_DELETE
 #  endif
 #  if __has_extension(cxx_override_control)
 #    define MOZ_HAVE_CXX11_OVERRIDE
 #    define MOZ_HAVE_CXX11_FINAL         final
 #  endif
+#  if __has_extension(cxx_strong_enums)
+#    define MOZ_HAVE_CXX11_ENUM_TYPE
+#    define MOZ_HAVE_CXX11_STRONG_ENUMS
+#  endif
 #  if __has_attribute(noinline)
 #    define MOZ_HAVE_NEVER_INLINE        __attribute__((noinline))
 #  endif
 #  if __has_attribute(noreturn)
 #    define MOZ_HAVE_NORETURN            __attribute__((noreturn))
 #  endif
 #elif defined(__GNUC__)
 #  if defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L
@@ -84,16 +88,18 @@
 #      define MOZ_HAVE_CXX11_FINAL       final
 #    elif __GNUC__ == 4
 #      if __GNUC_MINOR__ >= 7
 #        define MOZ_HAVE_CXX11_OVERRIDE
 #        define MOZ_HAVE_CXX11_FINAL     final
 #      endif
 #      if __GNUC_MINOR__ >= 4
 #        define MOZ_HAVE_CXX11_DELETE
+#        define MOZ_HAVE_CXX11_ENUM_TYPE
+#        define MOZ_HAVE_CXX11_STRONG_ENUMS
 #      endif
 #    endif
 #  else
      /* __final is a non-C++11 GCC synonym for 'final', per GCC r176655. */
 #    if __GNUC__ > 4
 #      define MOZ_HAVE_CXX11_FINAL       __final
 #    elif __GNUC__ == 4
 #      if __GNUC_MINOR__ >= 7
@@ -103,16 +109,20 @@
 #  endif
 #  define MOZ_HAVE_NEVER_INLINE          __attribute__((noinline))
 #  define MOZ_HAVE_NORETURN              __attribute__((noreturn))
 #elif defined(_MSC_VER)
 #  if _MSC_VER >= 1400
 #    define MOZ_HAVE_CXX11_OVERRIDE
      /* MSVC currently spells "final" as "sealed". */
 #    define MOZ_HAVE_CXX11_FINAL         sealed
+#    define MOZ_HAVE_CXX11_ENUM_TYPE
+#  endif
+#  if _MSC_VER >= 1700
+#    define MOZ_HAVE_CXX11_STRONG_ENUMS
 #  endif
 #  define MOZ_HAVE_NEVER_INLINE          __declspec(noinline)
 #  define MOZ_HAVE_NORETURN              __declspec(noreturn)
 #endif
 
 /*
  * MOZ_NEVER_INLINE is a macro which expands to tell the compiler that the
  * method decorated with it must never be inlined, even if the compiler would
@@ -294,16 +304,174 @@
  */
 #if defined(MOZ_HAVE_CXX11_FINAL)
 #  define MOZ_FINAL             MOZ_HAVE_CXX11_FINAL
 #else
 #  define MOZ_FINAL             /* no support */
 #endif
 
 /**
+ * MOZ_ENUM_TYPE specifies the underlying numeric type for an enum.  It's
+ * specified by placing MOZ_ENUM_TYPE(type) immediately after the enum name in
+ * its declaration, and before the opening curly brace, like
+ *
+ *   enum MyEnum MOZ_ENUM_TYPE(PRUint16)
+ *   {
+ *     A,
+ *     B = 7,
+ *     C
+ *   };
+ *
+ * In supporting compilers, the macro will expand to ": PRUint16".  The
+ * compiler will allocate exactly two bytes for MyEnum, and will require all
+ * enumerators to have values between 0 and 65535.  (Thus specifying "B =
+ * 100000" instead of "B = 7" would fail to compile.)  In old compilers, the
+ * macro expands to the empty string, and the underlying type is generally
+ * undefined.
+ */
+#ifdef MOZ_HAVE_CXX11_ENUM_TYPE
+#  define MOZ_ENUM_TYPE(type)   : type
+#else
+#  define MOZ_ENUM_TYPE(type)   /* no support */
+#endif
+
+/**
+ * MOZ_BEGIN_ENUM_CLASS and MOZ_END_ENUM_CLASS provide access to the
+ * strongly-typed enumeration feature of C++11 ("enum class").  If supported
+ * by the compiler, an enum defined using these macros will not be implicitly
+ * converted to any other type, and its enumerators will be scoped using the
+ * enumeration name.  Place MOZ_BEGIN_ENUM_CLASS(EnumName, type) in place of
+ * "enum EnumName {", and MOZ_END_ENUM_CLASS(EnumName) in place of the closing
+ * "};".  For example,
+ *
+ *   MOZ_BEGIN_ENUM_CLASS(Enum, PRInt32)
+ *     A, B = 6
+ *   MOZ_END_ENUM_CLASS(Enum)
+ *
+ * This will make "Enum::A" and "Enum::B" appear in the global scope, but "A"
+ * and "B" will not.  In compilers that support C++11 strongly-typed
+ * enumerations, implicit conversions of Enum values to numeric types will
+ * fail.  In other compilers, Enum itself will actually be defined as a class,
+ * and some implicit conversions will fail while others will succeed.
+ *
+ * The type argument specifies the underlying type for the enum where
+ * supported, as with MOZ_ENUM_TYPE().  For simplicity, it is currently
+ * mandatory.  As with MOZ_ENUM_TYPE(), it will do nothing on compilers that do
+ * not support it.
+ */
+#if defined(MOZ_HAVE_CXX11_STRONG_ENUMS)
+  /* All compilers that support strong enums also support an explicit
+   * underlying type, so no extra check is needed */
+#  define MOZ_BEGIN_ENUM_CLASS(Name, type) enum class Name : type {
+#  define MOZ_END_ENUM_CLASS(Name)         };
+#else
+   /**
+    * We need Name to both name a type, and scope the provided enumerator
+    * names.  Namespaces and classes both provide scoping, but namespaces
+    * aren't types, so we need to use a class that wraps the enum values.  We
+    * have an implicit conversion from the inner enum type to the class, so
+    * statements like
+    *
+    *   Enum x = Enum::A;
+    *
+    * will still work.  We need to define an implicit conversion from the class
+    * to the inner enum as well, so that (for instance) switch statements will
+    * work.  This means that the class can be implicitly converted to a numeric
+    * value as well via the enum type, since C++ allows an implicit
+    * user-defined conversion followed by a standard conversion to still be
+    * implicit.
+    *
+    * We have an explicit constructor from int defined, so that casts like
+    * (Enum)7 will still work.
+    *
+    * Additionally, we'll delete as many operators as possible for the inner
+    * enum type, so statements like this will still fail:
+    *
+    *   f(5 + Enum::B); // deleted operator+
+    *
+    * But we can't prevent things like this, because C++ doesn't allow
+    * overriding conversions or assignment operators for enums:
+    *
+    *   int x = Enum::A;
+    *   int f()
+    *   {
+    *     return Enum::A;
+    *   }
+    */
+#  define MOZ_BEGIN_ENUM_CLASS(Name, type) \
+     class Name \
+     { \
+       public: \
+         enum Enum MOZ_ENUM_TYPE(type) \
+         {
+#  define MOZ_END_ENUM_CLASS(Name) \
+         }; \
+         Name(Enum aEnum) : mEnum(aEnum) {} \
+         explicit Name(int num) : mEnum((Enum)num) {} \
+         operator Enum() const { return mEnum; } \
+       private: \
+         Enum mEnum; \
+     }; \
+     inline int operator+(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline int operator+(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline int operator-(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline int operator-(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline int operator*(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline int operator*(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline int operator/(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline int operator/(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline int operator%(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline int operator%(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline int operator+(const Name::Enum&) MOZ_DELETE; \
+     inline int operator-(const Name::Enum&) MOZ_DELETE; \
+     inline int& operator++(Name::Enum&) MOZ_DELETE; \
+     inline int operator++(Name::Enum&, int) MOZ_DELETE; \
+     inline int& operator--(Name::Enum&) MOZ_DELETE; \
+     inline int operator--(Name::Enum&, int) MOZ_DELETE; \
+     inline bool operator==(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline bool operator==(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline bool operator!=(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline bool operator!=(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline bool operator>(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline bool operator>(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline bool operator<(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline bool operator<(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline bool operator>=(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline bool operator>=(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline bool operator<=(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline bool operator<=(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline bool operator!(const Name::Enum&) MOZ_DELETE; \
+     inline bool operator&&(const bool&, const Name::Enum&) MOZ_DELETE; \
+     inline bool operator&&(const Name::Enum&, const bool&) MOZ_DELETE; \
+     inline bool operator||(const bool&, const Name::Enum&) MOZ_DELETE; \
+     inline bool operator||(const Name::Enum&, const bool&) MOZ_DELETE; \
+     inline int operator~(const Name::Enum&) MOZ_DELETE; \
+     inline int operator&(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline int operator&(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline int operator|(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline int operator|(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline int operator^(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline int operator^(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline int operator<<(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline int operator<<(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline int operator>>(const int&, const Name::Enum&) MOZ_DELETE; \
+     inline int operator>>(const Name::Enum&, const int&) MOZ_DELETE; \
+     inline int& operator+=(int&, const Name::Enum&) MOZ_DELETE; \
+     inline int& operator-=(int&, const Name::Enum&) MOZ_DELETE; \
+     inline int& operator*=(int&, const Name::Enum&) MOZ_DELETE; \
+     inline int& operator/=(int&, const Name::Enum&) MOZ_DELETE; \
+     inline int& operator%=(int&, const Name::Enum&) MOZ_DELETE; \
+     inline int& operator&=(int&, const Name::Enum&) MOZ_DELETE; \
+     inline int& operator|=(int&, const Name::Enum&) MOZ_DELETE; \
+     inline int& operator^=(int&, const Name::Enum&) MOZ_DELETE; \
+     inline int& operator<<=(int&, const Name::Enum&) MOZ_DELETE; \
+     inline int& operator>>=(int&, const Name::Enum&) MOZ_DELETE;
+#endif
+
+/**
  * MOZ_WARN_UNUSED_RESULT tells the compiler to emit a warning if a function's
  * return value is not used by the caller.
  *
  * Place this attribute at the very beginning of a function definition. For
  * example, write
  *
  *   MOZ_WARN_UNUSED_RESULT int foo();
  *
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -278,16 +278,20 @@ abstract public class BrowserApp extends
         if (Build.VERSION.SDK_INT >= 14 && !isTablet()) {
             int index = mMainLayout.indexOfChild(mBrowserToolbar.getLayout());
             mMainLayout.removeViewAt(index);
 
             LinearLayout actionBar = (LinearLayout) getActionBarLayout();
             mMainLayout.addView(actionBar, index);
             mBrowserToolbar.from(actionBar);
             mBrowserToolbar.refresh();
+
+            // The favicon view is different now, so we need to update the DoorHangerPopup anchor view.
+            if (mDoorHangerPopup != null)
+                mDoorHangerPopup.setAnchor(mBrowserToolbar.mFavicon);
         }
 
         invalidateOptionsMenu();
         mTabsPanel.refresh();
 
         if (mAboutHomeContent != null)
             mAboutHomeContent.refresh();
     }
--- a/mobile/android/base/DoorHangerPopup.java
+++ b/mobile/android/base/DoorHangerPopup.java
@@ -56,16 +56,20 @@ public class DoorHangerPopup extends Pop
     }
 
     void destroy() {
         GeckoAppShell.unregisterGeckoEventListener("Doorhanger:Add", this);
         GeckoAppShell.unregisterGeckoEventListener("Doorhanger:Remove", this);
         Tabs.unregisterOnTabsChangedListener(this);
     }
 
+    void setAnchor(View aAnchor) {
+        mAnchor = aAnchor;
+    }
+
     public void handleMessage(String event, JSONObject geckoObject) {
         try {
             if (event.equals("Doorhanger:Add")) {
                 int tabId = geckoObject.getInt("tabID");
                 String value = geckoObject.getString("value");
                 String message = geckoObject.getString("message");
                 JSONArray buttons = geckoObject.getJSONArray("buttons");
                 JSONObject options = geckoObject.getJSONObject("options");
--- a/mobile/android/base/tests/BaseTest.java.in
+++ b/mobile/android/base/tests/BaseTest.java.in
@@ -100,17 +100,17 @@ abstract class BaseTest extends Activity
             // re-throw to continue bail-out
             throw t;
         }
     }
 
     @Override
     public void tearDown() throws Exception {
         try {
-            mAsserter.finalize();
+            mAsserter.endTest();
             mSolo.finalize();
         } catch (Throwable e) {
             e.printStackTrace();
         }
         getActivity().finish();
         super.tearDown();
     }
 
--- a/mobile/android/base/tests/testBookmark.java.in
+++ b/mobile/android/base/tests/testBookmark.java.in
@@ -80,16 +80,25 @@ public class testBookmark extends BaseTe
         // Clear VKB so that list is not obscured
         mActions.sendSpecialKey(Actions.SpecialKey.BACK);
 
         // Wait for bookmark to appear in list
         mSolo.waitForText(ABOUT_HOME_URL);
 
         mAsserter.ok(bookmarksList != null, "checking that bookmarks list exists", "bookmarks list exists");
 
+        if (bookmarksList.getChildCount() != 4) {
+            mAsserter.dumpLog("Unexpected child count; dumping bookmarks...");
+            for (int i = 0; i < bookmarksList.getChildCount(); i++) {
+                Cursor c = (Cursor)bookmarksList.getItemAtPosition(i);
+                String url = c.getString(c.getColumnIndexOrThrow("url"));
+                mAsserter.dumpLog(url);
+            }
+        }
+
         // No folders should be visible if no desktop bookmarks exist
         mAsserter.is(bookmarksList.getChildCount(), 4,
             "bookmarks list has 4 children (the default bookmarks)");
 
         for (int i = 0; i < bookmarksList.getChildCount(); i++) {
             Cursor c = (Cursor)bookmarksList.getItemAtPosition(i);
             String url = c.getString(c.getColumnIndexOrThrow("url"));
             mAsserter.ok(Arrays.binarySearch(defaultBookmarks, url) > -1,
--- a/mobile/android/chrome/content/aboutCertError.xhtml
+++ b/mobile/android/chrome/content/aboutCertError.xhtml
@@ -202,34 +202,34 @@
         <div id="introContent">
           <p id="introContentP1">&certerror.introPara1;</p>
         </div>
 
         <div id="whatShouldIDoContent">
           <h2>&certerror.whatShouldIDo.heading;</h2>
           <div id="whatShouldIDoContentText">
             <p>&certerror.whatShouldIDo.content;</p>
-            <xul:button xmlns:xul='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul' id='getMeOutOfHereButton' label='&certerror.getMeOutOfHere.label;'/>
+            <button id="getMeOutOfHereButton">&certerror.getMeOutOfHere.label;</button>
           </div>
         </div>
 
         <!-- The following sections can be unhidden by default by setting the
              "browser.xul.error_pages.expert_bad_cert" pref to true -->
         <div id="technicalContent" collapsed="true">
           <h2 onclick="toggle('technicalContent');" id="technicalContentHeading">&certerror.technical.heading;</h2>
           <p id="technicalContentText"/>
         </div>
 
         <div id="expertContent" collapsed="true">
           <h2 onclick="toggle('expertContent');" id="expertContentHeading">&certerror.expert.heading;</h2>
           <div>
             <p>&certerror.expert.content;</p>
             <p>&certerror.expert.contentPara2;</p>
-            <xul:button xmlns:xul='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul' id='temporaryExceptionButton' label='&certerror.addTemporaryException.label;'/>
-            <xul:button xmlns:xul='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul' id='permanentExceptionButton' label='&certerror.addPermanentException.label;'/>
+            <button id="temporaryExceptionButton">&certerror.addTemporaryException.label;</button>
+            <button id="permanentExceptionButton">&certerror.addPermanentException.label;</button>
           </div>
         </div>
       </div>
     </div>
 
     <!--
     - Note: It is important to run the script this way, instead of using
     - an onload handler. This is because error pages are loaded as
--- a/modules/libpref/src/Preferences.cpp
+++ b/modules/libpref/src/Preferences.cpp
@@ -940,20 +940,20 @@ static nsresult pref_LoadPrefsInDirList(
 
 static nsresult pref_ReadPrefFromJar(nsZipArchive* jarReader, const char *name)
 {
   nsZipItemPtr<char> manifest(jarReader, name, true);
   NS_ENSURE_TRUE(manifest.Buffer(), NS_ERROR_NOT_AVAILABLE);
 
   PrefParseState ps;
   PREF_InitParseState(&ps, PREF_ReaderCallback, NULL);
-  nsresult rv = PREF_ParseBuf(&ps, manifest, manifest.Length());
+  PREF_ParseBuf(&ps, manifest, manifest.Length());
   PREF_FinalizeParseState(&ps);
 
-  return rv;
+  return NS_OK;
 }
 
 //----------------------------------------------------------------------------------------
 // Initialize default preference JavaScript buffers from
 // appropriate TEXT resources
 //----------------------------------------------------------------------------------------
 static nsresult pref_InitInitialObjects()
 {
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -657,17 +657,17 @@ nsUrlClassifierStore::BindStatement(cons
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = statement->BindInt32ByIndex(4, entry.mChunkId);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = statement->BindInt32ByIndex(5, entry.mTableId);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return true;
+  return NS_OK;
 }
 
 nsresult
 nsUrlClassifierStore::ReadEntries(mozIStorageStatement *statement,
                                   nsTArray<nsUrlClassifierEntry>& entries)
 {
   bool exists;
   nsresult rv = statement->ExecuteStep(&exists);
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -3217,16 +3217,23 @@ XREMain::XRE_mainStartup(bool* aExitFlag
   // in nsAppShell::Create, but we need to get in before gtk
   // has been initialized to make sure everything is running
   // consistently.
 #if (MOZ_WIDGET_GTK == 2)
   if (CheckArg("install"))
     gdk_rgb_set_install(TRUE);
 #endif
 
+  // Set program name to the one defined in application.ini.
+  {
+    nsCAutoString program(gAppData->name);
+    ToLowerCase(program);
+    g_set_prgname(program.get());
+  }
+
   // Initialize GTK here for splash.
 
   // Open the display ourselves instead of using gtk_init, so that we can
   // close it without fear that one day gtk might clean up the display it
   // opens.
   if (!gtk_parse_args(&gArgc, &gArgv))
     return 1;
 
@@ -3563,17 +3570,17 @@ XREMain::XRE_mainRun()
 
     comp = do_GetService("@mozilla.org/focus-event-suppressor-service;1");
     NS_TIME_FUNCTION_MARK("Focus Event Suppressor Service");
   }
 #endif
 
   rv = mScopedXPCom->SetWindowCreator(mNativeApp);
   NS_TIME_FUNCTION_MARK("ScopedXPCOMStartup: SetWindowCreator");
-  NS_ENSURE_SUCCESS(rv, 1);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
   NS_TIME_FUNCTION_MARK("ScopedXPCOMStartup: Done");
 
 #ifdef MOZ_CRASHREPORTER
   // tell the crash reporter to also send the release channel
   nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
   if (NS_SUCCEEDED(rv)) {
     nsCOMPtr<nsIPrefBranch> defaultPrefBranch;
@@ -3589,34 +3596,34 @@ XREMain::XRE_mainRun()
     }
   }
 #endif
 
   NS_TIME_FUNCTION_MARK("Next: AppStartup");
 
   if (mStartOffline) {
     nsCOMPtr<nsIIOService2> io (do_GetService("@mozilla.org/network/io-service;1"));
-    NS_ENSURE_TRUE(io, 1);
+    NS_ENSURE_TRUE(io, NS_ERROR_FAILURE);
     io->SetManageOfflineStatus(false);
     io->SetOffline(true);
   }
 
   {
     nsCOMPtr<nsIObserver> startupNotifier
       (do_CreateInstance(NS_APPSTARTUPNOTIFIER_CONTRACTID, &rv));
-    NS_ENSURE_SUCCESS(rv, 1);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
     startupNotifier->Observe(nullptr, APPSTARTUP_TOPIC, nullptr);
   }
 
   NS_TIME_FUNCTION_MARK("Finished startupNotifier");
 
   nsCOMPtr<nsIAppStartup> appStartup
     (do_GetService(NS_APPSTARTUP_CONTRACTID));
-  NS_ENSURE_TRUE(appStartup, 1);
+  NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE);
 
   NS_TIME_FUNCTION_MARK("Created AppStartup");
 
   if (gDoMigration) {
     nsCOMPtr<nsIFile> file;
     mDirProvider.GetAppDir()->Clone(getter_AddRefs(file));
     file->AppendNative(NS_LITERAL_CSTRING("override.ini"));
     nsINIParser parser;
@@ -3634,17 +3641,17 @@ XREMain::XRE_mainRun()
 
   {
     nsCOMPtr<nsIToolkitProfile> selectedProfile;
     if (gDoProfileReset) {
       // At this point we can be sure that profile reset is happening on the default profile.
       rv = mProfileSvc->GetSelectedProfile(getter_AddRefs(selectedProfile));
       if (NS_FAILED(rv)) {
         gDoProfileReset = false;
-        return 1;
+        return NS_ERROR_FAILURE;
       }
     }
 
     // Profile Migration
     if (mAppData->flags & NS_XRE_ENABLE_PROFILE_MIGRATOR && gDoMigration) {
       gDoMigration = false;
       nsCOMPtr<nsIProfileMigrator> pm(do_CreateInstance(NS_PROFILEMIGRATOR_CONTRACTID));
       if (pm) {
@@ -3675,25 +3682,25 @@ XREMain::XRE_mainRun()
   NS_TIME_FUNCTION_MARK("dirProvider.DoStartup() (profile-after-change)");
 
   appStartup->GetShuttingDown(&mShuttingDown);
 
   nsCOMPtr<nsICommandLineRunner> cmdLine;
 
   nsCOMPtr<nsIFile> workingDir;
   rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(workingDir));
-  NS_ENSURE_SUCCESS(rv, 1);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
   if (!mShuttingDown) {
     cmdLine = do_CreateInstance("@mozilla.org/toolkit/command-line;1");
-    NS_ENSURE_TRUE(cmdLine, 1);
+    NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE);
 
     rv = cmdLine->Init(gArgc, gArgv, workingDir,
                        nsICommandLine::STATE_INITIAL_LAUNCH);
-    NS_ENSURE_SUCCESS(rv, 1);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
     /* Special-case services that need early access to the command
         line. */
     nsCOMPtr<nsIObserverService> obsService =
       mozilla::services::GetObserverService();
     if (obsService) {
       obsService->NotifyObservers(cmdLine, "command-line-startup", nullptr);
     }
@@ -3716,17 +3723,17 @@ XREMain::XRE_mainRun()
   SaveToEnv("XRE_BINARY_PATH=");
 
   NS_TIME_FUNCTION_MARK("env munging");
 
   if (!mShuttingDown) {
     NS_TIME_FUNCTION_MARK("Next: CreateHiddenWindow");
 
     rv = appStartup->CreateHiddenWindow();
-    NS_ENSURE_SUCCESS(rv, 1);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
 #if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK)
     nsGTKToolkit* toolkit = nsGTKToolkit::GetToolkit();
     if (toolkit && !mDesktopStartupID.IsEmpty()) {
       toolkit->SetDesktopStartupID(mDesktopStartupID);
     }
     // Clear the environment variable so it won't be inherited by
     // child processes and confuse things.
@@ -3736,38 +3743,38 @@ XREMain::XRE_mainRun()
 #ifdef XP_MACOSX
     // Set up ability to respond to system (Apple) events. This must be
     // done before setting up the command line service.
     SetupMacApplicationDelegate();
 
     // we re-initialize the command-line service and do appleevents munging
     // after we are sure that we're not restarting
     cmdLine = do_CreateInstance("@mozilla.org/toolkit/command-line;1");
-    NS_ENSURE_TRUE(cmdLine, 1);
+    NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE);
 
     CommandLineServiceMac::SetupMacCommandLine(gArgc, gArgv, false);
 
     rv = cmdLine->Init(gArgc, gArgv,
                         workingDir, nsICommandLine::STATE_INITIAL_LAUNCH);
-    NS_ENSURE_SUCCESS(rv, 1);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 #endif
 
     nsCOMPtr<nsIObserverService> obsService =
       mozilla::services::GetObserverService();
     if (obsService)
       obsService->NotifyObservers(nullptr, "final-ui-startup", nullptr);
 
     NS_TIME_FUNCTION_MARK("final-ui-startup done");
 
     appStartup->GetShuttingDown(&mShuttingDown);
   }
 
   if (!mShuttingDown) {
     rv = cmdLine->Run();
-    NS_ENSURE_SUCCESS_LOG(rv, 1);
+    NS_ENSURE_SUCCESS_LOG(rv, NS_ERROR_FAILURE);
 
     appStartup->GetShuttingDown(&mShuttingDown);
   }
 
   if (!mShuttingDown) {
 #ifdef MOZ_ENABLE_XREMOTE
     // if we have X remote support, start listening for requests on the
     // proxy window.
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -286,65 +286,65 @@ XRE_InitChildProcess(int aArgc,
   SAMPLE_LABEL("Startup", "XRE_InitChildProcess");
 
   sChildProcessType = aProcess;
 
   // Complete 'task_t' exchange for Mac OS X. This structure has the same size
   // regardless of architecture so we don't have any cross-arch issues here.
 #ifdef XP_MACOSX
   if (aArgc < 1)
-    return 1;
+    return NS_ERROR_FAILURE;
   const char* const mach_port_name = aArgv[--aArgc];
 
   const int kTimeoutMs = 1000;
 
   MachSendMessage child_message(0);
   if (!child_message.AddDescriptor(mach_task_self())) {
     NS_WARNING("child AddDescriptor(mach_task_self()) failed.");
-    return 1;
+    return NS_ERROR_FAILURE;
   }
 
   ReceivePort child_recv_port;
   mach_port_t raw_child_recv_port = child_recv_port.GetPort();
   if (!child_message.AddDescriptor(raw_child_recv_port)) {
     NS_WARNING("Adding descriptor to message failed");
-    return 1;
+    return NS_ERROR_FAILURE;
   }
 
   MachPortSender child_sender(mach_port_name);
   kern_return_t err = child_sender.SendMessage(child_message, kTimeoutMs);
   if (err != KERN_SUCCESS) {
     NS_WARNING("child SendMessage() failed");
-    return 1;
+    return NS_ERROR_FAILURE;
   }
 
   MachReceiveMessage parent_message;
   err = child_recv_port.WaitForMessage(&parent_message, kTimeoutMs);
   if (err != KERN_SUCCESS) {
     NS_WARNING("child WaitForMessage() failed");
-    return 1;
+    return NS_ERROR_FAILURE;
   }
 
   if (parent_message.GetTranslatedPort(0) == MACH_PORT_NULL) {
     NS_WARNING("child GetTranslatedPort(0) failed");
-    return 1;
+    return NS_ERROR_FAILURE;
   }
   err = task_set_bootstrap_port(mach_task_self(),
                                 parent_message.GetTranslatedPort(0));
   if (err != KERN_SUCCESS) {
     NS_WARNING("child task_set_bootstrap_port() failed");
-    return 1;
+    return NS_ERROR_FAILURE;
   }
 #endif
 
   SetupErrorHandling(aArgv[0]);  
 
 #if defined(MOZ_CRASHREPORTER)
   if (aArgc < 1)
-    return 1;
+    return NS_ERROR_FAILURE;
   const char* const crashReporterArg = aArgv[--aArgc];
   
 #  if defined(XP_WIN) || defined(XP_MACOSX)
   // on windows and mac, |crashReporterArg| is the named pipe on which the
   // server is listening for requests, or "-" if crash reporting is
   // disabled.
   if (0 != strcmp("-", crashReporterArg) && 
       !XRE_SetRemoteExceptionHandler(crashReporterArg)) {
--- a/webapprt/gtk2/webapprt.cpp
+++ b/webapprt/gtk2/webapprt.cpp
@@ -133,24 +133,16 @@ bool GRELoadAndLaunch(const char* firefo
     return false;
   }
 
   if (NS_FAILED(XPCOMGlueLoadXULFunctions(kXULFuncs))) {
     ErrorDialog("Couldn't load libxul");
     return false;
   }
 
-  if (!isProfileOverridden) {
-    // Override the class name part of the WM_CLASS property, so that the
-    // DE can match our window to the correct launcher
-    char programClass[MAXPATHLEN];
-    snprintf(programClass, MAXPATHLEN, "owa-%s", profile);
-    g_set_prgname(programClass);
-  }
-
   // NOTE: The GRE has successfully loaded, so we can use XPCOM now
   { // Scope for any XPCOM stuff we create
     ScopedLogging log;
 
     // Get the path to the runtime
     char rtPath[MAXPATHLEN];
     snprintf(rtPath, MAXPATHLEN, "%s/%s", firefoxDir, kWEBAPPRT_PATH);
 
@@ -175,17 +167,22 @@ bool GRELoadAndLaunch(const char* firefo
     nsXREAppData *webShellAppData;
     if (NS_FAILED(XRE_CreateAppData(rtINI, &webShellAppData))) {
       ErrorDialog("Couldn't read WebappRT application.ini");
       return false;
     }
 
     if (!isProfileOverridden) {
       SetAllocatedString(webShellAppData->profile, profile);
-      SetAllocatedString(webShellAppData->name, profile);
+      // nsXREAppData::name is used for the class name part of the WM_CLASS
+      // property. Set it so that the DE can match our window to the correct
+      // launcher.
+      char programClass[MAXPATHLEN];
+      snprintf(programClass, MAXPATHLEN, "owa-%s", profile);
+      SetAllocatedString(webShellAppData->name, programClass);
     }
 
     nsCOMPtr<nsIFile> directory;
     if (NS_FAILED(XRE_GetFileFromPath(rtPath, getter_AddRefs(directory)))) {
       ErrorDialog("Couldn't open runtime directory");
       return false;
     }
 
--- a/xpcom/idl-parser/typelib.py
+++ b/xpcom/idl-parser/typelib.py
@@ -13,23 +13,23 @@ import xpidl, xpt
 
 # A map of xpidl.py types to xpt.py types
 TypeMap = {
     # nsresult is not strictly an xpidl.py type, but it's useful here
     'nsresult':           xpt.Type.Tags.uint32,
     # builtins
     'boolean':            xpt.Type.Tags.boolean,
     'void':               xpt.Type.Tags.void,
-    'int16_t':              xpt.Type.Tags.int16,
-    'int32_t':               xpt.Type.Tags.int32,
-    'int64_t':          xpt.Type.Tags.int64,
-    'uint8_t':              xpt.Type.Tags.uint8,
-    'uint16_t':     xpt.Type.Tags.uint16,
-    'uint32_t':      xpt.Type.Tags.uint32,
-    'uint64_t': xpt.Type.Tags.uint64,
+    'int16_t':            xpt.Type.Tags.int16,
+    'int32_t':            xpt.Type.Tags.int32,
+    'int64_t':            xpt.Type.Tags.int64,
+    'uint8_t':            xpt.Type.Tags.uint8,
+    'uint16_t':           xpt.Type.Tags.uint16,
+    'uint32_t':           xpt.Type.Tags.uint32,
+    'uint64_t':           xpt.Type.Tags.uint64,
     'octet':              xpt.Type.Tags.uint8,
     'short':              xpt.Type.Tags.int16,
     'long':               xpt.Type.Tags.int32,
     'long long':          xpt.Type.Tags.int64,
     'unsigned short':     xpt.Type.Tags.uint16,
     'unsigned long':      xpt.Type.Tags.uint32,
     'unsigned long long': xpt.Type.Tags.uint64,
     'float':              xpt.Type.Tags.float,