Merge mozilla-central to mozilla-inbound. CLOSED TREE
authorCsoregi Natalia <ncsoregi@mozilla.com>
Thu, 25 Oct 2018 07:48:39 +0300
changeset 491250 41a7fd6bfe54e1afcbf5e25eb057602debc1dce9
parent 491210 d12102a7cea7849009ff99668c23a3d7626e47f7 (current diff)
parent 491249 76d5b62fb151f9d28edd7a626c7757ec8fdeda47 (diff)
child 491252 f2e2f5d9a0eb88306fea082858c5eb54a956d204
push id247
push userfmarier@mozilla.com
push dateSat, 27 Oct 2018 01:06:44 +0000
milestone65.0a1
Merge mozilla-central to mozilla-inbound. CLOSED TREE
gfx/vr/gfxVROculus.cpp
gfx/vr/gfxVROculus.h
gfx/vr/gfxVROpenVR.cpp
gfx/vr/gfxVROpenVR.h
gfx/vr/openvr/LICENSE
gfx/vr/openvr/README.md
gfx/vr/openvr/README.mozilla
gfx/vr/openvr/headers/openvr.h
gfx/vr/openvr/moz.build
gfx/vr/openvr/src/README
gfx/vr/openvr/src/dirtools_public.cpp
gfx/vr/openvr/src/dirtools_public.h
gfx/vr/openvr/src/envvartools_public.cpp
gfx/vr/openvr/src/envvartools_public.h
gfx/vr/openvr/src/hmderrors_public.cpp
gfx/vr/openvr/src/hmderrors_public.h
gfx/vr/openvr/src/ivrclientcore.h
gfx/vr/openvr/src/openvr_api_public.cpp
gfx/vr/openvr/src/pathtools_public.cpp
gfx/vr/openvr/src/pathtools_public.h
gfx/vr/openvr/src/sharedlibtools_public.cpp
gfx/vr/openvr/src/sharedlibtools_public.h
gfx/vr/openvr/src/strtools_public.cpp
gfx/vr/openvr/src/strtools_public.h
gfx/vr/openvr/src/vrpathregistry_public.cpp
gfx/vr/openvr/src/vrpathregistry_public.h
gfx/vr/ovr_capi_dynamic.h
testing/web-platform/meta/beacon/beacon-error.window.js.ini
testing/web-platform/tests/beacon/beacon-error.window.js
--- a/.clang-format-ignore
+++ b/.clang-format-ignore
@@ -47,17 +47,17 @@ extensions/spellcheck/hunspell/src/.*
 gfx/angle/.*
 gfx/cairo/.*
 gfx/graphite2/.*
 gfx/harfbuzz/.*
 gfx/ots/.*
 gfx/qcms/.*
 gfx/sfntly/.*
 gfx/skia/.*
-gfx/vr/openvr/.*
+gfx/vr/service/openvr/.*
 gfx/webrender/.*
 gfx/webrender_api/.*
 gfx/wrench/.*
 gfx/ycbcr/.*
 intl/hyphenation/hyphen/.*
 intl/icu/.*
 ipc/chromium/.*
 js/src/ctypes/libffi/.*
--- a/.flake8
+++ b/.flake8
@@ -17,8 +17,9 @@ exclude =
     js/*.configure,
     memory/moz.configure,
     mobile/android/*.configure,
     node_modules,
     security/nss/,
     testing/mochitest/pywebsocket,
     tools/lint/test/files,
     build/build-infer/build-infer.py,
+    tools/infer/test/*.configure,    tools/infer/test/*.configure,
\ No newline at end of file
--- a/.hgignore
+++ b/.hgignore
@@ -69,16 +69,17 @@ compile_commands\.json
 # Ignore the files and directory that JetBrains IDEs create.
 \.idea/
 \.iml$
 # Android Monitor in Android Studio creates a captures/ directory.
 ^captures/
 
 # Gradle cache.
 ^.gradle/
+^tools/infer/test/.gradle/
 
 # Local Gradle configuration properties.
 ^local.properties$
 
 # Python stuff installed at build time.
 ^third_party/python/psutil/.*\.so
 ^third_party/python/psutil/.*\.pyd
 ^third_party/python/psutil/build/
--- a/browser/extensions/formautofill/FormAutofillPreferences.jsm
+++ b/browser/extensions/formautofill/FormAutofillPreferences.jsm
@@ -72,41 +72,38 @@ FormAutofillPreferences.prototype = {
     let formAutofillFragment = document.createDocumentFragment();
     let formAutofillGroupBoxCaption = document.createXULElement("caption");
     let formAutofillGroupBoxCaptionLabel = document.createXULElement("label");
     let formAutofillGroupBoxDescription = document.createXULElement("description");
     let formAutofillGroup = document.createXULElement("vbox");
     let addressAutofill = document.createXULElement("hbox");
     let addressAutofillCheckboxGroup = document.createXULElement("hbox");
     let addressAutofillCheckbox = document.createXULElement("checkbox");
-    let addressAutofillCheckboxLabel = document.createXULElement("label");
-    let addressAutofillCheckboxLabelSpacer = document.createXULElement("spacer");
     let addressAutofillLearnMore = document.createXULElement("label");
     let savedAddressesBtn = document.createXULElement("button");
     // Wrappers are used to properly compute the search tooltip positions
     let savedAddressesBtnWrapper = document.createXULElement("hbox");
     let savedCreditCardsBtnWrapper = document.createXULElement("hbox");
 
     savedAddressesBtn.className = "accessory-button";
-    addressAutofillCheckboxLabelSpacer.className = "tail-with-learn-more";
+    addressAutofillCheckbox.className = "tail-with-learn-more";
     addressAutofillLearnMore.className = "learnMore text-link";
 
     formAutofillGroup.id = "formAutofillGroup";
     addressAutofill.id = "addressAutofill";
     addressAutofillLearnMore.id = "addressAutofillLearnMore";
 
     formAutofillGroupBoxCaptionLabel.textContent = this.bundle.GetStringFromName("autofillHeader");
     formAutofillGroupBoxDescription.textContent =
       this.bundle.formatStringFromName("autofillDescription",
                                        [FormAutofillUtils.brandBundle.GetStringFromName("brandShortName")],
                                        1);
 
     addressAutofill.setAttribute("data-subcategory", "address-autofill");
-    addressAutofillCheckboxLabel.textContent = this.bundle.GetStringFromName("autofillAddressesCheckbox");
-    addressAutofillCheckbox.setAttribute("aria-label", addressAutofillCheckboxLabel.textContent);
+    addressAutofillCheckbox.setAttribute("label", this.bundle.GetStringFromName("autofillAddressesCheckbox"));
     addressAutofillLearnMore.textContent = this.bundle.GetStringFromName("learnMoreLabel");
     savedAddressesBtn.setAttribute("label", this.bundle.GetStringFromName("savedAddressesBtnLabel"));
     // Align the start to keep the savedAddressesBtn as original size
     // when addressAutofillCheckboxGroup's height is changed by a longer l10n string
     savedAddressesBtnWrapper.setAttribute("align", "start");
 
     addressAutofillLearnMore.setAttribute("href", learnMoreURL);
 
@@ -116,57 +113,50 @@ FormAutofillPreferences.prototype = {
 
     // Manually set the checked state
     if (FormAutofill.isAutofillAddressesEnabled) {
       addressAutofillCheckbox.setAttribute("checked", true);
     }
 
     addressAutofillCheckboxGroup.align = "center";
     addressAutofillCheckboxGroup.flex = 1;
-    addressAutofillCheckboxLabel.flex = 1;
 
     formAutofillGroupBoxCaption.appendChild(formAutofillGroupBoxCaptionLabel);
     formAutofillFragment.appendChild(formAutofillGroupBoxCaption);
     formAutofillFragment.appendChild(formAutofillGroupBoxDescription);
     formAutofillFragment.appendChild(formAutofillGroup);
     formAutofillGroup.appendChild(addressAutofill);
     addressAutofill.appendChild(addressAutofillCheckboxGroup);
     addressAutofillCheckboxGroup.appendChild(addressAutofillCheckbox);
-    addressAutofillCheckboxGroup.appendChild(addressAutofillCheckboxLabel);
-    addressAutofillCheckboxLabel.appendChild(addressAutofillCheckboxLabelSpacer);
-    addressAutofillCheckboxLabel.appendChild(addressAutofillLearnMore);
+    addressAutofillCheckboxGroup.appendChild(addressAutofillLearnMore);
     addressAutofill.appendChild(savedAddressesBtnWrapper);
     savedAddressesBtnWrapper.appendChild(savedAddressesBtn);
 
     this.refs = {
       formAutofillFragment,
       formAutofillGroup,
       addressAutofillCheckbox,
-      addressAutofillCheckboxLabel,
       savedAddressesBtn,
     };
 
     if (FormAutofill.isAutofillCreditCardsAvailable) {
       let creditCardAutofill = document.createXULElement("hbox");
       let creditCardAutofillCheckboxGroup = document.createXULElement("hbox");
       let creditCardAutofillCheckbox = document.createXULElement("checkbox");
-      let creditCardAutofillCheckboxLabel = document.createXULElement("label");
-      let creditCardAutofillCheckboxLabelSpacer = document.createXULElement("spacer");
       let creditCardAutofillLearnMore = document.createXULElement("label");
       let savedCreditCardsBtn = document.createXULElement("button");
       savedCreditCardsBtn.className = "accessory-button";
-      creditCardAutofillCheckboxLabelSpacer.className = "tail-with-learn-more";
+      creditCardAutofillCheckbox.className = "tail-with-learn-more";
       creditCardAutofillLearnMore.className = "learnMore text-link";
 
       creditCardAutofill.id = "creditCardAutofill";
       creditCardAutofillLearnMore.id = "creditCardAutofillLearnMore";
 
       creditCardAutofill.setAttribute("data-subcategory", "credit-card-autofill");
-      creditCardAutofillCheckboxLabel.textContent = this.bundle.GetStringFromName("autofillCreditCardsCheckbox");
-      creditCardAutofillCheckbox.setAttribute("aria-label", creditCardAutofillCheckboxLabel.textContent);
+      creditCardAutofillCheckbox.setAttribute("label", this.bundle.GetStringFromName("autofillCreditCardsCheckbox"));
       creditCardAutofillLearnMore.textContent = this.bundle.GetStringFromName("learnMoreLabel");
       savedCreditCardsBtn.setAttribute("label", this.bundle.GetStringFromName("savedCreditCardsBtnLabel"));
       // Align the start to keep the savedCreditCardsBtn as original size
       // when creditCardAutofillCheckboxGroup's height is changed by a longer l10n string
       savedCreditCardsBtnWrapper.setAttribute("align", "start");
 
       creditCardAutofillLearnMore.setAttribute("href", learnMoreURL);
 
@@ -176,29 +166,25 @@ FormAutofillPreferences.prototype = {
 
       // Manually set the checked state
       if (FormAutofill.isAutofillCreditCardsEnabled) {
         creditCardAutofillCheckbox.setAttribute("checked", true);
       }
 
       creditCardAutofillCheckboxGroup.align = "center";
       creditCardAutofillCheckboxGroup.flex = 1;
-      creditCardAutofillCheckboxLabel.flex = 1;
 
       formAutofillGroup.appendChild(creditCardAutofill);
       creditCardAutofill.appendChild(creditCardAutofillCheckboxGroup);
       creditCardAutofillCheckboxGroup.appendChild(creditCardAutofillCheckbox);
-      creditCardAutofillCheckboxGroup.appendChild(creditCardAutofillCheckboxLabel);
-      creditCardAutofillCheckboxLabel.appendChild(creditCardAutofillCheckboxLabelSpacer);
-      creditCardAutofillCheckboxLabel.appendChild(creditCardAutofillLearnMore);
+      creditCardAutofillCheckboxGroup.appendChild(creditCardAutofillLearnMore);
       creditCardAutofill.appendChild(savedCreditCardsBtnWrapper);
       savedCreditCardsBtnWrapper.appendChild(savedCreditCardsBtn);
 
       this.refs.creditCardAutofillCheckbox = creditCardAutofillCheckbox;
-      this.refs.creditCardAutofillCheckboxLabel = creditCardAutofillCheckboxLabel;
       this.refs.savedCreditCardsBtn = savedCreditCardsBtn;
     }
   },
 
   /**
    * Handle events
    *
    * @param  {DOMEvent} event
--- a/build/unix/elfhack/elf.cpp
+++ b/build/unix/elfhack/elf.cpp
@@ -621,17 +621,17 @@ void ElfSegment::addSection(ElfSection *
 void ElfSegment::removeSection(ElfSection *section)
 {
     sections.remove(section);
     section->removeFromSegment(this);
 }
 
 unsigned int ElfSegment::getFileSize()
 {
-    if (type == PT_GNU_RELRO || isElfHackFillerSegment())
+    if (type == PT_GNU_RELRO)
         return filesz;
 
     if (sections.empty())
         return 0;
     // Search the last section that is not SHT_NOBITS
     std::list<ElfSection *>::reverse_iterator i;
     for (i = sections.rbegin(); (i != sections.rend()) && ((*i)->getType() == SHT_NOBITS); ++i);
     // All sections are SHT_NOBITS
@@ -640,49 +640,42 @@ unsigned int ElfSegment::getFileSize()
 
     unsigned int end = (*i)->getAddr() + (*i)->getSize();
 
     return end - sections.front()->getAddr();
 }
 
 unsigned int ElfSegment::getMemSize()
 {
-    if (type == PT_GNU_RELRO || isElfHackFillerSegment())
+    if (type == PT_GNU_RELRO)
         return memsz;
 
     if (sections.empty())
         return 0;
 
     unsigned int end = sections.back()->getAddr() + sections.back()->getSize();
 
     return end - sections.front()->getAddr();
 }
 
 unsigned int ElfSegment::getOffset()
 {
     if ((type == PT_GNU_RELRO) && !sections.empty() &&
         (sections.front()->getAddr() != vaddr))
         throw std::runtime_error("PT_GNU_RELRO segment doesn't start on a section start");
 
-    // Neither bionic nor glibc linkers seem to like when the offset of that segment is 0
-    if (isElfHackFillerSegment())
-        return vaddr;
-
     return sections.empty() ? 0 : sections.front()->getOffset();
 }
 
 unsigned int ElfSegment::getAddr()
 {
     if ((type == PT_GNU_RELRO) && !sections.empty() &&
         (sections.front()->getAddr() != vaddr))
         throw std::runtime_error("PT_GNU_RELRO segment doesn't start on a section start");
 
-    if (isElfHackFillerSegment())
-        return vaddr;
-
     return sections.empty() ? 0 : sections.front()->getAddr();
 }
 
 void ElfSegment::clear()
 {
     for (std::list<ElfSection *>::iterator i = sections.begin(); i != sections.end(); ++i)
         (*i)->removeFromSegment(this);
     sections.clear();
--- a/build/unix/elfhack/elfhack.cpp
+++ b/build/unix/elfhack/elfhack.cpp
@@ -459,17 +459,17 @@ void set_relative_reloc(Elf_Rel *rel, El
 
 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;
 }
 
-void maybe_split_segment(Elf *elf, ElfSegment *segment, bool fill)
+void maybe_split_segment(Elf *elf, ElfSegment *segment)
 {
     std::list<ElfSection *>::iterator it = segment->begin();
     for (ElfSection *last = *(it++); it != segment->end(); last = *(it++)) {
         // When two consecutive non-SHT_NOBITS sections are apart by more
         // than the alignment of the section, the second can be moved closer
         // to the first, but this requires the segment to be split.
         if (((*it)->getType() != SHT_NOBITS) && (last->getType() != SHT_NOBITS) &&
             ((*it)->getOffset() - last->getOffset() - last->getSize() > segment->getAlign())) {
@@ -479,55 +479,22 @@ void maybe_split_segment(Elf *elf, ElfSe
             phdr.p_vaddr = 0;
             phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff();
             phdr.p_flags = segment->getFlags();
             phdr.p_align = segment->getAlign();
             phdr.p_filesz = (unsigned int)-1;
             phdr.p_memsz = (unsigned int)-1;
             ElfSegment *newSegment = new ElfSegment(&phdr);
             elf->insertSegmentAfter(segment, newSegment);
-            ElfSection *section = *it;
             for (; it != segment->end(); ++it) {
                 newSegment->addSection(*it);
             }
             for (it = newSegment->begin(); it != newSegment->end(); ++it) {
                 segment->removeSection(*it);
             }
-            // Fill the virtual address space gap left between the two PT_LOADs
-            // with a new PT_LOAD with no permissions. This avoids the linker
-            // (especially bionic's) filling the gap with anonymous memory,
-            // which breakpad doesn't like.
-            // /!\ running strip on a elfhacked binary will break this filler
-            // PT_LOAD.
-            if (!fill)
-                break;
-            // Insert dummy segment to normalize the entire Elf with the header
-            // sizes adjusted, before inserting a filler segment.
-            {
-              memset(&phdr, 0, sizeof(phdr));
-              ElfSegment dummySegment(&phdr);
-              elf->insertSegmentAfter(segment, &dummySegment);
-              elf->normalize();
-              elf->removeSegment(&dummySegment);
-            }
-            ElfSection *previous = section->getPrevious();
-            phdr.p_type = PT_LOAD;
-            phdr.p_vaddr = (previous->getAddr() + previous->getSize() + segment->getAlign() - 1) & ~(segment->getAlign() - 1);
-            phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff();
-            phdr.p_flags = 0;
-            phdr.p_align = 0;
-            phdr.p_filesz = (section->getAddr() & ~(newSegment->getAlign() - 1)) - phdr.p_vaddr;
-            phdr.p_memsz = phdr.p_filesz;
-            if (phdr.p_filesz) {
-                newSegment = new ElfSegment(&phdr);
-                assert(newSegment->isElfHackFillerSegment());
-                elf->insertSegmentAfter(segment, newSegment);
-            } else {
-                elf->normalize();
-            }
             break;
         }
     }
 }
 
 // EH_FRAME constants
 static const char DW_EH_PE_absptr = 0x00;
 static const char DW_EH_PE_omit = 0xff;
@@ -762,17 +729,17 @@ static void adjust_eh_frame(ElfSection* 
     }
     return;
 
 malformed:
     throw std::runtime_error("malformed .eh_frame");
 }
 
 template <typename Rel_Type>
-int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type2, bool force, bool fill)
+int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type2, bool force)
 {
     ElfDynamic_Section *dyn = elf->getDynSection();
     if (dyn == nullptr) {
         fprintf(stderr, "Couldn't find SHT_DYNAMIC section\n");
         return -1;
     }
 
     ElfRel_Section<Rel_Type> *section = (ElfRel_Section<Rel_Type> *)dyn->getSectionForType(Rel_Type::d_tag);
@@ -1131,17 +1098,17 @@ int do_relocation_section(Elf *elf, unsi
         assert(distance == second->getAddr() - first->getAddr());
         first->markDirty();
         adjust_eh_frame(eh_frame, origAddr, elf);
     }
 
     // Adjust PT_LOAD segments
     for (ElfSegment *segment = elf->getSegmentByType(PT_LOAD); segment;
          segment = elf->getSegmentByType(PT_LOAD, segment)) {
-        maybe_split_segment(elf, segment, fill);
+        maybe_split_segment(elf, segment);
     }
 
     // Ensure Elf sections will be at their final location.
     elf->normalize();
     ElfLocation *init = new ElfLocation(relhackcode, relhackcode->getEntryPoint());
     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
@@ -1162,17 +1129,17 @@ int do_relocation_section(Elf *elf, unsi
 
 static inline int backup_file(const char *name)
 {
     std::string fname(name);
     fname += ".bak";
     return rename(name, fname.c_str());
 }
 
-void do_file(const char *name, bool backup = false, bool force = false, bool fill = false)
+void do_file(const char *name, bool backup = false, bool force = false)
 {
     std::ifstream file(name, std::ios::in|std::ios::binary);
     Elf elf(file);
     unsigned int size = elf.getSize();
     fprintf(stderr, "%s: ", name);
     if (elf.getType() != ET_DYN) {
         fprintf(stderr, "Not a shared object. Skipping\n");
         return;
@@ -1185,23 +1152,23 @@ void do_file(const char *name, bool back
             fprintf(stderr, "Already elfhacked. Skipping\n");
             return;
         }
     }
 
     int exit = -1;
     switch (elf.getMachine()) {
     case EM_386:
-        exit = do_relocation_section<Elf_Rel>(&elf, R_386_RELATIVE, R_386_32, force, fill);
+        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, force, fill);
+        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, force, fill);
+        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 {
@@ -1234,72 +1201,66 @@ void undo_file(const char *name, bool ba
             text = section;
     }
 
     if (!data || !text) {
         fprintf(stderr, "Not elfhacked. Skipping\n");
         return;
     }
 
+    // When both elfhack sections are in the same segment, try to merge
+    // the segment that contains them both and the following segment.
+    // When the elfhack sections are in separate segments, try to merge
+    // those segments.
     ElfSegment *first = data->getSegmentByType(PT_LOAD);
     ElfSegment *second = text->getSegmentByType(PT_LOAD);
-    if (first != second) {
-        fprintf(stderr, elfhack_data " and " elfhack_text " not in the same segment. Skipping\n");
-        return;
+    if (first == second) {
+        second = elf.getSegmentByType(PT_LOAD, first);
     }
-    second = elf.getSegmentByType(PT_LOAD, first);
-    ElfSegment *filler = nullptr;
-    // If the second PT_LOAD is a filler from elfhack --fill, check the third.
-    if (second->isElfHackFillerSegment()) {
-        filler = second;
-        second = elf.getSegmentByType(PT_LOAD, filler);
-    }
+
+    // Only merge the segments when their flags match.
     if (second->getFlags() != first->getFlags()) {
-        fprintf(stderr, "Couldn't identify elfhacked PT_LOAD segments. Skipping\n");
+        fprintf(stderr, "Couldn't merge PT_LOAD segments. Skipping\n");
         return;
     }
     // Move sections from the second PT_LOAD to the first, and remove the
     // second PT_LOAD segment.
     for (std::list<ElfSection *>::iterator section = second->begin();
          section != second->end(); ++section)
         first->addSection(*section);
 
     elf.removeSegment(second);
-    if (filler)
-        elf.removeSegment(filler);
+    elf.normalize();
 
     if (backup && backup_file(name) != 0) {
         fprintf(stderr, "Couln't create backup file\n");
     } else {
         std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc);
         elf.write(ofile);
         fprintf(stderr, "Grown by %d bytes\n", elf.getSize() - size);
     }
 }
 
 int main(int argc, char *argv[])
 {
     int arg;
     bool backup = false;
     bool force = false;
     bool revert = false;
-    bool fill = false;
     char *lastSlash = rindex(argv[0], '/');
     if (lastSlash != nullptr)
         rundir = strndup(argv[0], lastSlash - argv[0]);
     for (arg = 1; arg < argc; arg++) {
         if (strcmp(argv[arg], "-f") == 0)
             force = true;
         else if (strcmp(argv[arg], "-b") == 0)
             backup = true;
         else if (strcmp(argv[arg], "-r") == 0)
             revert = true;
-        else if (strcmp(argv[arg], "--fill") == 0)
-            fill = true;
         else if (revert) {
             undo_file(argv[arg], backup);
         } else
-            do_file(argv[arg], backup, force, fill);
+            do_file(argv[arg], backup, force);
     }
 
     free(rundir);
     return 0;
 }
--- a/build/unix/elfhack/elfxx.h
+++ b/build/unix/elfhack/elfxx.h
@@ -481,20 +481,16 @@ public:
 
     void addSection(ElfSection *section);
     void removeSection(ElfSection *section);
 
     std::list<ElfSection *>::iterator begin() { return sections.begin(); }
     std::list<ElfSection *>::iterator end() { return sections.end(); }
 
     void clear();
-
-    bool isElfHackFillerSegment() {
-      return type == PT_LOAD && flags == 0;
-    }
 private:
     unsigned int type;
     int v_p_diff; // Difference between physical and virtual address
     unsigned int flags;
     unsigned int align;
     std::list<ElfSection *> sections;
     // The following are only really used for PT_GNU_RELRO until something
     // better is found.
--- a/devtools/client/framework/components/ToolboxTabs.js
+++ b/devtools/client/framework/components/ToolboxTabs.js
@@ -55,18 +55,19 @@ class ToolboxTabs extends Component {
     // Map with tool Id and its width size. This lifecycle is out of React's
     // lifecycle. If a tool is registered, ToolboxTabs will add target tool id
     // to this map. ToolboxTabs will never remove tool id from this cache.
     this._cachedToolTabsWidthMap = new Map();
 
     this._resizeTimerId = null;
     this.resizeHandler = this.resizeHandler.bind(this);
 
+    const { toolbox, onTabsOrderUpdated, panelDefinitions } = props;
     this._tabsOrderManager =
-      new ToolboxTabsOrderManager(props.onTabsOrderUpdated, props.panelDefinitions);
+      new ToolboxTabsOrderManager(toolbox, onTabsOrderUpdated, panelDefinitions);
   }
 
   componentDidMount() {
     window.addEventListener("resize", this.resizeHandler);
     this.updateCachedToolTabsWidthMap();
     this.updateOverflowedTabs();
   }
 
--- a/devtools/client/framework/toolbox-tabs-order-manager.js
+++ b/devtools/client/framework/toolbox-tabs-order-manager.js
@@ -10,17 +10,18 @@ const Services = require("Services");
 const Telemetry = require("devtools/client/shared/telemetry");
 const TABS_REORDERED_SCALAR = "devtools.toolbox.tabs_reordered";
 const PREFERENCE_NAME = "devtools.toolbox.tabsOrder";
 
 /**
  * Manage the order of devtools tabs.
  */
 class ToolboxTabsOrderManager {
-  constructor(onOrderUpdated, panelDefinitions) {
+  constructor(toolbox, onOrderUpdated, panelDefinitions) {
+    this.toolbox = toolbox;
     this.onOrderUpdated = onOrderUpdated;
     this.currentPanelDefinitions = panelDefinitions || [];
 
     this.onMouseDown = this.onMouseDown.bind(this);
     this.onMouseMove = this.onMouseMove.bind(this);
     this.onMouseUp = this.onMouseUp.bind(this);
 
     Services.prefs.addObserver(PREFERENCE_NAME, this.onOrderUpdated);
@@ -48,16 +49,20 @@ class ToolboxTabsOrderManager {
     return !tabElement.previousSibling;
   }
 
   isLastTab(tabElement) {
     return !tabElement.nextSibling ||
            tabElement.nextSibling.id === "tools-chevron-menu-button";
   }
 
+  isRTL() {
+    return this.toolbox.direction === "rtl";
+  }
+
   async saveOrderPreference() {
     const tabs = [...this.toolboxTabsElement.querySelectorAll(".devtools-tab")];
     const tabIds = tabs.map(tab => tab.dataset.extensionId || tab.dataset.id);
     // Concat the overflowed tabs id since they are not contained in visible tabs.
     // The overflowed tabs cannot be reordered so we just append the id from current
     // panel definitions on their order.
     const overflowedTabIds =
       this.currentPanelDefinitions
@@ -99,49 +104,64 @@ class ToolboxTabsOrderManager {
     this.eventTarget.addEventListener("mousemove", this.onMouseMove);
     this.eventTarget.addEventListener("mouseup", this.onMouseUp);
 
     this.toolboxContainerElement.classList.add("tabs-reordering");
   }
 
   onMouseMove(e) {
     const diffPageX = e.pageX - this.previousPageX;
-    const dragTargetCenterX =
+    let dragTargetCenterX =
       this.dragTarget.offsetLeft + diffPageX + this.dragTarget.clientWidth / 2;
     let isDragTargetPreviousSibling = false;
 
-    for (const tabElement of this.toolboxTabsElement.querySelectorAll(".devtools-tab")) {
+    const tabElements = this.toolboxTabsElement.querySelectorAll(".devtools-tab");
+
+    // Calculate the minimum and maximum X-offset that can be valid for the drag target.
+    const firstElement = tabElements[0];
+    const firstElementCenterX = firstElement.offsetLeft + firstElement.clientWidth / 2;
+    const lastElement = tabElements[tabElements.length - 1];
+    const lastElementCenterX = lastElement.offsetLeft + lastElement.clientWidth / 2;
+    const max = Math.max(firstElementCenterX, lastElementCenterX);
+    const min = Math.min(firstElementCenterX, lastElementCenterX);
+
+    // Normalize the target center X so to remain between the first and last tab.
+    dragTargetCenterX = Math.min(max, dragTargetCenterX);
+    dragTargetCenterX = Math.max(min, dragTargetCenterX);
+
+    for (const tabElement of tabElements) {
       if (tabElement === this.dragTarget) {
         isDragTargetPreviousSibling = true;
         continue;
       }
 
+      // Is the dragTarget near the center of the other tab?
       const anotherCenterX = tabElement.offsetLeft + tabElement.clientWidth / 2;
-      const isReplaceable =
-        // Is the dragTarget near the center of the other tab?
-        Math.abs(dragTargetCenterX - anotherCenterX) < tabElement.clientWidth / 3 ||
-        // Has the dragTarget moved before the first tab
-        // (mouse moved too fast between two events)
-        (this.isFirstTab(tabElement) && dragTargetCenterX < anotherCenterX) ||
-        // Has the dragTarget moved after the last tab
-        // (mouse moved too fast between two events)
-        (this.isLastTab(tabElement) && anotherCenterX < dragTargetCenterX);
+      const distanceWithDragTarget = Math.abs(dragTargetCenterX - anotherCenterX);
+      const isReplaceable = distanceWithDragTarget < tabElement.clientWidth / 3;
 
       if (isReplaceable) {
         const replaceableElement =
           isDragTargetPreviousSibling ? tabElement.nextSibling : tabElement;
         this.insertBefore(replaceableElement);
         break;
       }
     }
 
     let distance = e.pageX - this.dragStartX;
 
-    if ((this.isFirstTab(this.dragTarget) && distance < 0) ||
-        (this.isLastTab(this.dragTarget) && distance > 0)) {
+    // To accomodate for RTL locales, we cannot rely on the first/last element of the
+    // NodeList. We cannot have negative distances for the leftmost tab, and we cannot
+    // have positive distances for the rightmost tab.
+    const isFirstTab = this.isFirstTab(this.dragTarget);
+    const isLastTab = this.isLastTab(this.dragTarget);
+    const isLeftmostTab = this.isRTL() ? isLastTab : isFirstTab;
+    const isRightmostTab = this.isRTL() ? isFirstTab : isLastTab;
+
+    if ((isLeftmostTab && distance < 0) || (isRightmostTab && distance > 0)) {
       // If the drag target is already edge of the tabs and the mouse will make the
       // element to move to same direction more, keep the position.
       distance = 0;
     }
 
     this.dragTarget.style.left = `${ distance }px`;
     this.previousPageX = e.pageX;
   }
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1439,19 +1439,16 @@ nsIDocument::nsIDocument()
     mDOMCompleteSet(false),
     mAutoFocusFired(false),
     mScrolledToRefAlready(false),
     mChangeScrollPosWhenScrollingToRef(false),
     mHasWarnedAboutBoxObjects(false),
     mDelayFrameLoaderInitialization(false),
     mSynchronousDOMContentLoaded(false),
     mMaybeServiceWorkerControlled(false),
-    mValidWidth(false),
-    mValidHeight(false),
-    mAutoSize(false),
     mAllowZoom(false),
     mValidScaleFloat(false),
     mValidMaxScale(false),
     mScaleStrEmpty(false),
     mWidthStrEmpty(false),
     mParserAborted(false),
     mReportedUseCounters(false),
     mHasReportedShadowDOMUsage(false),
@@ -7432,38 +7429,16 @@ nsIDocument::GetViewportInfo(const Scree
 
     GetHeaderData(nsGkAtoms::viewport_height, heightStr);
     GetHeaderData(nsGkAtoms::viewport_width, widthStr);
 
     // Parse width and height properties
     // This function sets m{Min,Max}{Width,Height}.
     ParseWidthAndHeightInMetaViewport(widthStr, heightStr, scaleStr);
 
-    mAutoSize = false;
-    if (mMaxWidth == nsViewportInfo::DeviceSize) {
-      mAutoSize = true;
-    }
-    if (widthStr.IsEmpty() &&
-        (mMaxHeight == nsViewportInfo::DeviceSize ||
-         (mScaleFloat.scale == 1.0)))
-    {
-      mAutoSize = true;
-    }
-
-    // If width or height has not been set to a valid number by this point,
-    // fall back to a default value.
-    mValidWidth = (!widthStr.IsEmpty() && mMaxWidth > 0);
-    mValidHeight = (!heightStr.IsEmpty() && mMaxHeight > 0);
-
-    // If the width is set to some unrecognized value, and there is no
-    // height set, treat it as if device-width were specified.
-    if ((!mValidWidth && !widthStr.IsEmpty()) && !mValidHeight) {
-      mAutoSize = true;
-    }
-
     mAllowZoom = true;
     nsAutoString userScalable;
     GetHeaderData(nsGkAtoms::viewport_user_scalable, userScalable);
 
     if ((userScalable.EqualsLiteral("0")) ||
         (userScalable.EqualsLiteral("no")) ||
         (userScalable.EqualsLiteral("false"))) {
       mAllowZoom = false;
@@ -7603,17 +7578,26 @@ nsIDocument::GetViewportInfo(const Scree
     MOZ_ASSERT(width != nsViewportInfo::Auto && height != nsViewportInfo::Auto);
 
     CSSSize size(width, height);
 
     CSSToScreenScale scaleFloat = mScaleFloat * layoutDeviceScale;
     CSSToScreenScale scaleMinFloat = effectiveMinScale * layoutDeviceScale;
     CSSToScreenScale scaleMaxFloat = effectiveMaxScale * layoutDeviceScale;
 
-    if (mAutoSize) {
+    const bool autoSize =
+      mMaxWidth == nsViewportInfo::DeviceSize ||
+      (mWidthStrEmpty &&
+       (mMaxHeight == nsViewportInfo::DeviceSize ||
+        mScaleFloat.scale == 1.0f)) ||
+      (!mWidthStrEmpty && mMaxWidth == nsViewportInfo::Auto && mMaxHeight < 0);
+
+    // FIXME: Resolving width and height should be done above 'Resolve width
+    // value' and 'Resolve height value'.
+    if (autoSize) {
       size = displaySize;
     }
 
     size.width = clamped(size.width, float(kViewportMinSize.width), float(kViewportMaxSize.width));
 
     // Also recalculate the default zoom, if it wasn't specified in the metadata,
     // and the width is specified.
     if (mScaleStrEmpty && !mWidthStrEmpty) {
@@ -7631,17 +7615,17 @@ nsIDocument::GetViewportInfo(const Scree
       size.height = std::max(size.height, displaySize.height);
     } else if (effectiveValidMaxScale) {
       CSSSize displaySize = ScreenSize(aDisplaySize) / scaleMaxFloat;
       size.width = std::max(size.width, displaySize.width);
       size.height = std::max(size.height, displaySize.height);
     }
 
     return nsViewportInfo(scaleFloat, scaleMinFloat, scaleMaxFloat, size,
-                          mAutoSize, effectiveAllowZoom);
+                          autoSize, effectiveAllowZoom);
   }
 }
 
 void
 nsIDocument::UpdateViewportOverflowType(nscoord aScrolledWidth,
                                         nscoord aScrollportWidth)
 {
 #ifdef DEBUG
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -2861,17 +2861,17 @@ nsFocusManager::DetermineElementToMoveFo
     }
   }
 
   // Check if the starting content is the same as the content assigned to the
   // retargetdocumentfocus attribute. Is so, we don't want to start searching
   // from there but instead from the beginning of the document. Otherwise, the
   // content that appears before the retargetdocumentfocus element will never
   // get checked as it will be skipped when the focus is retargetted to it.
-  if (forDocumentNavigation && doc->IsXULDocument()) {
+  if (forDocumentNavigation && nsContentUtils::IsChromeDoc(doc)) {
     nsAutoString retarget;
 
     if (rootContent->GetAttr(kNameSpaceID_None,
                              nsGkAtoms::retargetdocumentfocus, retarget)) {
       nsIContent* retargetElement = doc->GetElementById(retarget);
       // The common case here is the urlbar where focus is on the anonymous
       // input inside the textbox, but the retargetdocumentfocus attribute
       // refers to the textbox. The Contains check will return false and the
@@ -4042,17 +4042,17 @@ nsresult
 nsFocusManager::FocusFirst(Element* aRootElement, nsIContent** aNextContent)
 {
   if (!aRootElement) {
     return NS_OK;
   }
 
   nsIDocument* doc = aRootElement->GetComposedDoc();
   if (doc) {
-    if (doc->IsXULDocument()) {
+    if (nsContentUtils::IsChromeDoc(doc)) {
       // If the redirectdocumentfocus attribute is set, redirect the focus to a
       // specific element. This is primarily used to retarget the focus to the
       // urlbar during document navigation.
       nsAutoString retarget;
 
       if (aRootElement->GetAttr(kNameSpaceID_None,
                                nsGkAtoms::retargetdocumentfocus, retarget)) {
         nsCOMPtr<Element> element = doc->GetElementById(retarget);
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -4313,19 +4313,16 @@ protected:
   bool mSynchronousDOMContentLoaded: 1;
 
   // Set to true when the document is possibly controlled by the ServiceWorker.
   // Used to prevent multiple requests to ServiceWorkerManager.
   bool mMaybeServiceWorkerControlled: 1;
 
   // These member variables cache information about the viewport so we don't
   // have to recalculate it each time.
-  bool mValidWidth: 1;
-  bool mValidHeight: 1;
-  bool mAutoSize: 1;
   bool mAllowZoom: 1;
   bool mValidScaleFloat: 1;
   bool mValidMaxScale: 1;
   bool mScaleStrEmpty: 1;
   bool mWidthStrEmpty: 1;
 
   // Parser aborted. True if the parser of this document was forcibly
   // terminated instead of letting it finish at its own pace.
--- a/dom/base/nsViewportInfo.cpp
+++ b/dom/base/nsViewportInfo.cpp
@@ -8,20 +8,16 @@
 #include "mozilla/Assertions.h"
 #include <algorithm>
 
 using namespace mozilla;
 
 void
 nsViewportInfo::ConstrainViewportValues()
 {
-  // Constrain the min/max zoom as specified at:
-  // dev.w3.org/csswg/css-device-adapt section 6.2
-  mMaxZoom = std::max(mMinZoom, mMaxZoom);
-
   if (mDefaultZoom > mMaxZoom) {
     mDefaultZoomValid = false;
     mDefaultZoom = mMaxZoom;
   }
   if (mDefaultZoom < mMinZoom) {
     mDefaultZoomValid = false;
     mDefaultZoom = mMinZoom;
   }
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -687,16 +687,25 @@ skip-if = !e10s # Track Bug 1281415
 [test_meta_viewport0.html]
 [test_meta_viewport1.html]
 [test_meta_viewport2.html]
 [test_meta_viewport3.html]
 [test_meta_viewport4.html]
 [test_meta_viewport5.html]
 [test_meta_viewport6.html]
 [test_meta_viewport7.html]
+[test_meta_viewport_auto_size_by_device_height.html]
+[test_meta_viewport_auto_size_by_device_width.html]
+[test_meta_viewport_auto_size_by_fixed_height_and_initial_scale_1.html]
+[test_meta_viewport_auto_size_by_fixed_width_and_device_height.html]
+[test_meta_viewport_auto_size_by_fixed_width_and_initial_scale_1.html]
+[test_meta_viewport_auto_size_by_initial_scale_0_5.html]
+[test_meta_viewport_auto_size_by_initial_scale_1.html]
+[test_meta_viewport_auto_size_by_invalid_width.html]
+[test_meta_viewport_auto_size_by_invalid_width_and_fixed_height.html]
 [test_meta_viewport_device_width.html]
 [test_meta_viewport_device_width_with_initial_scale_0_5.html]
 [test_meta_viewport_device_width_with_initial_scale_2.html]
 [test_meta_viewport_initial_scale_0_5.html]
 [test_meta_viewport_initial_scale_2.html]
 [test_meta_viewport_maximum_scale_0.html]
 [test_meta_viewport_maximum_scale_0_5.html]
 [test_meta_viewport_maximum_scale_2.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_meta_viewport_auto_size_by_device_height.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>device-height enables autoSize</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="height=device-height">
+  <script src="viewport_helpers.js"></script>
+</head>
+<body>
+  <p>height=device-height</p>
+  <script type="application/javascript">
+    "use strict";
+
+    add_task(async function device_height() {
+      await SpecialPowers.pushPrefEnv(scaleRatio(1.0));
+
+      let info = getViewportInfo(800, 480);
+      ok(info.autoSize, "device-height should enable autoSize");
+    });
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_meta_viewport_auto_size_by_device_width.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>device-width enables autoSize</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="width=device-width">
+  <script src="viewport_helpers.js"></script>
+</head>
+<body>
+  <p>width=device-width</p>
+  <script type="application/javascript">
+    "use strict";
+
+    add_task(async function device_width() {
+      await SpecialPowers.pushPrefEnv(scaleRatio(1.0));
+
+      let info = getViewportInfo(800, 480);
+      ok(info.autoSize, "device-width should enable autoSize");
+    });
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_meta_viewport_auto_size_by_fixed_height_and_initial_scale_1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>initial-scale=1 with fixed height enable autoSize</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="height=400, initial-scale=1">
+  <script src="viewport_helpers.js"></script>
+</head>
+<body>
+  <p>height=400, initial-scale=1</p>
+  <script type="application/javascript">
+    "use strict";
+
+    add_task(async function fixed_height_and_initial_scale_1() {
+      await SpecialPowers.pushPrefEnv(scaleRatio(1.0));
+
+      let info = getViewportInfo(800, 480);
+      ok(info.autoSize,
+         "initial-scale=1 with fixed height should enable autoSize");
+    });
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_meta_viewport_auto_size_by_fixed_width_and_device_height.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Fixed width and device-height disables autoSize</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="width=400,height=device-height">
+  <script src="viewport_helpers.js"></script>
+</head>
+<body>
+  <p>width=400, height=device-height</p>
+  <script type="application/javascript">
+    "use strict";
+
+    add_task(async function fixed_width_and_device_height() {
+      await SpecialPowers.pushPrefEnv(scaleRatio(1.0));
+
+      let info = getViewportInfo(800, 480);
+      ok(!info.autoSize,
+         "Fixed width should disable autoSize even if height is device-height");
+    });
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_meta_viewport_auto_size_by_fixed_width_and_initial_scale_1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>initial-scale=1 with fixed width disables autoSize</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="width=400, initial-scale=1">
+  <script src="viewport_helpers.js"></script>
+</head>
+<body>
+  <p>width=400, initial-scale=1</p>
+  <script type="application/javascript">
+    "use strict";
+
+    add_task(async function fixed_width_and_initial_scale_1() {
+      await SpecialPowers.pushPrefEnv(scaleRatio(1.0));
+
+      let info = getViewportInfo(800, 480);
+      ok(!info.autoSize,
+         "initial-scale=1 with fixed width should disable autoSize");
+    });
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_meta_viewport_auto_size_by_initial_scale_0_5.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>initial-scale!=1 without width disables autoSize</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="initial-scale=0.5">
+  <script src="viewport_helpers.js"></script>
+</head>
+<body>
+  <p>initial-scale!=1</p>
+  <script type="application/javascript">
+    "use strict";
+
+    add_task(async function initial_scale_0_5() {
+      await SpecialPowers.pushPrefEnv(scaleRatio(1.0));
+
+      let info = getViewportInfo(800, 480);
+      ok(!info.autoSize,
+         "initial-scale!=1 without width should disable autoSize");
+    });
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_meta_viewport_auto_size_by_initial_scale_1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>initial-scale=1 without width enables autoSize</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="initial-scale=1">
+  <script src="viewport_helpers.js"></script>
+</head>
+<body>
+  <p>initial-scale=1</p>
+  <script type="application/javascript">
+    "use strict";
+
+    add_task(async function initial_scale_1() {
+      await SpecialPowers.pushPrefEnv(scaleRatio(1.0));
+
+      let info = getViewportInfo(800, 480);
+      ok(info.autoSize,
+         "initial-scale=1 without width should enable autoSize");
+    });
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_meta_viewport_auto_size_by_invalid_width.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>invalid width enables autoSize</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="width=-1">
+  <script src="viewport_helpers.js"></script>
+</head>
+<body>
+  <p>width=-1</p>
+  <script type="application/javascript">
+    "use strict";
+
+    add_task(async function invalid_width() {
+      await SpecialPowers.pushPrefEnv(scaleRatio(1.0));
+
+      let info = getViewportInfo(800, 480);
+      ok(info.autoSize, "invalid width should enable autoSize");
+    });
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_meta_viewport_auto_size_by_invalid_width_and_fixed_height.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>invalid width but with fixed height disables autoSize</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <meta name="viewport" content="width=-1,height=200">
+  <script src="viewport_helpers.js"></script>
+</head>
+<body>
+  <p>width=-1,height=200</p>
+  <script type="application/javascript">
+    "use strict";
+
+    add_task(async function invalid_width_and_fixed_height() {
+      await SpecialPowers.pushPrefEnv(scaleRatio(1.0));
+
+      let info = getViewportInfo(800, 480);
+      ok(!info.autoSize,
+         "invalid width but with valid height should disable autoSize");
+    });
+  </script>
+</body>
+</html>
--- a/dom/chrome-webidl/Flex.webidl
+++ b/dom/chrome-webidl/Flex.webidl
@@ -66,19 +66,28 @@ interface FlexLineValues
 
   /**
    * getItems() returns FlexItemValues only for the Elements in
    * this Flex container -- ignoring struts and abs-pos Elements.
    */
   sequence<FlexItemValues> getItems();
 };
 
+/**
+ * Item main sizes have either been unclamped, clamped to the minimum,
+ * or clamped to the maximum.
+ */
+enum FlexItemClampState {
+  "unclamped", "clamped_to_min", "clamped_to_max"
+};
+
 [ChromeOnly]
 interface FlexItemValues
 {
   readonly attribute Node? node;
   readonly attribute double mainBaseSize;
   readonly attribute double mainDeltaSize;
   readonly attribute double mainMinSize;
   readonly attribute double mainMaxSize;
   readonly attribute double crossMinSize;
   readonly attribute double crossMaxSize;
+  readonly attribute FlexItemClampState clampState;
 };
--- a/dom/flex/FlexItemValues.cpp
+++ b/dom/flex/FlexItemValues.cpp
@@ -55,16 +55,18 @@ FlexItemValues::FlexItemValues(FlexLineV
   mMainDeltaSize = nsPresContext::AppUnitsToDoubleCSSPixels(
     aItem->mMainDeltaSize);
   mMainMinSize = nsPresContext::AppUnitsToDoubleCSSPixels(
     aItem->mMainMinSize);
   mMainMaxSize = ToPossiblyUnconstrainedPixels(aItem->mMainMaxSize);
   mCrossMinSize = nsPresContext::AppUnitsToDoubleCSSPixels(
     aItem->mCrossMinSize);
   mCrossMaxSize = ToPossiblyUnconstrainedPixels(aItem->mCrossMaxSize);
+
+  mClampState = aItem->mClampState;
 }
 
 JSObject*
 FlexItemValues::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return FlexItemValues_Binding::Wrap(aCx, this, aGivenProto);
 }
 
@@ -105,10 +107,16 @@ FlexItemValues::CrossMinSize() const
 }
 
 double
 FlexItemValues::CrossMaxSize() const
 {
   return mCrossMaxSize;
 }
 
+FlexItemClampState
+FlexItemValues::ClampState() const
+{
+  return mClampState;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/flex/FlexItemValues.h
+++ b/dom/flex/FlexItemValues.h
@@ -40,26 +40,28 @@ public:
 
   nsINode* GetNode() const;
   double MainBaseSize() const;
   double MainDeltaSize() const;
   double MainMinSize() const;
   double MainMaxSize() const;
   double CrossMinSize() const;
   double CrossMaxSize() const;
+  FlexItemClampState ClampState() const;
 
 protected:
   RefPtr<FlexLineValues> mParent;
   RefPtr<nsINode> mNode;
 
   // These sizes are all CSS pixel units.
   double mMainBaseSize;
   double mMainDeltaSize;
   double mMainMinSize;
   double mMainMaxSize;
   double mCrossMinSize;
   double mCrossMaxSize;
+  FlexItemClampState mClampState;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_FlexItemValues_h */
--- a/dom/flex/test/chrome.ini
+++ b/dom/flex/test/chrome.ini
@@ -1,3 +1,4 @@
 [chrome/test_flex_axis_directions.html]
 [chrome/test_flex_items.html]
+[chrome/test_flex_item_clamp.html]
 [chrome/test_flex_lines.html]
new file mode 100644
--- /dev/null
+++ b/dom/flex/test/chrome/test_flex_item_clamp.html
@@ -0,0 +1,169 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+<style>
+f {
+  display: flex;
+  background-color: grey;
+  width: 400px;
+  height: 20px;
+  margin-bottom: 5px;
+}
+
+b {
+  flex-basis: 100px;
+  flex-grow: 1;
+  flex-shrink: 1;
+  background-color: gold;
+}
+
+c {
+  flex-basis: 100px;
+  flex-grow: 1;
+  flex-shrink: 1;
+  background-color: yellow;
+}
+
+d {
+  flex: none;
+  background-color: orange;
+}
+
+b::after, c::after, d::after {
+  content: "";
+  display: block;
+  width: 10px;
+  height: 10px;
+  border: 1px solid teal;
+}
+
+
+.min50 {
+  min-width: 50px;
+}
+.min370 {
+  min-width: 370px;
+}
+.min400 {
+  min-width: 400px;
+}
+
+.max5 {
+  max-width: 5px;
+}
+.max50 {
+  max-width: 50px;
+}
+
+</style>
+
+<script>
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+const TEXT_NODE = Node.TEXT_NODE;
+
+function testItemMatchesExpectedValues(item, values, index) {
+  is(item.clampState, values.cs, "Item index " + index + " should have expected clampState.");
+}
+
+function runTests() {
+  /**
+   * The expectedValues array contains one element for each flex container child of
+   * of the body. The values in this object are compared against the returned flex
+   * API values of the first flex item in the first line of the corresponding flex
+   * container. The "cs" value is compared against the flex item's clampState.
+   **/
+  const expectedValues = [
+    { cs: "unclamped" },
+    { cs: "unclamped" },
+    { cs: "unclamped" },
+    { cs: "unclamped" },
+
+    { cs: "clamped_to_min" },
+    { cs: "clamped_to_min" },
+    { cs: "clamped_to_min" },
+    { cs: "clamped_to_min" },
+    { cs: "clamped_to_min" },
+
+    { cs: "clamped_to_max" },
+    { cs: "clamped_to_max" },
+    { cs: "clamped_to_max" },
+    { cs: "clamped_to_max" },
+    { cs: "clamped_to_max" },
+  ];
+
+  let children = document.body.children;
+  is(children.length, expectedValues.length, "Document should have expected number of flex containers.");
+
+  for (let i = 0; i < children.length; ++i) {
+    const flex = children.item(i).getAsFlexContainer();
+    ok(flex, "Document child index " + i + " should be a flex container.");
+    if (flex) {
+      const values = expectedValues[i];
+      const item = flex.getLines()[0].getItems()[0];
+      testItemMatchesExpectedValues(item, values, i);
+    }
+  }
+
+  SimpleTest.finish();
+}
+</script>
+</head>
+
+<body onLoad="runTests();">
+  <!-- unclamped cases -->
+  <!-- a flex:none item -->
+  <f><d></d></f>
+
+  <!-- a flex-grow item with room to grow -->
+  <f><b class="min370"></b></f>
+
+  <!-- a flex-shrink item with room to shrink -->
+  <f><b class="max50"></b><c class="min370"></c></f>
+
+  <!-- a flex-grow basis 100px item paired with a basis 200px item, where the second item is clamped,
+       and the first item then can grow past its minimum -->
+  <f><b style="min-width: 170px"></b><c class="max50" style="flex-basis:200px"></c></f>
+
+
+  <!-- clamped_to_min cases -->
+  <!-- a flex-grow item with a min smaller than the container -->
+  <f><b class="min370"></b><c></c></f>
+
+  <!-- a flex-shrink item with a min, paired with another that in total exceeds the container -->
+  <f><b class="min50"></b><c class="min370"></c></f>
+
+  <!-- a flex-shrink item shrunk to its (content-based) automatic minimum size -->
+  <f><b></b><c class="min400"></c></f>
+
+  <!-- a flex:none item with a min that is larger than its flex base size -->
+  <f><d class="min50"></d><c></c></f>
+
+  <!-- a flex-grow item paired with another flex-grow item that have equal-sized violations of
+       the first item's min with the second item's max -->
+  <f><b style="min-width: 200px"></b><c style="flex-basis:150px; max-width:200px"></c></f>
+
+
+  <!-- clamped_to_max cases -->
+  <!-- a flexible item with a max -->
+  <f><b class="max50"></b></f>
+
+  <!-- a flexible item with a max, paired with another flex-grow item -->
+  <f><b class="max50"></b><c></c></f>
+
+  <!-- a flexible item with a max smaller than its content size -->
+  <f><b class="max5"></b><c></c></f>
+
+  <!-- a flex:none item with a max smaller than its content size -->
+  <f><d class="max5"></d><c></c></f>
+
+  <!-- a flex-grow item paired with another flex-grow item that have equal-sized violations of
+       the first item's max with the second item's min -->
+  <f><b style="flex-basis:150px; max-width:200px"></b><c style="min-width: 200px"></c></f>
+</body>
+</html>
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -588,16 +588,18 @@ public:
   ScreenPoint GetCurrentMousePosition() const;
 
   /**
    * Process a movement of the dynamic toolbar by |aDeltaY| over the time
    * period from |aStartTimestampMs| to |aEndTimestampMs|.
    * This is used to track velocities accurately in the presence of movement
    * of the dynamic toolbar, since in such cases the finger can be moving
    * relative to the screen even though no scrolling is occurring.
+   * Note that this function expects "spatial coordinates" (i.e. toolbar
+   * moves up --> negative delta).
    */
   void ProcessDynamicToolbarMovement(uint32_t aStartTimestampMs,
                                      uint32_t aEndTimestampMs,
                                      ScreenCoord aDeltaY);
 private:
   typedef bool (*GuidComparator)(const ScrollableLayerGuid&, const ScrollableLayerGuid&);
 
   /* Helpers */
--- a/gfx/layers/apz/src/AndroidDynamicToolbarAnimator.cpp
+++ b/gfx/layers/apz/src/AndroidDynamicToolbarAnimator.cpp
@@ -623,17 +623,17 @@ AndroidDynamicToolbarAnimator::ProcessTo
 
     uint32_t timeDelta = aTimeStamp - mControllerLastEventTimeStamp;
     if (mControllerLastEventTimeStamp && timeDelta && aDelta) {
       // we can't use mApz because we're on the controller thread, so we have
       // the caller provide a RefPtr to the same underlying object, which should
       // be safe to use.
       aApz->ProcessDynamicToolbarMovement(mControllerLastEventTimeStamp,
                                           aTimeStamp,
-                                          -(float)aDelta);
+                                          (float)aDelta);
     }
   }
 
   return status;
 }
 
 void
 AndroidDynamicToolbarAnimator::HandleTouchEnd(StaticToolbarState aCurrentToolbarState, ScreenIntCoord aCurrentTouch)
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -3083,17 +3083,21 @@ ParentLayerPoint AsyncPanZoomController:
   }
   if (mY.CanScroll()) {
     mY.SetVelocity(mY.GetVelocity() + aHandoffState.mVelocity.y);
     residualVelocity.y = 0;
   }
 
   // If we're not scrollable in at least one of the directions in which we
   // were handed velocity, don't start a fling animation.
-  if (GetVelocityVector().Length() < gfxPrefs::APZFlingMinVelocityThreshold()) {
+  // The |IsFinite()| condition should only fail when running some tests
+  // that generate events faster than the clock resolution.
+  ParentLayerPoint velocity = GetVelocityVector();
+  if (!velocity.IsFinite() ||
+      velocity.Length() < gfxPrefs::APZFlingMinVelocityThreshold()) {
     // Relieve overscroll now if needed, since we will not transition to a fling
     // animation and then an overscroll animation, and relieve it then.
     aHandoffState.mChain->SnapBackOverscrolledApzc(this);
     return residualVelocity;
   }
 
   // If there's a scroll snap point near the predicted fling destination,
   // scroll there using a smooth scroll animation. Otherwise, start a
--- a/gfx/layers/apz/src/SimpleVelocityTracker.cpp
+++ b/gfx/layers/apz/src/SimpleVelocityTracker.cpp
@@ -73,17 +73,20 @@ SimpleVelocityTracker::AddPosition(Paren
 
 float
 SimpleVelocityTracker::HandleDynamicToolbarMovement(uint32_t aStartTimestampMs,
                                                     uint32_t aEndTimestampMs,
                                                     ParentLayerCoord aDelta)
 {
   float timeDelta = aEndTimestampMs - aStartTimestampMs;
   MOZ_ASSERT(timeDelta != 0);
-  float velocity = aDelta / timeDelta;
+  // Negate the delta to convert from spatial coordinates (e.g. toolbar
+  // has moved up --> negative delta) to scroll coordinates (e.g. toolbar
+  // has moved up --> scroll offset is increasing).
+  float velocity = -aDelta / timeDelta;
   velocity = ApplyFlingCurveToVelocity(velocity);
   mVelocitySampleTimeMs = aEndTimestampMs;
 
   AddVelocityToQueue(aEndTimestampMs, velocity);
   return velocity;
 }
 
 Maybe<float>
--- a/gfx/vr/VRManager.cpp
+++ b/gfx/vr/VRManager.cpp
@@ -16,22 +16,16 @@
 #include "mozilla/layers/TextureHost.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/Unused.h"
 #include "mozilla/gfx/GPUParent.h"
 
 #include "gfxPrefs.h"
 #include "gfxVR.h"
 #include "gfxVRExternal.h"
-#if defined(XP_WIN)
-#include "gfxVROculus.h"
-#endif
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
-#include "gfxVROpenVR.h"
-#endif
 
 #include "gfxVRPuppet.h"
 #include "ipc/VRLayerParent.h"
 #if !defined(MOZ_WIDGET_ANDROID)
 #include "service/VRService.h"
 #endif
 
 using namespace mozilla;
@@ -80,30 +74,16 @@ VRManager::VRManager()
   , mVRServiceStarted(false)
   , mTaskInterval(0)
 {
   MOZ_COUNT_CTOR(VRManager);
   MOZ_ASSERT(sVRManagerSingleton == nullptr);
 
   RefPtr<VRSystemManager> mgr;
 
-  /**
-   * We must add the VRDisplayManager's to mManagers in a careful order to
-   * ensure that we don't detect the same VRDisplay from multiple API's.
-   *
-   * Oculus comes first, as it will only enumerate Oculus HMD's and is the
-   * native interface for Oculus HMD's.
-   *
-   * OpenvR comes second, as it is the native interface for HTC Vive
-   * which is the most common HMD at this time.
-   *
-   * OSVR will be used if Oculus SDK and OpenVR don't detect any HMDS,
-   * to support everyone else.
-   */
-
 #if !defined(MOZ_WIDGET_ANDROID)
   // The VR Service accesses all hardware from a separate process
   // and replaces the other VRSystemManager when enabled.
   if (!gfxPrefs::VRProcessEnabled()) {
     mVRService = VRService::Create();
   } else if (gfxPrefs::VRProcessEnabled() && XRE_IsGPUProcess()) {
     gfx::GPUParent* gpu = GPUParent::GetSingleton();
     MOZ_ASSERT(gpu);
@@ -119,36 +99,16 @@ VRManager::VRManager()
 
   if (!mExternalManager) {
     mExternalManager = VRSystemManagerExternal::Create();
     if (mExternalManager) {
       mManagers.AppendElement(mExternalManager);
     }
   }
 
-#if defined(XP_WIN)
-  if (!mVRService) {
-    // The Oculus runtime is supported only on Windows
-    mgr = VRSystemManagerOculus::Create();
-    if (mgr) {
-      mManagers.AppendElement(mgr);
-    }
-  }
-#endif
-
-#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID))
-  if (!mVRService) {
-    // OpenVR is cross platform compatible
-    mgr = VRSystemManagerOpenVR::Create();
-    if (mgr) {
-      mManagers.AppendElement(mgr);
-    }
-  } // !mVRService
-#endif
-
   // Enable gamepad extensions while VR is enabled.
   // Preference only can be set at the Parent process.
   if (XRE_IsParentProcess() && gfxPrefs::VREnabled()) {
     Preferences::SetBool("dom.gamepad.extensions.enabled", true);
   }
 }
 
 VRManager::~VRManager()
deleted file mode 100644
--- a/gfx/vr/gfxVROculus.cpp
+++ /dev/null
@@ -1,2067 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef XP_WIN
-#error "Oculus 1.3 runtime support only available for Windows"
-#endif
-
-#include <math.h>
-
-
-#include "prenv.h"
-#include "gfxPrefs.h"
-#include "nsString.h"
-#include "mozilla/DebugOnly.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/SharedLibrary.h"
-#include "mozilla/TimeStamp.h"
-#include "mozilla/gfx/DeviceManagerDx.h"
-#include "mozilla/layers/CompositorThread.h"
-#include "ipc/VRLayerParent.h"
-
-#include "mozilla/gfx/Quaternion.h"
-
-#include <d3d11.h>
-#include "CompositorD3D11.h"
-#include "TextureD3D11.h"
-
-#include "gfxVROculus.h"
-#include "VRManagerParent.h"
-#include "VRThread.h"
-
-#include "mozilla/dom/GamepadEventTypes.h"
-#include "mozilla/dom/GamepadBinding.h"
-#include "mozilla/Telemetry.h"
-
-/** XXX The DX11 objects and quad blitting could be encapsulated
- *    into a separate object if either Oculus starts supporting
- *     non-Windows platforms or the blit is needed by other HMD\
- *     drivers.
- *     Alternately, we could remove the extra blit for
- *     Oculus as well with some more refactoring.
- */
-
-// See CompositorD3D11Shaders.h
-namespace mozilla {
-namespace layers {
-struct ShaderBytes { const void* mData; size_t mLength; };
-extern ShaderBytes sRGBShader;
-extern ShaderBytes sLayerQuadVS;
-} // namespace layers
-} // namespace mozilla
-#ifndef M_PI
-# define M_PI 3.14159265358979323846
-#endif
-
-using namespace mozilla;
-using namespace mozilla::gfx;
-using namespace mozilla::gfx::impl;
-using namespace mozilla::layers;
-using namespace mozilla::dom;
-
-namespace {
-
-static pfn_ovr_Initialize ovr_Initialize = nullptr;
-static pfn_ovr_Shutdown ovr_Shutdown = nullptr;
-static pfn_ovr_GetLastErrorInfo ovr_GetLastErrorInfo = nullptr;
-static pfn_ovr_GetVersionString ovr_GetVersionString = nullptr;
-static pfn_ovr_TraceMessage ovr_TraceMessage = nullptr;
-static pfn_ovr_IdentifyClient ovr_IdentifyClient = nullptr;
-static pfn_ovr_GetHmdDesc ovr_GetHmdDesc = nullptr;
-static pfn_ovr_GetTrackerCount ovr_GetTrackerCount = nullptr;
-static pfn_ovr_GetTrackerDesc ovr_GetTrackerDesc = nullptr;
-static pfn_ovr_Create ovr_Create = nullptr;
-static pfn_ovr_Destroy ovr_Destroy = nullptr;
-static pfn_ovr_GetSessionStatus ovr_GetSessionStatus = nullptr;
-static pfn_ovr_IsExtensionSupported ovr_IsExtensionSupported = nullptr;
-static pfn_ovr_EnableExtension ovr_EnableExtension = nullptr;
-static pfn_ovr_SetTrackingOriginType ovr_SetTrackingOriginType = nullptr;
-static pfn_ovr_GetTrackingOriginType ovr_GetTrackingOriginType = nullptr;
-static pfn_ovr_RecenterTrackingOrigin ovr_RecenterTrackingOrigin = nullptr;
-static pfn_ovr_SpecifyTrackingOrigin ovr_SpecifyTrackingOrigin = nullptr;
-static pfn_ovr_ClearShouldRecenterFlag ovr_ClearShouldRecenterFlag = nullptr;
-static pfn_ovr_GetTrackingState ovr_GetTrackingState = nullptr;
-static pfn_ovr_GetDevicePoses ovr_GetDevicePoses = nullptr;
-static pfn_ovr_GetTrackerPose ovr_GetTrackerPose = nullptr;
-static pfn_ovr_GetInputState ovr_GetInputState = nullptr;
-static pfn_ovr_GetConnectedControllerTypes ovr_GetConnectedControllerTypes = nullptr;
-static pfn_ovr_GetTouchHapticsDesc ovr_GetTouchHapticsDesc = nullptr;
-static pfn_ovr_SetControllerVibration ovr_SetControllerVibration = nullptr;
-static pfn_ovr_SubmitControllerVibration ovr_SubmitControllerVibration = nullptr;
-static pfn_ovr_GetControllerVibrationState ovr_GetControllerVibrationState = nullptr;
-static pfn_ovr_TestBoundary ovr_TestBoundary = nullptr;
-static pfn_ovr_TestBoundaryPoint ovr_TestBoundaryPoint = nullptr;
-static pfn_ovr_SetBoundaryLookAndFeel ovr_SetBoundaryLookAndFeel = nullptr;
-static pfn_ovr_ResetBoundaryLookAndFeel ovr_ResetBoundaryLookAndFeel = nullptr;
-static pfn_ovr_GetBoundaryGeometry ovr_GetBoundaryGeometry = nullptr;
-static pfn_ovr_GetBoundaryDimensions ovr_GetBoundaryDimensions = nullptr;
-static pfn_ovr_GetBoundaryVisible ovr_GetBoundaryVisible = nullptr;
-static pfn_ovr_RequestBoundaryVisible ovr_RequestBoundaryVisible = nullptr;
-static pfn_ovr_GetTextureSwapChainLength ovr_GetTextureSwapChainLength = nullptr;
-static pfn_ovr_GetTextureSwapChainCurrentIndex ovr_GetTextureSwapChainCurrentIndex = nullptr;
-static pfn_ovr_GetTextureSwapChainDesc ovr_GetTextureSwapChainDesc = nullptr;
-static pfn_ovr_CommitTextureSwapChain ovr_CommitTextureSwapChain = nullptr;
-static pfn_ovr_DestroyTextureSwapChain ovr_DestroyTextureSwapChain = nullptr;
-static pfn_ovr_DestroyMirrorTexture ovr_DestroyMirrorTexture = nullptr;
-static pfn_ovr_GetFovTextureSize ovr_GetFovTextureSize = nullptr;
-static pfn_ovr_GetRenderDesc2 ovr_GetRenderDesc2 = nullptr;
-static pfn_ovr_WaitToBeginFrame ovr_WaitToBeginFrame = nullptr;
-static pfn_ovr_BeginFrame ovr_BeginFrame = nullptr;
-static pfn_ovr_EndFrame ovr_EndFrame = nullptr;
-static pfn_ovr_SubmitFrame ovr_SubmitFrame = nullptr;
-static pfn_ovr_GetPerfStats ovr_GetPerfStats = nullptr;
-static pfn_ovr_ResetPerfStats ovr_ResetPerfStats = nullptr;
-static pfn_ovr_GetPredictedDisplayTime ovr_GetPredictedDisplayTime = nullptr;
-static pfn_ovr_GetTimeInSeconds ovr_GetTimeInSeconds = nullptr;
-static pfn_ovr_GetBool ovr_GetBool = nullptr;
-static pfn_ovr_SetBool ovr_SetBool = nullptr;
-static pfn_ovr_GetInt ovr_GetInt = nullptr;
-static pfn_ovr_SetInt ovr_SetInt = nullptr;
-static pfn_ovr_GetFloat ovr_GetFloat = nullptr;
-static pfn_ovr_SetFloat ovr_SetFloat = nullptr;
-static pfn_ovr_GetFloatArray ovr_GetFloatArray = nullptr;
-static pfn_ovr_SetFloatArray ovr_SetFloatArray = nullptr;
-static pfn_ovr_GetString ovr_GetString = nullptr;
-static pfn_ovr_SetString ovr_SetString = nullptr;
-static pfn_ovr_GetExternalCameras ovr_GetExternalCameras = nullptr;
-static pfn_ovr_SetExternalCameraProperties ovr_SetExternalCameraProperties = nullptr;
-
-#ifdef XP_WIN
-static pfn_ovr_CreateTextureSwapChainDX ovr_CreateTextureSwapChainDX = nullptr;
-static pfn_ovr_GetTextureSwapChainBufferDX ovr_GetTextureSwapChainBufferDX = nullptr;
-static pfn_ovr_CreateMirrorTextureDX ovr_CreateMirrorTextureDX = nullptr;
-static pfn_ovr_GetMirrorTextureBufferDX ovr_GetMirrorTextureBufferDX = nullptr;
-#endif
-
-static pfn_ovr_CreateTextureSwapChainGL ovr_CreateTextureSwapChainGL = nullptr;
-static pfn_ovr_GetTextureSwapChainBufferGL ovr_GetTextureSwapChainBufferGL = nullptr;
-static pfn_ovr_CreateMirrorTextureGL ovr_CreateMirrorTextureGL = nullptr;
-static pfn_ovr_GetMirrorTextureBufferGL ovr_GetMirrorTextureBufferGL = nullptr;
-
-#ifdef HAVE_64BIT_BUILD
-#define BUILD_BITS 64
-#else
-#define BUILD_BITS 32
-#endif
-
-#define OVR_PRODUCT_VERSION 1
-#define OVR_MAJOR_VERSION   1
-#define OVR_MINOR_VERSION   19
-
-enum class OculusLeftControllerButtonType : uint16_t {
-  LThumb,
-  IndexTrigger,
-  HandTrigger,
-  Button_X,
-  Button_Y,
-  LThumbRest,
-  NumButtonType
-};
-
-enum class OculusRightControllerButtonType : uint16_t {
-  RThumb,
-  IndexTrigger,
-  HandTrigger,
-  Button_A,
-  Button_B,
-  RThumbRest,
-  NumButtonType
-};
-
-static const uint32_t kNumOculusButton = static_cast<uint32_t>
-                                         (OculusLeftControllerButtonType::
-                                         NumButtonType);
-static const uint32_t kNumOculusHaptcs = 1;
-/*
-ovrFovPort
-ToFovPort(const VRFieldOfView& aFOV)
-{
-  ovrFovPort fovPort;
-  fovPort.LeftTan = tan(aFOV.leftDegrees * M_PI / 180.0);
-  fovPort.RightTan = tan(aFOV.rightDegrees * M_PI / 180.0);
-  fovPort.UpTan = tan(aFOV.upDegrees * M_PI / 180.0);
-  fovPort.DownTan = tan(aFOV.downDegrees * M_PI / 180.0);
-  return fovPort;
-}
-*/
-VRFieldOfView
-FromFovPort(const ovrFovPort& aFOV)
-{
-  VRFieldOfView fovInfo;
-  fovInfo.leftDegrees = atan(aFOV.LeftTan) * 180.0 / M_PI;
-  fovInfo.rightDegrees = atan(aFOV.RightTan) * 180.0 / M_PI;
-  fovInfo.upDegrees = atan(aFOV.UpTan) * 180.0 / M_PI;
-  fovInfo.downDegrees = atan(aFOV.DownTan) * 180.0 / M_PI;
-  return fovInfo;
-}
-
-} // namespace
-
-VROculusSession::VROculusSession()
-  : mOvrLib(nullptr)
-  , mSession(nullptr)
-  , mInitFlags((ovrInitFlags)0)
-  , mTextureSet(nullptr)
-  , mRequestPresentation(false)
-  , mRequestTracking(false)
-  , mDrawBlack(false)
-  , mIsConnected(false)
-  , mIsMounted(false)
-{
-}
-
-ovrSession
-VROculusSession::Get()
-{
-  MOZ_ASSERT(mSession);
-  return mSession;
-}
-
-bool
-VROculusSession::IsTrackingReady() const
-{
-  // We should return true only if the HMD is connected and we
-  // are ready for tracking
-  MOZ_ASSERT(!mIsConnected || mSession);
-  return mIsConnected;
-}
-
-bool
-VROculusSession::IsPresentationReady() const
-{
-  return !mRenderTargets.IsEmpty();
-}
-
-bool
-VROculusSession::IsMounted() const
-{
-  return mIsMounted;
-}
-
-void
-VROculusSession::StopTracking()
-{
-  if (mRequestTracking) {
-    mRequestTracking = false;
-    Refresh();
-  }
-}
-
-void
-VROculusSession::StartTracking()
-{
-  if (!mRequestTracking) {
-    mRequestTracking = true;
-    Refresh();
-  }
-}
-
-void
-VROculusSession::StartPresentation(const IntSize& aSize)
-{
-  if (!mRequestPresentation) {
-    mRequestPresentation = true;
-    mTelemetry.Clear();
-    mTelemetry.mPresentationStart = TimeStamp::Now();
-
-    ovrPerfStats perfStats;
-    if (ovr_GetPerfStats(mSession, &perfStats) == ovrSuccess) {
-      if (perfStats.FrameStatsCount) {
-        mTelemetry.mLastDroppedFrameCount = perfStats.FrameStats[0].AppDroppedFrameCount;
-      }
-    }
-  }
-
-  // Update the size, even when we are already presenting.
-  mPresentationSize = aSize;
-  Refresh();
-}
-
-void
-VROculusSession::StopPresentation()
-{
-  if (mRequestPresentation) {
-    mLastPresentationEnd = TimeStamp::Now();
-    mRequestPresentation = false;
-
-    const TimeDuration duration = mLastPresentationEnd - mTelemetry.mPresentationStart;
-    Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, 1);
-    Telemetry::Accumulate(Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_OCULUS,
-                          duration.ToMilliseconds());
-
-    if (mTelemetry.IsLastDroppedFrameValid() && duration.ToSeconds()) {
-      ovrPerfStats perfStats;
-      if (ovr_GetPerfStats(mSession, &perfStats) == ovrSuccess) {
-        if (perfStats.FrameStatsCount) {
-          const uint32_t droppedFramesPerSec = (perfStats.FrameStats[0].AppDroppedFrameCount -
-                                                mTelemetry.mLastDroppedFrameCount) / duration.ToSeconds();
-          Telemetry::Accumulate(Telemetry::WEBVR_DROPPED_FRAMES_IN_OCULUS, droppedFramesPerSec);
-        }
-      }
-    }
-    Refresh();
-  }
-}
-
-VROculusSession::~VROculusSession()
-{
-  mSubmitThread = nullptr;
-  Uninitialize();
-}
-
-void
-VROculusSession::Uninitialize()
-{
-  StopRendering();
-  StopSession();
-  StopLib();
-  UnloadOvrLib();
-}
-
-void
-VROculusSession::StopRendering()
-{
-  if (!mRenderTargets.IsEmpty()) {
-    mRenderTargets.Clear();
-  }
-  if (mTextureSet && mSession) {
-    ovr_DestroyTextureSwapChain(mSession, mTextureSet);
-  }
-  mTextureSet = nullptr;
-  mDevice = nullptr;
-}
-
-void
-VROculusSession::StopSession()
-{
-  if (mSession) {
-    ovr_Destroy(mSession);
-    mIsConnected = false;
-    mIsMounted = false;
-    mSession = nullptr;
-  }
-}
-
-void
-VROculusSession::StopLib()
-{
-  if (mInitFlags) {
-    ovr_Shutdown();
-    mInitFlags = (ovrInitFlags)0;
-  }
-}
-
-void
-VROculusSession::Refresh(bool aForceRefresh)
-{
-  // We are waiting for drawing the black layer command for
-  // Compositor thread. Ignore Refresh() calls from other threads.
-  if (mDrawBlack && !aForceRefresh) {
-    return;
-  }
-
-  if (!mRequestTracking) {
-    Uninitialize();
-    return;
-  }
-
-  ovrInitFlags flags = (ovrInitFlags)(ovrInit_RequestVersion | ovrInit_MixedRendering);
-  bool bInvisible = true;
-  if (!gfxPrefs::VROculusInvisibleEnabled()) {
-    bInvisible = false;
-  }
-  if (mRequestPresentation) {
-    bInvisible = false;
-  } else if (!mLastPresentationEnd.IsNull()) {
-    TimeDuration duration = TimeStamp::Now() - mLastPresentationEnd;
-    TimeDuration timeout = TimeDuration::FromMilliseconds(gfxPrefs::VROculusPresentTimeout());
-    if (timeout > TimeDuration(0) && duration < timeout) {
-      // Do not immediately re-initialize with an invisible session after
-      // the end of a VR presentation.  Waiting for the configured duraction
-      // ensures that the user will not drop to Oculus Home during VR link
-      // traversal.
-      bInvisible = false;
-
-      // While we are waiting for either the timeout or a new presentation,
-      // fill the HMD with black / no layers.
-      if (mSession && mTextureSet) {
-        if (!aForceRefresh) {
-          // VROculusSession didn't start submitting frames yet.
-          // Or, the VR thread has been shut down already.
-          if (!mSubmitThread || !mSubmitThread->IsActive()) {
-            return;
-          }
-          // ovr_SubmitFrame is running at VR Submit thread,
-          // so we post this task to VR Submit thread and let it paint
-          // a black frame.
-          mDrawBlack = true;
-          mSubmitThread->PostTask(NewRunnableMethod<bool>(
-            "gfx::VROculusSession::Refresh",
-            this,
-            &VROculusSession::Refresh, true));
-          return;
-        }
-        ovrLayerEyeFov layer;
-        memset(&layer, 0, sizeof(layer));
-        layer.Header.Type = ovrLayerType_Disabled;
-        ovrLayerHeader *layers = &layer.Header;
-        ovr_SubmitFrame(mSession, 0, nullptr, &layers, 1);
-        mDrawBlack = false;
-      }
-    }
-  }
-  if (bInvisible) {
-    flags = (ovrInitFlags)(flags | ovrInit_Invisible);
-  }
-
-  if (mInitFlags != flags) {
-    Uninitialize();
-  }
-
-  if(!Initialize(flags)) {
-    // If we fail to initialize, ensure the Oculus libraries
-    // are unloaded, as we can't poll for ovrSessionStatus::ShouldQuit
-    // without an active ovrSession.
-    Uninitialize();
-  }
-
-  if (mSession) {
-    ovrSessionStatus status;
-    if (OVR_SUCCESS(ovr_GetSessionStatus(mSession, &status))) {
-      mIsConnected = status.HmdPresent;
-      mIsMounted = status.HmdMounted;
-      if (status.ShouldQuit) {
-        mLastShouldQuit = TimeStamp::Now();
-        Uninitialize();
-      }
-    } else {
-      mIsConnected = false;
-      mIsMounted = false;
-    }
-  }
-}
-
-bool
-VROculusSession::IsQuitTimeoutActive()
-{
-  // If Oculus asked us to quit our session, do not try to initialize again
-  // immediately.
-  if (!mLastShouldQuit.IsNull()) {
-    TimeDuration duration = TimeStamp::Now() - mLastShouldQuit;
-    TimeDuration timeout = TimeDuration::FromMilliseconds(gfxPrefs::VROculusQuitTimeout());
-    if (timeout > TimeDuration(0) && duration < timeout) {
-      return true;
-    }
-  }
-  return false;
-}
-
-bool
-VROculusSession::Initialize(ovrInitFlags aFlags)
-{
-  if (IsQuitTimeoutActive()) {
-    return false;
-  }
-
-  if (!LoadOvrLib()) {
-    return false;
-  }
-  if (!StartLib(aFlags)) {
-    return false;
-  }
-  if (!StartSession()) {
-    return false;
-  }
-  if (!StartRendering()) {
-    return false;
-  }
-  return true;
-}
-
-bool
-VROculusSession::StartRendering()
-{
-  if (!mRequestPresentation) {
-    // Nothing to do if we aren't presenting
-    return true;
-  }
-  if (!mDevice) {
-    mDevice = gfx::DeviceManagerDx::Get()->GetVRDevice();
-    if (!mDevice) {
-      NS_WARNING("Failed to get a D3D11Device for Oculus");
-      return false;
-    }
-  }
-
-  if (!mTextureSet) {
-    /**
-    * The presentation format is determined by content, which describes the
-    * left and right eye rectangles in the VRLayer.  The default, if no
-    * coordinates are passed is to place the left and right eye textures
-    * side-by-side within the buffer.
-    *
-    * XXX - An optimization would be to dynamically resize this buffer
-    *       to accomodate sites that are choosing to render in a lower
-    *       resolution or are using space outside of the left and right
-    *       eye textures for other purposes.  (Bug 1291443)
-    */
-
-    ovrTextureSwapChainDesc desc;
-    memset(&desc, 0, sizeof(desc));
-    desc.Type = ovrTexture_2D;
-    desc.ArraySize = 1;
-    desc.Format = OVR_FORMAT_B8G8R8A8_UNORM_SRGB;
-    desc.Width = mPresentationSize.width;
-    desc.Height = mPresentationSize.height;
-    desc.MipLevels = 1;
-    desc.SampleCount = 1;
-    desc.StaticImage = false;
-    desc.MiscFlags = ovrTextureMisc_DX_Typeless;
-    desc.BindFlags = ovrTextureBind_DX_RenderTarget;
-
-    ovrResult orv = ovr_CreateTextureSwapChainDX(mSession, mDevice, &desc, &mTextureSet);
-    if (orv != ovrSuccess) {
-      NS_WARNING("ovr_CreateTextureSwapChainDX failed");
-      return false;
-    }
-  }
-
-  if (mTextureSet && mRenderTargets.IsEmpty()) {
-    int textureCount = 0;
-    ovrResult orv = ovr_GetTextureSwapChainLength(mSession, mTextureSet, &textureCount);
-    if (orv != ovrSuccess) {
-      NS_WARNING("ovr_GetTextureSwapChainLength failed");
-      return false;
-    }
-    mRenderTargets.SetLength(textureCount);
-    for (int i = 0; i < textureCount; ++i) {
-      RefPtr<CompositingRenderTargetD3D11> rt;
-      ID3D11Texture2D* texture = nullptr;
-      orv = ovr_GetTextureSwapChainBufferDX(mSession, mTextureSet, i, IID_PPV_ARGS(&texture));
-      MOZ_ASSERT(orv == ovrSuccess, "ovr_GetTextureSwapChainBufferDX failed.");
-      rt = new CompositingRenderTargetD3D11(texture, IntPoint(0, 0), DXGI_FORMAT_B8G8R8A8_UNORM);
-      rt->SetSize(mPresentationSize);
-      mRenderTargets[i] = rt;
-      texture->Release();
-    }
-  }
-  return true;
-}
-
-bool
-VROculusSession::StartLib(ovrInitFlags aFlags)
-{
-  if (mInitFlags == 0) {
-    ovrInitParams params;
-    memset(&params, 0, sizeof(params));
-    params.Flags = aFlags;
-    params.RequestedMinorVersion = OVR_MINOR_VERSION;
-    params.LogCallback = nullptr;
-    params.ConnectionTimeoutMS = 0;
-
-    ovrResult orv = ovr_Initialize(&params);
-
-    if (orv == ovrSuccess) {
-      mInitFlags = aFlags;
-    }
-    else {
-      return false;
-    }
-  }
-  MOZ_ASSERT(mInitFlags == aFlags);
-  return true;
-}
-
-bool
-VROculusSession::StartSession()
-{
-  // ovr_Create can be slow when no HMD is present and we wish
-  // to keep the same oculus session when possible, so we detect
-  // presence of an HMD with ovr_GetHmdDesc before calling ovr_Create
-  ovrHmdDesc desc = ovr_GetHmdDesc(NULL);
-  if (desc.Type == ovrHmd_None) {
-    // No HMD connected, destroy any existing session
-    if (mSession) {
-      ovr_Destroy(mSession);
-      mSession = nullptr;
-    }
-    return false;
-  }
-  if (mSession != nullptr) {
-    // HMD Detected and we already have a session, let's keep using it.
-    return true;
-  }
-
-  // HMD Detected and we don't have a session yet,
-  // try to create a new session
-  ovrSession session;
-  ovrGraphicsLuid luid;
-  ovrResult orv = ovr_Create(&session, &luid);
-  if (orv == ovrSuccess) {
-    orv = ovr_SetTrackingOriginType(session, ovrTrackingOrigin_FloorLevel);
-    if (orv != ovrSuccess) {
-      NS_WARNING("ovr_SetTrackingOriginType failed.\n");
-    }
-    mSession = session;
-    return true;
-  }
-
-  // Failed to create a session for the HMD
-  return false;
-}
-
-bool
-VROculusSession::LoadOvrLib()
-{
-  if (mOvrLib) {
-    // Already loaded, early exit
-    return true;
-  }
-#if defined(_WIN32)
-  nsTArray<nsString> libSearchPaths;
-  nsString libName;
-  nsString searchPath;
-
-  for (;;) {
-    UINT requiredLength = ::GetSystemDirectoryW(char16ptr_t(searchPath.BeginWriting()),
-                                                searchPath.Length());
-    if (!requiredLength) {
-      break;
-    }
-    if (requiredLength < searchPath.Length()) {
-      searchPath.Truncate(requiredLength);
-      libSearchPaths.AppendElement(searchPath);
-      break;
-    }
-    searchPath.SetLength(requiredLength);
-  }
-  libName.AppendPrintf("LibOVRRT%d_%d.dll", BUILD_BITS, OVR_PRODUCT_VERSION);
-
-  // search the path/module dir
-  libSearchPaths.InsertElementsAt(0, 1, EmptyString());
-
-  // If the env var is present, we override libName
-  if (_wgetenv(L"OVR_LIB_PATH")) {
-    searchPath = _wgetenv(L"OVR_LIB_PATH");
-    libSearchPaths.InsertElementsAt(0, 1, searchPath);
-  }
-
-  if (_wgetenv(L"OVR_LIB_NAME")) {
-    libName = _wgetenv(L"OVR_LIB_NAME");
-  }
-
-  for (uint32_t i = 0; i < libSearchPaths.Length(); ++i) {
-    nsString& libPath = libSearchPaths[i];
-    nsString fullName;
-    if (libPath.Length() == 0) {
-      fullName.Assign(libName);
-    } else {
-      fullName.Assign(libPath + NS_LITERAL_STRING(u"\\") + libName);
-    }
-
-    mOvrLib = LoadLibraryWithFlags(fullName.get());
-    if (mOvrLib) {
-      break;
-    }
-  }
-#else
-#error "Unsupported platform!"
-#endif
-
-  if (!mOvrLib) {
-    return false;
-  }
-
-#define REQUIRE_FUNCTION(_x) do { \
-    *(void **)&_x = (void *) PR_FindSymbol(mOvrLib, #_x);                \
-    if (!_x) { printf_stderr(#_x " symbol missing\n"); goto fail; }       \
-  } while (0)
-
-  REQUIRE_FUNCTION(ovr_Initialize);
-  REQUIRE_FUNCTION(ovr_Shutdown);
-  REQUIRE_FUNCTION(ovr_GetLastErrorInfo);
-  REQUIRE_FUNCTION(ovr_GetVersionString);
-  REQUIRE_FUNCTION(ovr_TraceMessage);
-  REQUIRE_FUNCTION(ovr_IdentifyClient);
-  REQUIRE_FUNCTION(ovr_GetHmdDesc);
-  REQUIRE_FUNCTION(ovr_GetTrackerCount);
-  REQUIRE_FUNCTION(ovr_GetTrackerDesc);
-  REQUIRE_FUNCTION(ovr_Create);
-  REQUIRE_FUNCTION(ovr_Destroy);
-  REQUIRE_FUNCTION(ovr_GetSessionStatus);
-  REQUIRE_FUNCTION(ovr_IsExtensionSupported);
-  REQUIRE_FUNCTION(ovr_EnableExtension);
-  REQUIRE_FUNCTION(ovr_SetTrackingOriginType);
-  REQUIRE_FUNCTION(ovr_GetTrackingOriginType);
-  REQUIRE_FUNCTION(ovr_RecenterTrackingOrigin);
-  REQUIRE_FUNCTION(ovr_SpecifyTrackingOrigin);
-  REQUIRE_FUNCTION(ovr_ClearShouldRecenterFlag);
-  REQUIRE_FUNCTION(ovr_GetTrackingState);
-  REQUIRE_FUNCTION(ovr_GetDevicePoses);
-  REQUIRE_FUNCTION(ovr_GetTrackerPose);
-  REQUIRE_FUNCTION(ovr_GetInputState);
-  REQUIRE_FUNCTION(ovr_GetConnectedControllerTypes);
-  REQUIRE_FUNCTION(ovr_GetTouchHapticsDesc);
-  REQUIRE_FUNCTION(ovr_SetControllerVibration);
-  REQUIRE_FUNCTION(ovr_SubmitControllerVibration);
-  REQUIRE_FUNCTION(ovr_GetControllerVibrationState);
-  REQUIRE_FUNCTION(ovr_TestBoundary);
-  REQUIRE_FUNCTION(ovr_TestBoundaryPoint);
-  REQUIRE_FUNCTION(ovr_SetBoundaryLookAndFeel);
-  REQUIRE_FUNCTION(ovr_ResetBoundaryLookAndFeel);
-  REQUIRE_FUNCTION(ovr_GetBoundaryGeometry);
-  REQUIRE_FUNCTION(ovr_GetBoundaryDimensions);
-  REQUIRE_FUNCTION(ovr_GetBoundaryVisible);
-  REQUIRE_FUNCTION(ovr_RequestBoundaryVisible);
-  REQUIRE_FUNCTION(ovr_GetTextureSwapChainLength);
-  REQUIRE_FUNCTION(ovr_GetTextureSwapChainCurrentIndex);
-  REQUIRE_FUNCTION(ovr_GetTextureSwapChainDesc);
-  REQUIRE_FUNCTION(ovr_CommitTextureSwapChain);
-  REQUIRE_FUNCTION(ovr_DestroyTextureSwapChain);
-  REQUIRE_FUNCTION(ovr_DestroyMirrorTexture);
-  REQUIRE_FUNCTION(ovr_GetFovTextureSize);
-  REQUIRE_FUNCTION(ovr_GetRenderDesc2);
-  REQUIRE_FUNCTION(ovr_WaitToBeginFrame);
-  REQUIRE_FUNCTION(ovr_BeginFrame);
-  REQUIRE_FUNCTION(ovr_EndFrame);
-  REQUIRE_FUNCTION(ovr_SubmitFrame);
-  REQUIRE_FUNCTION(ovr_GetPerfStats);
-  REQUIRE_FUNCTION(ovr_ResetPerfStats);
-  REQUIRE_FUNCTION(ovr_GetPredictedDisplayTime);
-  REQUIRE_FUNCTION(ovr_GetTimeInSeconds);
-  REQUIRE_FUNCTION(ovr_GetBool);
-  REQUIRE_FUNCTION(ovr_SetBool);
-  REQUIRE_FUNCTION(ovr_GetInt);
-  REQUIRE_FUNCTION(ovr_SetInt);
-  REQUIRE_FUNCTION(ovr_GetFloat);
-  REQUIRE_FUNCTION(ovr_SetFloat);
-  REQUIRE_FUNCTION(ovr_GetFloatArray);
-  REQUIRE_FUNCTION(ovr_SetFloatArray);
-  REQUIRE_FUNCTION(ovr_GetString);
-  REQUIRE_FUNCTION(ovr_SetString);
-  REQUIRE_FUNCTION(ovr_GetExternalCameras);
-  REQUIRE_FUNCTION(ovr_SetExternalCameraProperties);
-
-#ifdef XP_WIN
-
-  REQUIRE_FUNCTION(ovr_CreateTextureSwapChainDX);
-  REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferDX);
-  REQUIRE_FUNCTION(ovr_CreateMirrorTextureDX);
-  REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferDX);
-
-#endif
-
-  REQUIRE_FUNCTION(ovr_CreateTextureSwapChainGL);
-  REQUIRE_FUNCTION(ovr_GetTextureSwapChainBufferGL);
-  REQUIRE_FUNCTION(ovr_CreateMirrorTextureGL);
-  REQUIRE_FUNCTION(ovr_GetMirrorTextureBufferGL);
-
-#undef REQUIRE_FUNCTION
-
-  return true;
-
- fail:
-  ovr_Initialize = nullptr;
-  PR_UnloadLibrary(mOvrLib);
-  mOvrLib = nullptr;
-  return false;
-}
-
-already_AddRefed<CompositingRenderTargetD3D11>
-VROculusSession::GetNextRenderTarget()
-{
-  int currentRenderTarget = 0;
-  DebugOnly<ovrResult> orv = ovr_GetTextureSwapChainCurrentIndex(mSession, mTextureSet, &currentRenderTarget);
-  MOZ_ASSERT(orv == ovrSuccess, "ovr_GetTextureSwapChainCurrentIndex failed.");
-
-  mRenderTargets[currentRenderTarget]->ClearOnBind();
-  RefPtr<CompositingRenderTargetD3D11> rt = mRenderTargets[currentRenderTarget];
-  return rt.forget();
-}
-
-ovrTextureSwapChain
-VROculusSession::GetSwapChain()
-{
-  MOZ_ASSERT(mTextureSet);
-  return mTextureSet;
-}
-
-void
-VROculusSession::UnloadOvrLib()
-{
-  if (mOvrLib) {
-    PR_UnloadLibrary(mOvrLib);
-    mOvrLib = nullptr;
-  }
-}
-
-VRDisplayOculus::VRDisplayOculus(VROculusSession* aSession)
-  : VRDisplayLocal(VRDeviceType::Oculus)
-  , mSession(aSession)
-  , mQuadVS(nullptr)
-  , mQuadPS(nullptr)
-  , mLinearSamplerState(nullptr)
-  , mVSConstantBuffer(nullptr)
-  , mPSConstantBuffer(nullptr)
-  , mVertexBuffer(nullptr)
-  , mInputLayout(nullptr)
-  , mEyeHeight(OVR_DEFAULT_EYE_HEIGHT)
-{
-  MOZ_COUNT_CTOR_INHERITED(VRDisplayOculus, VRDisplayLocal);
-  VRDisplayState& state = mDisplayInfo.mDisplayState;
-  strncpy(state.mDisplayName, "Oculus VR HMD", kVRDisplayNameMaxLen);
-  state.mIsConnected = true;
-  state.mIsMounted = false;
-
-  mDesc = ovr_GetHmdDesc(aSession->Get());
-
-  state.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None;
-  if (mDesc.AvailableTrackingCaps & ovrTrackingCap_Orientation) {
-    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Orientation;
-    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration;
-  }
-  if (mDesc.AvailableTrackingCaps & ovrTrackingCap_Position) {
-    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Position;
-    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
-    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_StageParameters;
-  }
-  state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_External;
-  state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
-  state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_Present;
-
-  mFOVPort[VRDisplayState::Eye_Left] = mDesc.DefaultEyeFov[ovrEye_Left];
-  mFOVPort[VRDisplayState::Eye_Right] = mDesc.DefaultEyeFov[ovrEye_Right];
-
-  state.mEyeFOV[VRDisplayState::Eye_Left] = FromFovPort(mFOVPort[VRDisplayState::Eye_Left]);
-  state.mEyeFOV[VRDisplayState::Eye_Right] = FromFovPort(mFOVPort[VRDisplayState::Eye_Right]);
-
-  float pixelsPerDisplayPixel = 1.0;
-  ovrSizei texSize[2];
-
-  // get eye texture sizes
-  for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
-    texSize[eye] = ovr_GetFovTextureSize(mSession->Get(), (ovrEyeType)eye, mFOVPort[eye], pixelsPerDisplayPixel);
-  }
-
-  // take the max of both for eye resolution
-  state.mEyeResolution.width = std::max(texSize[VRDisplayState::Eye_Left].w, texSize[VRDisplayState::Eye_Right].w);
-  state.mEyeResolution.height = std::max(texSize[VRDisplayState::Eye_Left].h, texSize[VRDisplayState::Eye_Right].h);
-
-  UpdateEyeParameters();
-  UpdateStageParameters();
-}
-
-VRDisplayOculus::~VRDisplayOculus() {
-  Destroy();
-  MOZ_COUNT_DTOR_INHERITED(VRDisplayOculus, VRDisplayLocal);
-}
-
-void
-VRDisplayOculus::Destroy()
-{
-  StopPresentation();
-  mSession = nullptr;
-}
-
-void
-VRDisplayOculus::UpdateEyeParameters(gfx::Matrix4x4* aHeadToEyeTransforms /* = nullptr */)
-{
-  // Note this must be called every frame, as the IPD adjustment can be changed
-  // by the user during a VR session.
-  for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
-    // As of Oculus 1.17 SDK, we must use the ovr_GetRenderDesc2 function to return the updated
-    // version of ovrEyeRenderDesc.  This is normally done by the Oculus static lib shim, but we
-    // need to do this explicitly as we are loading the Oculus runtime dll directly.
-    ovrEyeRenderDesc renderDesc = ovr_GetRenderDesc2(mSession->Get(), (ovrEyeType)eye, mFOVPort[eye]);
-    VRDisplayState& state = mDisplayInfo.mDisplayState;
-    state.mEyeTranslation[eye].x = renderDesc.HmdToEyePose.Position.x;
-    state.mEyeTranslation[eye].y = renderDesc.HmdToEyePose.Position.y;
-    state.mEyeTranslation[eye].z = renderDesc.HmdToEyePose.Position.z;
-    if (aHeadToEyeTransforms) {
-      Matrix4x4 pose;
-      pose.SetRotationFromQuaternion(gfx::Quaternion(renderDesc.HmdToEyePose.Orientation.x, renderDesc.HmdToEyePose.Orientation.y, renderDesc.HmdToEyePose.Orientation.z, renderDesc.HmdToEyePose.Orientation.w));
-      pose.PreTranslate(renderDesc.HmdToEyePose.Position.x, renderDesc.HmdToEyePose.Position.y, renderDesc.HmdToEyePose.Position.z);
-      pose.Invert();
-      aHeadToEyeTransforms[eye] = pose;
-    }
-  }
-}
-
-void
-VRDisplayOculus::UpdateStageParameters()
-{
-  if (!mSession->IsTrackingReady()) {
-    return;
-  }
-  VRDisplayState& state = mDisplayInfo.mDisplayState;
-  ovrVector3f playArea;
-  ovrResult res = ovr_GetBoundaryDimensions(mSession->Get(), ovrBoundary_PlayArea, &playArea);
-  if (res == ovrSuccess) {
-    state.mStageSize.width = playArea.x;
-    state.mStageSize.height = playArea.z;
-  } else {
-    // If we fail, fall back to reasonable defaults.
-    // 1m x 1m space
-    state.mStageSize.width = 1.0f;
-    state.mStageSize.height = 1.0f;
-  }
-
-  mEyeHeight = ovr_GetFloat(mSession->Get(), OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
-
-  state.mSittingToStandingTransform[0] = 1.0f;
-  state.mSittingToStandingTransform[1] = 0.0f;
-  state.mSittingToStandingTransform[2] = 0.0f;
-  state.mSittingToStandingTransform[3] = 0.0f;
-
-  state.mSittingToStandingTransform[4] = 0.0f;
-  state.mSittingToStandingTransform[5] = 1.0f;
-  state.mSittingToStandingTransform[6] = 0.0f;
-  state.mSittingToStandingTransform[7] = 0.0f;
-
-  state.mSittingToStandingTransform[8] = 0.0f;
-  state.mSittingToStandingTransform[9] = 0.0f;
-  state.mSittingToStandingTransform[10] = 1.0f;
-  state.mSittingToStandingTransform[11] = 0.0f;
-
-  state.mSittingToStandingTransform[12] = 0.0f;
-  state.mSittingToStandingTransform[13] = mEyeHeight;
-  state.mSittingToStandingTransform[14] = 0.0f;
-  state.mSittingToStandingTransform[15] = 1.0f;
-}
-
-void
-VRDisplayOculus::ZeroSensor()
-{
-  if (!mSession->IsTrackingReady()) {
-    return;
-  }
-  ovr_RecenterTrackingOrigin(mSession->Get());
-  UpdateStageParameters();
-}
-
-VRHMDSensorState
-VRDisplayOculus::GetSensorState()
-{
-  VRHMDSensorState result{};
-  if (mSession->IsTrackingReady()) {
-    gfx::Matrix4x4 headToEyeTransforms[2];
-    UpdateEyeParameters(headToEyeTransforms);
-    double predictedFrameTime = 0.0f;
-    if (gfxPrefs::VRPosePredictionEnabled()) {
-      // XXX We might need to call ovr_GetPredictedDisplayTime even if we don't use the result.
-      // If we don't call it, the Oculus driver will spew out many warnings...
-      predictedFrameTime = ovr_GetPredictedDisplayTime(mSession->Get(), 0);
-    }
-    result = GetSensorState(predictedFrameTime);
-    result.pose.position[1] -= mEyeHeight;
-    result.CalcViewMatrices(headToEyeTransforms);
-  }
-  result.inputFrameID = mDisplayInfo.mFrameId;
-
-  return result;
-}
-
-VRHMDSensorState
-VRDisplayOculus::GetSensorState(double absTime)
-{
-  VRHMDSensorState result{};
-
-  ovrTrackingState state = ovr_GetTrackingState(mSession->Get(), absTime, true);
-  ovrPoseStatef& pose(state.HeadPose);
-
-  result.timestamp = pose.TimeInSeconds;
-
-  if (state.StatusFlags & ovrStatus_OrientationTracked) {
-    result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
-
-    result.pose.orientation[0] = pose.ThePose.Orientation.x;
-    result.pose.orientation[1] = pose.ThePose.Orientation.y;
-    result.pose.orientation[2] = pose.ThePose.Orientation.z;
-    result.pose.orientation[3] = pose.ThePose.Orientation.w;
-
-    result.pose.angularVelocity[0] = pose.AngularVelocity.x;
-    result.pose.angularVelocity[1] = pose.AngularVelocity.y;
-    result.pose.angularVelocity[2] = pose.AngularVelocity.z;
-
-    result.flags |= VRDisplayCapabilityFlags::Cap_AngularAcceleration;
-
-    result.pose.angularAcceleration[0] = pose.AngularAcceleration.x;
-    result.pose.angularAcceleration[1] = pose.AngularAcceleration.y;
-    result.pose.angularAcceleration[2] = pose.AngularAcceleration.z;
-  } else {
-    // default to an identity quaternion
-    result.pose.orientation[3] = 1.0f;
-  }
-
-  if (state.StatusFlags & ovrStatus_PositionTracked) {
-    result.flags |= VRDisplayCapabilityFlags::Cap_Position;
-
-    result.pose.position[0] = pose.ThePose.Position.x;
-    result.pose.position[1] = pose.ThePose.Position.y;
-    result.pose.position[2] = pose.ThePose.Position.z;
-
-    result.pose.linearVelocity[0] = pose.LinearVelocity.x;
-    result.pose.linearVelocity[1] = pose.LinearVelocity.y;
-    result.pose.linearVelocity[2] = pose.LinearVelocity.z;
-
-    result.flags |= VRDisplayCapabilityFlags::Cap_LinearAcceleration;
-
-    result.pose.linearAcceleration[0] = pose.LinearAcceleration.x;
-    result.pose.linearAcceleration[1] = pose.LinearAcceleration.y;
-    result.pose.linearAcceleration[2] = pose.LinearAcceleration.z;
-  }
-  result.flags |= VRDisplayCapabilityFlags::Cap_External;
-  result.flags |= VRDisplayCapabilityFlags::Cap_MountDetection;
-  result.flags |= VRDisplayCapabilityFlags::Cap_Present;
-
-  return result;
-}
-
-void
-VRDisplayOculus::StartPresentation()
-{
-  if (!CreateD3DObjects()) {
-    return;
-  }
-  mSession->StartPresentation(IntSize(mDisplayInfo.mDisplayState.mEyeResolution.width * 2, mDisplayInfo.mDisplayState.mEyeResolution.height));
-  if (!mSession->IsPresentationReady()) {
-    return;
-  }
-
-  if (!mQuadVS) {
-    if (FAILED(mDevice->CreateVertexShader(sLayerQuadVS.mData, sLayerQuadVS.mLength, nullptr, &mQuadVS))) {
-      NS_WARNING("Failed to create vertex shader for Oculus");
-      return;
-    }
-  }
-
-  if (!mQuadPS) {
-    if (FAILED(mDevice->CreatePixelShader(sRGBShader.mData, sRGBShader.mLength, nullptr, &mQuadPS))) {
-      NS_WARNING("Failed to create pixel shader for Oculus");
-      return;
-    }
-  }
-
-  CD3D11_BUFFER_DESC cBufferDesc(sizeof(layers::VertexShaderConstants),
-    D3D11_BIND_CONSTANT_BUFFER,
-    D3D11_USAGE_DYNAMIC,
-    D3D11_CPU_ACCESS_WRITE);
-
-  if (!mVSConstantBuffer) {
-    if (FAILED(mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mVSConstantBuffer)))) {
-      NS_WARNING("Failed to vertex shader constant buffer for Oculus");
-      return;
-    }
-  }
-
-  if (!mPSConstantBuffer) {
-    cBufferDesc.ByteWidth = sizeof(layers::PixelShaderConstants);
-    if (FAILED(mDevice->CreateBuffer(&cBufferDesc, nullptr, getter_AddRefs(mPSConstantBuffer)))) {
-      NS_WARNING("Failed to pixel shader constant buffer for Oculus");
-      return;
-    }
-  }
-
-  if (!mLinearSamplerState) {
-    CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
-    if (FAILED(mDevice->CreateSamplerState(&samplerDesc, getter_AddRefs(mLinearSamplerState)))) {
-      NS_WARNING("Failed to create sampler state for Oculus");
-      return;
-    }
-  }
-
-  if (!mInputLayout) {
-    D3D11_INPUT_ELEMENT_DESC layout[] =
-    {
-      { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
-    };
-
-    if (FAILED(mDevice->CreateInputLayout(layout,
-                                              sizeof(layout) / sizeof(D3D11_INPUT_ELEMENT_DESC),
-                                              sLayerQuadVS.mData,
-                                              sLayerQuadVS.mLength,
-                                              getter_AddRefs(mInputLayout)))) {
-      NS_WARNING("Failed to create input layout for Oculus");
-      return;
-    }
-  }
-
-  if (!mVertexBuffer) {
-    Vertex vertices[] = { { { 0.0, 0.0 } },{ { 1.0, 0.0 } },{ { 0.0, 1.0 } },{ { 1.0, 1.0 } } };
-    CD3D11_BUFFER_DESC bufferDesc(sizeof(vertices), D3D11_BIND_VERTEX_BUFFER);
-    D3D11_SUBRESOURCE_DATA data;
-    data.pSysMem = (void*)vertices;
-
-    if (FAILED(mDevice->CreateBuffer(&bufferDesc, &data, getter_AddRefs(mVertexBuffer)))) {
-      NS_WARNING("Failed to create vertex buffer for Oculus");
-      return;
-    }
-  }
-
-  memset(&mVSConstants, 0, sizeof(mVSConstants));
-  memset(&mPSConstants, 0, sizeof(mPSConstants));
-}
-
-void
-VRDisplayOculus::StopPresentation()
-{
-  if (mSession) {
-    mSession->StopPresentation();
-  }
-}
-
-bool
-VRDisplayOculus::UpdateConstantBuffers()
-{
-  HRESULT hr;
-  D3D11_MAPPED_SUBRESOURCE resource;
-  resource.pData = nullptr;
-
-  hr = mContext->Map(mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
-  if (FAILED(hr) || !resource.pData) {
-    return false;
-  }
-  *(VertexShaderConstants*)resource.pData = mVSConstants;
-  mContext->Unmap(mVSConstantBuffer, 0);
-  resource.pData = nullptr;
-
-  hr = mContext->Map(mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
-  if (FAILED(hr) || !resource.pData) {
-    return false;
-  }
-  *(PixelShaderConstants*)resource.pData = mPSConstants;
-  mContext->Unmap(mPSConstantBuffer, 0);
-
-  ID3D11Buffer *buffer = mVSConstantBuffer;
-  mContext->VSSetConstantBuffers(0, 1, &buffer);
-  buffer = mPSConstantBuffer;
-  mContext->PSSetConstantBuffers(0, 1, &buffer);
-  return true;
-}
-
-bool
-VRDisplayOculus::SubmitFrame(ID3D11Texture2D* aSource,
-                             const IntSize& aSize,
-                             const gfx::Rect& aLeftEyeRect,
-                             const gfx::Rect& aRightEyeRect)
-{
-  MOZ_ASSERT(mSubmitThread->GetThread() == NS_GetCurrentThread());
-  if (!CreateD3DObjects()) {
-    return false;
-  }
-
-  AutoRestoreRenderState restoreState(this);
-  if (!restoreState.IsSuccess()) {
-    return false;
-  }
-
-  if (!mSession->IsPresentationReady()) {
-    return false;
-  }
-  /**
-    * XXX - We should resolve fail the promise returned by
-    *       VRDisplay.requestPresent() when the DX11 resources fail allocation
-    *       in VRDisplayOculus::StartPresentation().
-    *       Bailing out here prevents the crash but content should be aware
-    *       that frames are not being presented.
-    *       See Bug 1299309.
-    **/
-
-  RefPtr<CompositingRenderTargetD3D11> surface = mSession->GetNextRenderTarget();
-
-  surface->BindRenderTarget(mContext);
-
-  Matrix viewMatrix = Matrix::Translation(-1.0, 1.0);
-  viewMatrix.PreScale(2.0f / float(aSize.width), 2.0f / float(aSize.height));
-  viewMatrix.PreScale(1.0f, -1.0f);
-  Matrix4x4 projection = Matrix4x4::From2D(viewMatrix);
-  projection._33 = 0.0f;
-
-  Matrix transform2d;
-  gfx::Matrix4x4 transform = gfx::Matrix4x4::From2D(transform2d);
-
-  D3D11_VIEWPORT viewport;
-  viewport.MinDepth = 0.0f;
-  viewport.MaxDepth = 1.0f;
-  viewport.Width = aSize.width;
-  viewport.Height = aSize.height;
-  viewport.TopLeftX = 0;
-  viewport.TopLeftY = 0;
-
-  D3D11_RECT scissor;
-  scissor.left = 0;
-  scissor.right = aSize.width;
-  scissor.top = 0;
-  scissor.bottom = aSize.height;
-
-  memcpy(&mVSConstants.layerTransform, &transform._11, sizeof(mVSConstants.layerTransform));
-  memcpy(&mVSConstants.projection, &projection._11, sizeof(mVSConstants.projection));
-  mVSConstants.renderTargetOffset[0] = 0.0f;
-  mVSConstants.renderTargetOffset[1] = 0.0f;
-  mVSConstants.layerQuad = Rect(0.0f, 0.0f, aSize.width, aSize.height);
-  mVSConstants.textureCoords = Rect(0.0f, 1.0f, 1.0f, -1.0f);
-
-  mPSConstants.layerOpacity[0] = 1.0f;
-
-  ID3D11Buffer* vbuffer = mVertexBuffer;
-  UINT vsize = sizeof(Vertex);
-  UINT voffset = 0;
-  mContext->IASetVertexBuffers(0, 1, &vbuffer, &vsize, &voffset);
-  mContext->IASetIndexBuffer(nullptr, DXGI_FORMAT_R16_UINT, 0);
-  mContext->IASetInputLayout(mInputLayout);
-  mContext->RSSetViewports(1, &viewport);
-  mContext->RSSetScissorRects(1, &scissor);
-  mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
-  mContext->VSSetShader(mQuadVS, nullptr, 0);
-  mContext->PSSetShader(mQuadPS, nullptr, 0);
-
-  RefPtr<ID3D11ShaderResourceView> srView;
-  HRESULT hr = mDevice->CreateShaderResourceView(aSource, nullptr, getter_AddRefs(srView));
-  if (FAILED(hr)) {
-    gfxWarning() << "Could not create shader resource view for Oculus: " << hexa(hr);
-    return false;
-  }
-  ID3D11ShaderResourceView* viewPtr = srView.get();
-  mContext->PSSetShaderResources(0 /* 0 == TexSlot::RGB */, 1, &viewPtr);
-  // XXX Use Constant from TexSlot in CompositorD3D11.cpp?
-
-  ID3D11SamplerState *sampler = mLinearSamplerState;
-  mContext->PSSetSamplers(0, 1, &sampler);
-
-  if (!UpdateConstantBuffers()) {
-    NS_WARNING("Failed to update constant buffers for Oculus");
-    return false;
-  }
-
-  mContext->Draw(4, 0);
-
-  ovrResult orv = ovr_CommitTextureSwapChain(mSession->Get(), mSession->GetSwapChain());
-  if (orv != ovrSuccess) {
-    NS_WARNING("ovr_CommitTextureSwapChain failed.\n");
-    return false;
-  }
-
-  ovrLayerEyeFov layer;
-  memset(&layer, 0, sizeof(layer));
-  layer.Header.Type = ovrLayerType_EyeFov;
-  layer.Header.Flags = 0;
-  layer.ColorTexture[0] = mSession->GetSwapChain();
-  layer.ColorTexture[1] = nullptr;
-  layer.Fov[0] = mFOVPort[0];
-  layer.Fov[1] = mFOVPort[1];
-  layer.Viewport[0].Pos.x = aSize.width * aLeftEyeRect.X();
-  layer.Viewport[0].Pos.y = aSize.height * aLeftEyeRect.Y();
-  layer.Viewport[0].Size.w = aSize.width * aLeftEyeRect.Width();
-  layer.Viewport[0].Size.h = aSize.height * aLeftEyeRect.Height();
-  layer.Viewport[1].Pos.x = aSize.width * aRightEyeRect.X();
-  layer.Viewport[1].Pos.y = aSize.height * aRightEyeRect.Y();
-  layer.Viewport[1].Size.w = aSize.width * aRightEyeRect.Width();
-  layer.Viewport[1].Size.h = aSize.height * aRightEyeRect.Height();
-
-  const VRHMDSensorState& sensorState = mDisplayInfo.GetSensorState();
-  gfx::Matrix4x4 matView[2];
-  memcpy(matView[0].components, sensorState.leftViewMatrix, sizeof(sensorState.leftViewMatrix));
-  memcpy(matView[1].components, sensorState.rightViewMatrix, sizeof(sensorState.rightViewMatrix));
-
-  for (uint32_t i = 0; i < 2; ++i) {
-    Point3D eyeTranslation;
-    Quaternion eyeRotation;
-    Point3D eyeScale;
-    if (!matView[i].Decompose(eyeTranslation, eyeRotation, eyeScale)) {
-      NS_WARNING("Failed to decompose eye pose matrix for Oculus");
-    }
-    layer.RenderPose[i].Orientation.x = eyeRotation.x;
-    layer.RenderPose[i].Orientation.y = eyeRotation.y;
-    layer.RenderPose[i].Orientation.z = eyeRotation.z;
-    layer.RenderPose[i].Orientation.w = eyeRotation.w;
-    layer.RenderPose[i].Position.x = eyeTranslation.x;
-    layer.RenderPose[i].Position.y = eyeTranslation.y;
-    layer.RenderPose[i].Position.z = eyeTranslation.z;
-  }
-
-  ovrLayerHeader *layers = &layer.Header;
-  orv = ovr_SubmitFrame(mSession->Get(), mDisplayInfo.mFrameId, nullptr, &layers, 1);
-  // ovr_SubmitFrame will fail during the Oculus health and safety warning.
-  // and will start succeeding once the warning has been dismissed by the user.
-
-  if (!OVR_UNQUALIFIED_SUCCESS(orv)) {
-    /**
-     * We wish to throttle the framerate for any case that the rendered
-     * result is not visible.  In some cases, such as during the Oculus
-     * "health and safety warning", orv will be > 0 (OVR_SUCCESS but not
-     * OVR_UNQUALIFIED_SUCCESS) and ovr_SubmitFrame will not block.
-     * In this case, returning true would have resulted in an unthrottled
-     * render loop hiting excessive frame rates and consuming resources.
-     */
-    return false;
-  }
-
-  mSession->mSubmitThread = mSubmitThread;
-  return true;
-}
-
-void
-VRDisplayOculus::Refresh()
-{
-  mDisplayInfo.mDisplayState.mIsConnected = mSession->IsTrackingReady();
-  mDisplayInfo.mDisplayState.mIsMounted = mSession->IsMounted();
-}
-
-VRControllerOculus::VRControllerOculus(dom::GamepadHand aHand, uint32_t aDisplayID)
-  : VRControllerHost(VRDeviceType::Oculus, aHand, aDisplayID)
-  , mIndexTrigger(0.0f)
-  , mHandTrigger(0.0f)
-  , mVibrateThread(nullptr)
-  , mIsVibrateStopped(false)
-{
-  MOZ_COUNT_CTOR_INHERITED(VRControllerOculus, VRControllerHost);
-
-  VRControllerState& state = mControllerInfo.mControllerState;
-
-  char* touchID = (char*)"";
-  switch (aHand) {
-    case dom::GamepadHand::Left:
-      touchID = (char*)"Oculus Touch (Left)";
-      break;
-    case dom::GamepadHand::Right:
-      touchID = (char*)"Oculus Touch (Right)";
-      break;
-    default:
-      MOZ_ASSERT(false);
-      break;
-  }
-
-  strncpy(state.controllerName, touchID, kVRControllerNameMaxLen);
-
-  MOZ_ASSERT(kNumOculusButton ==
-             static_cast<uint32_t>(OculusLeftControllerButtonType::NumButtonType)
-             && kNumOculusButton ==
-             static_cast<uint32_t>(OculusRightControllerButtonType::NumButtonType));
-
-  state.numButtons = kNumOculusButton;
-  state.numAxes = static_cast<uint32_t>(
-                   OculusControllerAxisType::NumVRControllerAxisType);
-  state.numHaptics = kNumOculusHaptcs;
-}
-
-float
-VRControllerOculus::GetAxisMove(uint32_t aAxis)
-{
-  return mAxisMove[aAxis];
-}
-
-void
-VRControllerOculus::SetAxisMove(uint32_t aAxis, float aValue)
-{
-  mAxisMove[aAxis] = aValue;
-}
-
-float
-VRControllerOculus::GetIndexTrigger()
-{
-  return mIndexTrigger;
-}
-
-void
-VRControllerOculus::SetIndexTrigger(float aValue)
-{
-  mIndexTrigger = aValue;
-}
-
-float
-VRControllerOculus::GetHandTrigger()
-{
-  return mHandTrigger;
-}
-
-void
-VRControllerOculus::SetHandTrigger(float aValue)
-{
-  mHandTrigger = aValue;
-}
-
-VRControllerOculus::~VRControllerOculus()
-{
-  ShutdownVibrateHapticThread();
-  MOZ_COUNT_DTOR_INHERITED(VRControllerOculus, VRControllerHost);
-}
-
-void
-VRControllerOculus::UpdateVibrateHaptic(ovrSession aSession,
-                                        uint32_t aHapticIndex,
-                                        double aIntensity,
-                                        double aDuration,
-                                        uint64_t aVibrateIndex,
-                                        const VRManagerPromise& aPromise)
-{
-  // UpdateVibrateHaptic() only can be called by mVibrateThread
-  MOZ_ASSERT(mVibrateThread->GetThread() == NS_GetCurrentThread());
-
-  // It has been interrupted by loss focus.
-  if (mIsVibrateStopped) {
-    VibrateHapticComplete(aSession, aPromise, true);
-    return;
-  }
-  // Avoid the previous vibrate event to override the new one.
-  if (mVibrateIndex != aVibrateIndex) {
-    VibrateHapticComplete(aSession, aPromise, false);
-    return;
-  }
-
-  const double duration = (aIntensity == 0) ? 0 : aDuration;
-  // Vibration amplitude in the [0.0, 1.0] range.
-  const float amplitude = aIntensity > 1.0 ? 1.0 : aIntensity;
-  // Vibration is enabled by specifying the frequency.
-  // Specifying 0.0f will disable the vibration, 0.5f will vibrate at 160Hz,
-  // and 1.0f will vibrate at 320Hz.
-  const float frequency = (duration > 0) ? 1.0f : 0.0f;
-  ovrControllerType hand;
-
-  switch (GetHand()) {
-    case GamepadHand::Left:
-      hand = ovrControllerType::ovrControllerType_LTouch;
-      break;
-    case GamepadHand::Right:
-      hand = ovrControllerType::ovrControllerType_RTouch;
-      break;
-    default:
-      MOZ_ASSERT(false);
-      break;
-  }
-
-  // Oculus Touch only can get the response from ovr_SetControllerVibration()
-  // at the presenting mode.
-  ovrResult result = ovr_SetControllerVibration(aSession, hand, frequency,
-                                                (frequency == 0.0f) ? 0.0f : amplitude);
-  if (result != ovrSuccess) {
-    printf_stderr("%s hand ovr_SetControllerVibration skipped.\n",
-                  GamepadHandValues::strings[uint32_t(GetHand())].value);
-  }
-
-  // In Oculus dev doc, it mentions vibration lasts for a maximum of 2.5 seconds
-  // at ovr_SetControllerVibration(), but we found 2.450 sec is more close to the
-  // real looping use case.
-  const double kVibrateRate = 2450.0;
-  const double remainingTime = (duration > kVibrateRate)
-                                ? (duration - kVibrateRate) : duration;
-
-  if (remainingTime) {
-    MOZ_ASSERT(mVibrateThread);
-
-    RefPtr<Runnable> runnable =
-      NewRunnableMethod<ovrSession, uint32_t, double, double, uint64_t,
-        StoreCopyPassByConstLRef<VRManagerPromise>>(
-          "VRControllerOculus::UpdateVibrateHaptic",
-          this, &VRControllerOculus::UpdateVibrateHaptic, aSession,
-          aHapticIndex, aIntensity, (duration > kVibrateRate) ? remainingTime : 0, aVibrateIndex, aPromise);
-    mVibrateThread->PostDelayedTask(runnable.forget(), (duration > kVibrateRate) ? kVibrateRate : remainingTime);
-  } else {
-    VibrateHapticComplete(aSession, aPromise, true);
-  }
-}
-
-void
-VRControllerOculus::VibrateHapticComplete(ovrSession aSession, const VRManagerPromise& aPromise,
-                                          bool aStop)
-{
-  if (aStop) {
-    ovrControllerType hand;
-
-    switch (GetHand()) {
-      case GamepadHand::Left:
-        hand = ovrControllerType::ovrControllerType_LTouch;
-        break;
-      case GamepadHand::Right:
-        hand = ovrControllerType::ovrControllerType_RTouch;
-        break;
-      default:
-        MOZ_ASSERT(false);
-        break;
-    }
-
-    ovrResult result = ovr_SetControllerVibration(aSession, hand, 0.0f, 0.0f);
-    if (result != ovrSuccess) {
-      printf_stderr("%s Haptics skipped.\n",
-                    GamepadHandValues::strings[uint32_t(GetHand())].value);
-    }
-  }
-
-  VRManager *vm = VRManager::Get();
-  MOZ_ASSERT(vm);
-
-  CompositorThreadHolder::Loop()->PostTask(
-    NewRunnableMethod<StoreCopyPassByConstLRef<VRManagerPromise>>(
-      "VRManager::NotifyVibrateHapticCompleted",
-      vm, &VRManager::NotifyVibrateHapticCompleted, aPromise));
-}
-
-void
-VRControllerOculus::VibrateHaptic(ovrSession aSession,
-                                  uint32_t aHapticIndex,
-                                  double aIntensity,
-                                  double aDuration,
-                                  const VRManagerPromise& aPromise)
-{
-  // Spinning up the haptics thread at the first haptics call.
-  if (!mVibrateThread) {
-    mVibrateThread = new VRThread(NS_LITERAL_CSTRING("Oculus_Vibration"));
-  }
-  mVibrateThread->Start();
-  ++mVibrateIndex;
-  mIsVibrateStopped = false;
-
-  RefPtr<Runnable> runnable =
-    NewRunnableMethod<ovrSession, uint32_t, double, double, uint64_t,
-      StoreCopyPassByConstLRef<VRManagerPromise>>(
-        "VRControllerOculus::UpdateVibrateHaptic",
-        this, &VRControllerOculus::UpdateVibrateHaptic, aSession,
-        aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromise);
-  mVibrateThread->PostTask(runnable.forget());
-}
-
-void
-VRControllerOculus::StopVibrateHaptic()
-{
-  mIsVibrateStopped = true;
-}
-
-void
-VRControllerOculus::ShutdownVibrateHapticThread()
-{
-  StopVibrateHaptic();
-  if (mVibrateThread) {
-    mVibrateThread->Shutdown();
-    mVibrateThread = nullptr;
-  }
-}
-
-/*static*/ already_AddRefed<VRSystemManagerOculus>
-VRSystemManagerOculus::Create()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (!gfxPrefs::VREnabled() || !gfxPrefs::VROculusEnabled())
-  {
-    return nullptr;
-  }
-
-  RefPtr<VRSystemManagerOculus> manager = new VRSystemManagerOculus();
-  return manager.forget();
-}
-
-VRSystemManagerOculus::VRSystemManagerOculus()
-  : mSession(nullptr)
-{
-}
-
-void
-VRSystemManagerOculus::Destroy()
-{
-  Shutdown();
-  mSession = nullptr;
-}
-
-void
-VRSystemManagerOculus::Shutdown()
-{
-  if (mSession) {
-    mSession->StopTracking();
-  }
-  RemoveControllers();
-  if (mDisplay) {
-    mDisplay->Destroy();
-  }
-  mDisplay = nullptr;
-}
-
-void
-VRSystemManagerOculus::NotifyVSync()
-{
-  VRSystemManager::NotifyVSync();
-  if (!mSession) {
-    return;
-  }
-  mSession->Refresh();
-  if (mDisplay) {
-    mDisplay->Refresh();
-  }
-  // Detect disconnection
-  if (!mSession->IsTrackingReady()) {
-    // No HMD connected
-    mDisplay = nullptr;
-    // Prevent enumeration from continuously running
-    // after the headset is disconnected.
-    mSession->StopTracking();
-  }
-}
-
-bool
-VRSystemManagerOculus::ShouldInhibitEnumeration()
-{
-  if (VRSystemManager::ShouldInhibitEnumeration()) {
-    return true;
-  }
-  if (mDisplay) {
-    // When we find an Oculus VR device, don't
-    // allow any further enumeration as it
-    // may get picked up redundantly by other
-    // API's such as OpenVR.
-    return true;
-  }
-  if (mSession && mSession->IsQuitTimeoutActive()) {
-    // When we are responding to ShouldQuit, we return true here
-    // to prevent further enumeration by other VRSystemManager's such as
-    // VRSystemManagerOpenVR which would also enumerate the connected Oculus
-    // HMD, resulting in interference with the Oculus runtime software updates.
-    return true;
-  }
-  return false;
-}
-
-void
-VRSystemManagerOculus::Enumerate()
-{
-  if (!mSession) {
-    mSession = new VROculusSession();
-  }
-  mSession->StartTracking();
-  if (mDisplay == nullptr && mSession->IsTrackingReady()) {
-    // HMD Detected
-    mDisplay = new VRDisplayOculus(mSession);
-  }
-  if (mDisplay == nullptr) {
-    // Prevent enumeration from continuously running
-    // when a headset was not found.
-    mSession->StopTracking();
-  }
-}
-
-void
-VRSystemManagerOculus::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
-{
-  if (mDisplay) {
-    aHMDResult.AppendElement(mDisplay);
-  }
-}
-
-bool
-VRSystemManagerOculus::GetIsPresenting()
-{
-  if (mDisplay) {
-    VRDisplayInfo displayInfo(mDisplay->GetDisplayInfo());
-    return displayInfo.GetPresentingGroups() != 0;
-  }
-
-  return false;
-}
-
-void
-VRSystemManagerOculus::HandleInput()
-{
-  // The session is available after VRDisplay is created
-  // at GetHMDs().
-  if (!mSession || !mSession->IsTrackingReady()) {
-    return;
-  }
-
-  RefPtr<impl::VRControllerOculus> controller;
-  ovrInputState inputState;
-  uint32_t axis = 0;
-  const bool hasInputState = ovr_GetInputState(mSession->Get(), ovrControllerType_Touch,
-                                               &inputState) == ovrSuccess;
-
-  if (!hasInputState) {
-    return;
-  }
-
-  for (uint32_t i = 0; i < mOculusController.Length(); ++i) {
-    controller = mOculusController[i];
-    const GamepadHand hand = controller->GetHand();
-    const uint32_t handIdx = static_cast<uint32_t>(hand) - 1;
-    uint32_t buttonIdx = 0;
-    ovrHandType oculusHandType;
-
-    switch (hand) {
-      case dom::GamepadHand::Left:
-        oculusHandType = ovrHand_Left;
-        HandleButtonPress(i, buttonIdx, ovrButton_LThumb, inputState.Buttons,
-                          inputState.Touches);
-        ++buttonIdx;
-        HandleIndexTriggerPress(i, buttonIdx, inputState.IndexTrigger[handIdx], ovrTouch_LIndexTrigger, inputState.Touches);
-        ++buttonIdx;
-        HandleHandTriggerPress(i, buttonIdx, inputState.HandTrigger[handIdx]);
-        ++buttonIdx;
-        HandleButtonPress(i, buttonIdx, ovrButton_X, inputState.Buttons,
-                          inputState.Touches);
-        ++buttonIdx;
-        HandleButtonPress(i, buttonIdx, ovrButton_Y, inputState.Buttons,
-                          inputState.Touches);
-        ++buttonIdx;
-        HandleTouchEvent(i, buttonIdx, ovrTouch_LThumbRest, inputState.Touches);
-        ++buttonIdx;
-        break;
-      case dom::GamepadHand::Right:
-        oculusHandType = ovrHand_Right;
-        HandleButtonPress(i, buttonIdx, ovrButton_RThumb, inputState.Buttons,
-                          inputState.Touches);
-        ++buttonIdx;
-        HandleIndexTriggerPress(i, buttonIdx, inputState.IndexTrigger[handIdx], ovrTouch_RIndexTrigger, inputState.Touches);
-        ++buttonIdx;
-        HandleHandTriggerPress(i, buttonIdx, inputState.HandTrigger[handIdx]);
-        ++buttonIdx;
-        HandleButtonPress(i, buttonIdx, ovrButton_A, inputState.Buttons,
-                          inputState.Touches);
-        ++buttonIdx;
-        HandleButtonPress(i, buttonIdx, ovrButton_B, inputState.Buttons,
-                          inputState.Touches);
-        ++buttonIdx;
-        HandleTouchEvent(i, buttonIdx, ovrTouch_RThumbRest, inputState.Touches);
-        ++buttonIdx;
-        break;
-      default:
-        MOZ_ASSERT(false);
-        break;
-    }
-    controller->SetButtonPressed(inputState.Buttons);
-    controller->SetButtonTouched(inputState.Touches);
-
-    axis = static_cast<uint32_t>(OculusControllerAxisType::ThumbstickXAxis);
-    HandleAxisMove(i, axis, inputState.Thumbstick[oculusHandType].x);
-
-    axis = static_cast<uint32_t>(OculusControllerAxisType::ThumbstickYAxis);
-    HandleAxisMove(i, axis, -inputState.Thumbstick[oculusHandType].y);
-
-    // Process pose state.
-    GamepadPoseState poseState;
-    GetControllerPoseState(handIdx, poseState);
-    HandlePoseTracking(i, poseState, controller);
-  }
-}
-
-void
-VRSystemManagerOculus::GetControllerPoseState(uint32_t aHandIdx, GamepadPoseState& aPoseState,
-                                              bool aForceUpdate)
-{
-    ovrTrackingState state = ovr_GetTrackingState(mSession->Get(), 0.0, false);
-
-    // HandPoses is ordered by ovrControllerType_LTouch and ovrControllerType_RTouch,
-    // therefore, we can't get its state by the index of mOculusController.
-    ovrPoseStatef& pose(state.HandPoses[aHandIdx]);
-
-    if (aForceUpdate || state.HandStatusFlags[aHandIdx] & ovrStatus_OrientationTracked) {
-      aPoseState.flags |= GamepadCapabilityFlags::Cap_Orientation;
-      aPoseState.orientation[0] = pose.ThePose.Orientation.x;
-      aPoseState.orientation[1] = pose.ThePose.Orientation.y;
-      aPoseState.orientation[2] = pose.ThePose.Orientation.z;
-      aPoseState.orientation[3] = pose.ThePose.Orientation.w;
-      aPoseState.angularVelocity[0] = pose.AngularVelocity.x;
-      aPoseState.angularVelocity[1] = pose.AngularVelocity.y;
-      aPoseState.angularVelocity[2] = pose.AngularVelocity.z;
-
-      aPoseState.flags |= GamepadCapabilityFlags::Cap_AngularAcceleration;
-      aPoseState.angularAcceleration[0] = pose.AngularAcceleration.x;
-      aPoseState.angularAcceleration[1] = pose.AngularAcceleration.y;
-      aPoseState.angularAcceleration[2] = pose.AngularAcceleration.z;
-      aPoseState.isOrientationValid = true;
-    }
-    if (aForceUpdate || state.HandStatusFlags[aHandIdx] & ovrStatus_PositionTracked) {
-      aPoseState.flags |= GamepadCapabilityFlags::Cap_Position;
-      aPoseState.position[0] = pose.ThePose.Position.x;
-      aPoseState.position[1] = pose.ThePose.Position.y;
-      aPoseState.position[2] = pose.ThePose.Position.z;
-      aPoseState.linearVelocity[0] = pose.LinearVelocity.x;
-      aPoseState.linearVelocity[1] = pose.LinearVelocity.y;
-      aPoseState.linearVelocity[2] = pose.LinearVelocity.z;
-
-      aPoseState.flags |= GamepadCapabilityFlags::Cap_LinearAcceleration;
-      aPoseState.linearAcceleration[0] = pose.LinearAcceleration.x;
-      aPoseState.linearAcceleration[1] = pose.LinearAcceleration.y;
-      aPoseState.linearAcceleration[2] = pose.LinearAcceleration.z;
-
-      float eyeHeight = ovr_GetFloat(mSession->Get(), OVR_KEY_EYE_HEIGHT, OVR_DEFAULT_EYE_HEIGHT);
-      aPoseState.position[1] -= eyeHeight;
-      aPoseState.isPositionValid = true;
-    }
-}
-
-void
-VRSystemManagerOculus::HandleButtonPress(uint32_t aControllerIdx,
-                                         uint32_t aButton,
-                                         uint64_t aButtonMask,
-                                         uint64_t aButtonPressed,
-                                         uint64_t aButtonTouched)
-{
-  RefPtr<impl::VRControllerOculus> controller(mOculusController[aControllerIdx]);
-  MOZ_ASSERT(controller);
-  const uint64_t pressedDiff = (controller->GetButtonPressed() ^ aButtonPressed);
-  const uint64_t touchedDiff = (controller->GetButtonTouched() ^ aButtonTouched);
-
-  if (!pressedDiff && !touchedDiff) {
-    return;
-  }
-
-  if (pressedDiff & aButtonMask ||
-      touchedDiff & aButtonMask) {
-    // diff & (aButtonPressed, aButtonTouched) would be true while a new button pressed or
-    // touched event, otherwise it is an old event and needs to notify
-    // the button has been released.
-    if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) {
-      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
-      MOZ_CRASH_UNSAFE_PRINTF("Oculus handleButton(aButton = %d, length = %d, controller: %s.)",
-                              aButton,
-                              controller->GetControllerInfo().GetNumButtons(),
-                              controller->GetControllerInfo().GetControllerName());
-    }
-    NewButtonEvent(aControllerIdx, aButton, aButtonMask & aButtonPressed,
-                   aButtonMask & aButtonTouched,
-                   (aButtonMask & aButtonPressed) ? 1.0L : 0.0L);
-  }
-}
-
-void
-VRSystemManagerOculus::HandleIndexTriggerPress(uint32_t aControllerIdx,
-                                               uint32_t aButton,
-                                               float aValue,
-                                               uint64_t aTouchMask,
-                                               uint64_t aButtonTouched)
-{
-  RefPtr<impl::VRControllerOculus> controller(mOculusController[aControllerIdx]);
-  MOZ_ASSERT(controller);
-
-  const uint64_t touchedDiff = (controller->GetButtonTouched() ^ aButtonTouched);
-  const float oldValue = controller->GetIndexTrigger();
-  // We prefer to let developers to set their own threshold for the adjustment.
-  // Therefore, we don't check ButtonPressed and ButtonTouched with TouchMask here.
-  // we just check the button value is larger than the threshold value or not.
-  const float threshold = gfxPrefs::VRControllerTriggerThreshold();
-
-  // Avoid sending duplicated events in IPC channels.
-  if (oldValue != aValue ||
-      touchedDiff & aTouchMask) {
-    if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) {
-      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
-      MOZ_CRASH_UNSAFE_PRINTF("Oculus handleIndexTrigger(aButton = %d, length = %d, controller: %s.)",
-                              aButton,
-                              controller->GetControllerInfo().GetNumButtons(),
-                              controller->GetControllerInfo().GetControllerName());
-    }
-    NewButtonEvent(aControllerIdx, aButton, aValue > threshold,
-                   aButtonTouched & aTouchMask, aValue);
-    controller->SetIndexTrigger(aValue);
-  }
-}
-
-void
-VRSystemManagerOculus::HandleHandTriggerPress(uint32_t aControllerIdx,
-                                              uint32_t aButton,
-                                              float aValue)
-{
-  RefPtr<impl::VRControllerOculus> controller(mOculusController[aControllerIdx]);
-  MOZ_ASSERT(controller);
-  const float oldValue = controller->GetHandTrigger();
-  // We prefer to let developers to set their own threshold for the adjustment.
-  // Therefore, we don't check ButtonPressed and ButtonTouched with TouchMask here.
-  // we just check the button value is larger than the threshold value or not.
-  const float threshold = gfxPrefs::VRControllerTriggerThreshold();
-
-  // Avoid sending duplicated events in IPC channels.
-  if (oldValue != aValue) {
-    if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) {
-      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
-      MOZ_CRASH_UNSAFE_PRINTF("Oculus handleHandTrigger(aButton = %d, length = %d, controller: %s.)",
-                              aButton,
-                              controller->GetControllerInfo().GetNumButtons(),
-                              controller->GetControllerInfo().GetControllerName());
-    }
-    NewButtonEvent(aControllerIdx, aButton, aValue > threshold,
-                   aValue > threshold, aValue);
-    controller->SetHandTrigger(aValue);
-  }
-}
-
-void
-VRSystemManagerOculus::HandleTouchEvent(uint32_t aControllerIdx, uint32_t aButton,
-                                        uint64_t aTouchMask, uint64_t aButtonTouched)
-{
-  RefPtr<impl::VRControllerOculus> controller(mOculusController[aControllerIdx]);
-  MOZ_ASSERT(controller);
-  const uint64_t touchedDiff = (controller->GetButtonTouched() ^ aButtonTouched);
-
-  if (touchedDiff & aTouchMask) {
-    if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) {
-      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
-      MOZ_CRASH_UNSAFE_PRINTF("Oculus HandleTouchEvent(aButton = %d, length = %d, controller: %s.)",
-                               aButton,
-                               controller->GetControllerInfo().GetNumButtons(),
-                               controller->GetControllerInfo().GetControllerName());
-    }
-    NewButtonEvent(aControllerIdx, aButton, false, aTouchMask & aButtonTouched, 0.0f);
-  }
-}
-
-void
-VRSystemManagerOculus::HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
-                                      float aValue)
-{
-  RefPtr<impl::VRControllerOculus> controller(mOculusController[aControllerIdx]);
-  MOZ_ASSERT(controller);
-  float value = aValue;
-
-  if (abs(aValue) < 0.0000009f) {
-    value = 0.0f; // Clear noise signal
-  }
-
-  if (controller->GetAxisMove(aAxis) != value) {
-    if (MOZ_UNLIKELY(aAxis >= controller->GetControllerInfo().GetNumAxes())) {
-      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
-      MOZ_CRASH_UNSAFE_PRINTF("Oculus HandleAxisMove(aAxis = %d, length = %d, controller: %s.)",
-                               aAxis,
-                               controller->GetControllerInfo().GetNumAxes(),
-                               controller->GetControllerInfo().GetControllerName());
-    }
-    NewAxisMove(aControllerIdx, aAxis, value);
-    controller->SetAxisMove(aAxis, value);
-  }
-}
-
-void
-VRSystemManagerOculus::HandlePoseTracking(uint32_t aControllerIdx,
-                                          const GamepadPoseState& aPose,
-                                          VRControllerHost* aController)
-{
-  MOZ_ASSERT(aController);
-  if (aPose != aController->GetPose()) {
-    aController->SetPose(aPose);
-    NewPoseState(aControllerIdx, aPose);
-  }
-}
-
-void
-VRSystemManagerOculus::VibrateHaptic(uint32_t aControllerIdx,
-                                     uint32_t aHapticIndex,
-                                     double aIntensity,
-                                     double aDuration,
-                                     const VRManagerPromise& aPromise)
-{
-  // The session is available after VRDisplay is created
-  // at GetHMDs().
-  if (!mSession || !mSession->IsTrackingReady() ||
-      (aControllerIdx >= mOculusController.Length())) {
-    return;
-  }
-
-  RefPtr<impl::VRControllerOculus> controller = mOculusController[aControllerIdx];
-  MOZ_ASSERT(controller);
-
-  controller->VibrateHaptic(mSession->Get(), aHapticIndex, aIntensity, aDuration, aPromise);
-}
-
-void
-VRSystemManagerOculus::StopVibrateHaptic(uint32_t aControllerIdx)
-{
-  // The session is available after VRDisplay is created
-  // at GetHMDs().
-  if (!mSession || !mSession->IsTrackingReady() ||
-      (aControllerIdx >= mOculusController.Length())) {
-    return;
-  }
-
-  RefPtr<impl::VRControllerOculus> controller = mOculusController[aControllerIdx];
-  MOZ_ASSERT(controller);
-
-  controller->StopVibrateHaptic();
-}
-
-void
-VRSystemManagerOculus::GetControllers(nsTArray<RefPtr<VRControllerHost>>&
-                                      aControllerResult)
-{
-  aControllerResult.Clear();
-  for (uint32_t i = 0; i < mOculusController.Length(); ++i) {
-    aControllerResult.AppendElement(mOculusController[i]);
-  }
-}
-
-void
-VRSystemManagerOculus::ScanForControllers()
-{
-  // mSession is available after VRDisplay is created
-  // at GetHMDs().
-  if (!mSession || !mSession->IsTrackingReady()) {
-    return;
-  }
-
-  ovrInputState inputState;
-  bool hasInputState = ovr_GetInputState(mSession->Get(), ovrControllerType_Touch,
-                                         &inputState) == ovrSuccess;
-
-  if (!hasInputState) {
-    return;
-  }
-
-  ovrControllerType activeControllerArray[2];
-  uint32_t newControllerCount = 0;
-
-  if (inputState.ControllerType & ovrControllerType_LTouch) {
-    activeControllerArray[newControllerCount] = ovrControllerType_LTouch;
-    ++newControllerCount;
-  }
-
-  if (inputState.ControllerType & ovrControllerType_RTouch) {
-    activeControllerArray[newControllerCount] = ovrControllerType_RTouch;
-    ++newControllerCount;
-  }
-
-  if (newControllerCount != mControllerCount) {
-    RemoveControllers();
-
-    // Re-adding controllers to VRControllerManager.
-    for (uint32_t i = 0; i < newControllerCount; ++i) {
-      GamepadHand hand;
-
-      switch (activeControllerArray[i]) {
-        case ovrControllerType::ovrControllerType_LTouch:
-          hand = GamepadHand::Left;
-          break;
-        case ovrControllerType::ovrControllerType_RTouch:
-          hand = GamepadHand::Right;
-          break;
-        default:
-          continue;
-      }
-      RefPtr<VRControllerOculus> oculusController = new VRControllerOculus(hand,
-                                                      mDisplay->GetDisplayInfo().GetDisplayID());
-      mOculusController.AppendElement(oculusController);
-
-      // Not already present, add it.
-      AddGamepad(oculusController->GetControllerInfo());
-
-      // Process pose state.
-      // We wanna Oculus Touch has the right position when it shows up,
-      // so we force to update the pose no matter if it has OrientationTracked
-      // or PositionTracked.
-      const uint32_t handIdx = static_cast<uint32_t>(hand) - 1;
-      GamepadPoseState poseState;
-      GetControllerPoseState(handIdx, poseState, true);
-      HandlePoseTracking(i, poseState, oculusController);
-
-      ++mControllerCount;
-    }
-  }
-}
-
-void
-VRSystemManagerOculus::RemoveControllers()
-{
-  // controller count is changed, removing the existing gamepads first.
-  for (uint32_t i = 0; i < mOculusController.Length(); ++i) {
-    mOculusController[i]->ShutdownVibrateHapticThread();
-    RemoveGamepad(i);
-  }
-
-  mOculusController.Clear();
-  mControllerCount = 0;
-}
deleted file mode 100644
--- a/gfx/vr/gfxVROculus.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef GFX_VR_OCULUS_H
-#define GFX_VR_OCULUS_H
-
-#include "nsTArray.h"
-#include "nsISupportsImpl.h" // For NS_INLINE_DECL_REFCOUNTING
-#include "mozilla/RefPtr.h"
-
-#include "mozilla/gfx/2D.h"
-#include "mozilla/EnumeratedArray.h"
-
-#include "gfxVR.h"
-#include "VRDisplayLocal.h"
-#include "ovr_capi_dynamic.h"
-
-struct ID3D11Device;
-
-namespace mozilla {
-namespace layers {
-class CompositingRenderTargetD3D11;
-struct VertexShaderConstants;
-struct PixelShaderConstants;
-}
-namespace gfx {
-class VRThread;
-
-namespace impl {
-
-enum class OculusControllerAxisType : uint16_t {
-  ThumbstickXAxis,
-  ThumbstickYAxis,
-  NumVRControllerAxisType
-};
-
-class VROculusSession
-{
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VROculusSession);
-  friend class VRDisplayOculus;
-public:
-  VROculusSession();
-  void Refresh(bool aForceRefresh = false);
-  void StartTracking();
-  void StopTracking();
-  bool IsTrackingReady() const;
-  void StartPresentation(const IntSize& aSize);
-  void StopPresentation();
-  bool IsPresentationReady() const;
-  bool IsMounted() const;
-  ovrSession Get();
-  bool IsQuitTimeoutActive();
-  already_AddRefed<layers::CompositingRenderTargetD3D11> GetNextRenderTarget();
-  ovrTextureSwapChain GetSwapChain();
-
-private:
-  PRLibrary* mOvrLib;
-  ovrSession mSession;
-  ovrInitFlags mInitFlags;
-  ovrTextureSwapChain mTextureSet;
-  nsTArray<RefPtr<layers::CompositingRenderTargetD3D11>> mRenderTargets;
-  IntSize mPresentationSize;
-  RefPtr<ID3D11Device> mDevice;
-  RefPtr<VRThread> mSubmitThread;
-  // The timestamp of the last time Oculus set ShouldQuit to true.
-  TimeStamp mLastShouldQuit;
-  // The timestamp of the last ending presentation
-  TimeStamp mLastPresentationEnd;
-  VRTelemetry mTelemetry;
-  bool mRequestPresentation;
-  bool mRequestTracking;
-  bool mDrawBlack;
-  bool mIsConnected;
-  bool mIsMounted;
-
-  ~VROculusSession();
-  void Uninitialize();
-  bool Initialize(ovrInitFlags aFlags);
-  bool LoadOvrLib();
-  void UnloadOvrLib();
-  bool StartSession();
-  void StopSession();
-  bool StartLib(ovrInitFlags aFlags);
-  void StopLib();
-  bool StartRendering();
-  void StopRendering();
-};
-
-class VRDisplayOculus : public VRDisplayLocal
-{
-public:
-  void ZeroSensor() override;
-
-protected:
-  virtual VRHMDSensorState GetSensorState() override;
-  virtual void StartPresentation() override;
-  virtual void StopPresentation() override;
-  virtual bool SubmitFrame(ID3D11Texture2D* aSource,
-                           const IntSize& aSize,
-                           const gfx::Rect& aLeftEyeRect,
-                           const gfx::Rect& aRightEyeRect) override;
-  void UpdateStageParameters();
-
-public:
-  explicit VRDisplayOculus(VROculusSession* aSession);
-  void Destroy();
-  void Refresh();
-
-protected:
-  virtual ~VRDisplayOculus();
-
-  VRHMDSensorState GetSensorState(double absTime);
-
-  ovrHmdDesc mDesc;
-  RefPtr<VROculusSession> mSession;
-  ovrFovPort mFOVPort[2];
-
-  ID3D11VertexShader* mQuadVS;
-  ID3D11PixelShader* mQuadPS;
-  RefPtr<ID3D11SamplerState> mLinearSamplerState;
-  layers::VertexShaderConstants mVSConstants;
-  layers::PixelShaderConstants mPSConstants;
-  RefPtr<ID3D11Buffer> mVSConstantBuffer;
-  RefPtr<ID3D11Buffer> mPSConstantBuffer;
-  RefPtr<ID3D11Buffer> mVertexBuffer;
-  RefPtr<ID3D11InputLayout> mInputLayout;
-
-  float mEyeHeight;
-
-  bool UpdateConstantBuffers();
-  void UpdateEyeParameters(gfx::Matrix4x4* aHeadToEyeTransforms = nullptr);
-
-  struct Vertex
-  {
-    float position[2];
-  };
-};
-
-class VRControllerOculus : public VRControllerHost
-{
-public:
-  explicit VRControllerOculus(dom::GamepadHand aHand, uint32_t aDisplayID);
-  float GetAxisMove(uint32_t aAxis);
-  void SetAxisMove(uint32_t aAxis, float aValue);
-  float GetIndexTrigger();
-  void SetIndexTrigger(float aValue);
-  float GetHandTrigger();
-  void SetHandTrigger(float aValue);
-  void VibrateHaptic(ovrSession aSession,
-                     uint32_t aHapticIndex,
-                     double aIntensity,
-                     double aDuration,
-                     const VRManagerPromise& aPromise);
-  void StopVibrateHaptic();
-  void ShutdownVibrateHapticThread();
-
-protected:
-  virtual ~VRControllerOculus();
-
-private:
-  void UpdateVibrateHaptic(ovrSession aSession,
-                           uint32_t aHapticIndex,
-                           double aIntensity,
-                           double aDuration,
-                           uint64_t aVibrateIndex,
-                           const VRManagerPromise& aPromise);
-  void VibrateHapticComplete(ovrSession aSession, const VRManagerPromise& aPromise, bool aStop);
-
-  float mAxisMove[static_cast<uint32_t>(
-                  OculusControllerAxisType::NumVRControllerAxisType)];
-  float mIndexTrigger;
-  float mHandTrigger;
-  RefPtr<VRThread> mVibrateThread;
-  Atomic<bool> mIsVibrateStopped;
-};
-
-} // namespace impl
-
-class VRSystemManagerOculus : public VRSystemManager
-{
-public:
-  static already_AddRefed<VRSystemManagerOculus> Create();
-  virtual void Destroy() override;
-  virtual void Shutdown() override;
-  virtual void Enumerate() override;
-  virtual void NotifyVSync() override;
-  virtual bool ShouldInhibitEnumeration() override;
-  virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost> >& aHMDResult) override;
-  virtual bool GetIsPresenting() override;
-  virtual void HandleInput() override;
-  virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&
-                              aControllerResult) override;
-  virtual void ScanForControllers() override;
-  virtual void RemoveControllers() override;
-  virtual void VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
-                             double aIntensity, double aDuration, const VRManagerPromise& aPromise) override;
-  virtual void StopVibrateHaptic(uint32_t aControllerIdx) override;
-
-protected:
-  VRSystemManagerOculus();
-
-private:
-  void HandleButtonPress(uint32_t aControllerIdx,
-                         uint32_t aButton,
-                         uint64_t aButtonMask,
-                         uint64_t aButtonPressed,
-                         uint64_t aButtonTouched);
-  void HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
-                      float aValue);
-  void HandlePoseTracking(uint32_t aControllerIdx,
-                          const dom::GamepadPoseState& aPose,
-                          VRControllerHost* aController);
-  void HandleIndexTriggerPress(uint32_t aControllerIdx, uint32_t aButton, float aValue,
-                               uint64_t aTouchMask, uint64_t aButtonTouched);
-  void HandleHandTriggerPress(uint32_t aControllerIdx, uint32_t aButton, float aValue);
-  void HandleTouchEvent(uint32_t aControllerIdx, uint32_t aButton,
-                        uint64_t aTouchMask, uint64_t aTouched);
-  void GetControllerPoseState(uint32_t aHandIdx, dom::GamepadPoseState& aPoseState,
-                              bool aForceUpdate = false);
-
-  RefPtr<impl::VRDisplayOculus> mDisplay;
-  nsTArray<RefPtr<impl::VRControllerOculus>> mOculusController;
-  RefPtr<impl::VROculusSession> mSession;
-};
-
-} // namespace gfx
-} // namespace mozilla
-
-#endif /* GFX_VR_OCULUS_H */
deleted file mode 100644
--- a/gfx/vr/gfxVROpenVR.cpp
+++ /dev/null
@@ -1,1349 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include <math.h>
-
-#include "prlink.h"
-#include "prenv.h"
-#include "gfxPrefs.h"
-#include "mozilla/Preferences.h"
-
-#include "mozilla/gfx/Quaternion.h"
-
-#ifdef XP_WIN
-#include "CompositorD3D11.h"
-#include "TextureD3D11.h"
-#elif defined(XP_MACOSX)
-#include "mozilla/gfx/MacIOSurface.h"
-#endif
-
-#include "gfxVROpenVR.h"
-#include "VRManagerParent.h"
-#include "VRManager.h"
-#include "VRThread.h"
-
-#include "nsServiceManagerUtils.h"
-#include "nsIScreenManager.h"
-
-#include "mozilla/dom/GamepadEventTypes.h"
-#include "mozilla/dom/GamepadBinding.h"
-#include "mozilla/Telemetry.h"
-
-#ifndef M_PI
-# define M_PI 3.14159265358979323846
-#endif
-
-using namespace mozilla;
-using namespace mozilla::gfx;
-using namespace mozilla::gfx::impl;
-using namespace mozilla::layers;
-using namespace mozilla::dom;
-
-#define BTN_MASK_FROM_ID(_id) \
-  ::vr::ButtonMaskFromId(vr::EVRButtonId::_id)
-
-static const uint32_t kNumOpenVRHaptcs = 1;
-
-VRDisplayOpenVR::VRDisplayOpenVR(::vr::IVRSystem *aVRSystem,
-                                 ::vr::IVRChaperone *aVRChaperone,
-                                 ::vr::IVRCompositor *aVRCompositor)
-  : VRDisplayLocal(VRDeviceType::OpenVR)
-  , mVRSystem(aVRSystem)
-  , mVRChaperone(aVRChaperone)
-  , mVRCompositor(aVRCompositor)
-  , mIsPresenting(false)
-{
-  MOZ_COUNT_CTOR_INHERITED(VRDisplayOpenVR, VRDisplayLocal);
-
-  VRDisplayState& state = mDisplayInfo.mDisplayState;
-
-  strncpy(state.mDisplayName, "OpenVR HMD", kVRDisplayNameMaxLen);
-  state.mIsConnected = mVRSystem->IsTrackedDeviceConnected(::vr::k_unTrackedDeviceIndex_Hmd);
-  state.mIsMounted = false;
-  state.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
-                           VRDisplayCapabilityFlags::Cap_Orientation |
-                           VRDisplayCapabilityFlags::Cap_Position |
-                           VRDisplayCapabilityFlags::Cap_External |
-                           VRDisplayCapabilityFlags::Cap_Present |
-                           VRDisplayCapabilityFlags::Cap_StageParameters;
-  mIsHmdPresent = ::vr::VR_IsHmdPresent();
-
-  ::vr::ETrackedPropertyError err;
-  bool bHasProximitySensor = mVRSystem->GetBoolTrackedDeviceProperty(::vr::k_unTrackedDeviceIndex_Hmd, ::vr::Prop_ContainsProximitySensor_Bool, &err);
-  if (err == ::vr::TrackedProp_Success && bHasProximitySensor) {
-    state.mCapabilityFlags |= VRDisplayCapabilityFlags::Cap_MountDetection;
-  }
-
-  mVRCompositor->SetTrackingSpace(::vr::TrackingUniverseSeated);
-
-  uint32_t w, h;
-  mVRSystem->GetRecommendedRenderTargetSize(&w, &h);
-  state.mEyeResolution.width = w;
-  state.mEyeResolution.height = h;
-
-  // SteamVR gives the application a single FOV to use; it's not configurable as with Oculus
-  for (uint32_t eye = 0; eye < 2; ++eye) {
-    // get l/r/t/b clip plane coordinates
-    float l, r, t, b;
-    mVRSystem->GetProjectionRaw(static_cast<::vr::Hmd_Eye>(eye), &l, &r, &t, &b);
-    state.mEyeFOV[eye].SetFromTanRadians(-t, r, b, -l);
-  }
-  UpdateEyeParameters();
-  UpdateStageParameters();
-}
-
-VRDisplayOpenVR::~VRDisplayOpenVR()
-{
-  Destroy();
-  MOZ_COUNT_DTOR_INHERITED(VRDisplayOpenVR, VRDisplayLocal);
-}
-
-void
-VRDisplayOpenVR::Destroy()
-{
-  StopPresentation();
-  ::vr::VR_Shutdown();
-}
-
-void
-VRDisplayOpenVR::UpdateEyeParameters(gfx::Matrix4x4* aHeadToEyeTransforms /* = nullptr */)
-{
-  // Note this must be called every frame, as the IPD adjustment can be changed
-  // by the user during a VR session.
-  for (uint32_t eye = 0; eye < VRDisplayState::NumEyes; eye++) {
-    ::vr::HmdMatrix34_t eyeToHead = mVRSystem->GetEyeToHeadTransform(static_cast<::vr::Hmd_Eye>(eye));
-
-    mDisplayInfo.mDisplayState.mEyeTranslation[eye].x = eyeToHead.m[0][3];
-    mDisplayInfo.mDisplayState.mEyeTranslation[eye].y = eyeToHead.m[1][3];
-    mDisplayInfo.mDisplayState.mEyeTranslation[eye].z = eyeToHead.m[2][3];
-
-    if (aHeadToEyeTransforms) {
-      Matrix4x4 pose;
-      // NOTE! eyeToHead.m is a 3x4 matrix, not 4x4.  But
-      // because of its arrangement, we can copy the 12 elements in and
-      // then transpose them to the right place.
-      memcpy(&pose._11, &eyeToHead.m, sizeof(eyeToHead.m));
-      pose.Transpose();
-      pose.Invert();
-      aHeadToEyeTransforms[eye] = pose;
-    }
-  }
-}
-
-void
-VRDisplayOpenVR::UpdateStageParameters()
-{
-  VRDisplayState& state = mDisplayInfo.mDisplayState;
-  float sizeX = 0.0f;
-  float sizeZ = 0.0f;
-  if (mVRChaperone->GetPlayAreaSize(&sizeX, &sizeZ)) {
-    ::vr::HmdMatrix34_t t = mVRSystem->GetSeatedZeroPoseToStandingAbsoluteTrackingPose();
-    state.mStageSize.width = sizeX;
-    state.mStageSize.height = sizeZ;
-
-    state.mSittingToStandingTransform[0] = t.m[0][0];
-    state.mSittingToStandingTransform[1] = t.m[1][0];
-    state.mSittingToStandingTransform[2] = t.m[2][0];
-    state.mSittingToStandingTransform[3] = 0.0f;
-
-    state.mSittingToStandingTransform[4] = t.m[0][1];
-    state.mSittingToStandingTransform[5] = t.m[1][1];
-    state.mSittingToStandingTransform[6] = t.m[2][1];
-    state.mSittingToStandingTransform[7] = 0.0f;
-
-    state.mSittingToStandingTransform[8] = t.m[0][2];
-    state.mSittingToStandingTransform[9] = t.m[1][2];
-    state.mSittingToStandingTransform[10] = t.m[2][2];
-    state.mSittingToStandingTransform[11] = 0.0f;
-
-    state.mSittingToStandingTransform[12] = t.m[0][3];
-    state.mSittingToStandingTransform[13] = t.m[1][3];
-    state.mSittingToStandingTransform[14] = t.m[2][3];
-    state.mSittingToStandingTransform[15] = 1.0f;
-  } else {
-    // If we fail, fall back to reasonable defaults.
-    // 1m x 1m space, 0.75m high in seated position
-
-    state.mStageSize.width = 1.0f;
-    state.mStageSize.height = 1.0f;
-
-    state.mSittingToStandingTransform[0] = 1.0f;
-    state.mSittingToStandingTransform[1] = 0.0f;
-    state.mSittingToStandingTransform[2] = 0.0f;
-    state.mSittingToStandingTransform[3] = 0.0f;
-
-    state.mSittingToStandingTransform[4] = 0.0f;
-    state.mSittingToStandingTransform[5] = 1.0f;
-    state.mSittingToStandingTransform[6] = 0.0f;
-    state.mSittingToStandingTransform[7] = 0.0f;
-
-    state.mSittingToStandingTransform[8] = 0.0f;
-    state.mSittingToStandingTransform[9] = 0.0f;
-    state.mSittingToStandingTransform[10] = 1.0f;
-    state.mSittingToStandingTransform[11] = 0.0f;
-
-    state.mSittingToStandingTransform[12] = 0.0f;
-    state.mSittingToStandingTransform[13] = 0.75f;
-    state.mSittingToStandingTransform[14] = 0.0f;
-    state.mSittingToStandingTransform[15] = 1.0f;
-  }
-}
-
-void
-VRDisplayOpenVR::ZeroSensor()
-{
-  mVRSystem->ResetSeatedZeroPose();
-  UpdateStageParameters();
-}
-
-bool
-VRDisplayOpenVR::GetIsHmdPresent()
-{
-  return mIsHmdPresent;
-}
-
-void
-VRDisplayOpenVR::Refresh()
-{
-  mIsHmdPresent = ::vr::VR_IsHmdPresent();
-
-  ::vr::VREvent_t event;
-  while (mVRSystem && mVRSystem->PollNextEvent(&event, sizeof(event))) {
-    switch (event.eventType) {
-      case ::vr::VREvent_TrackedDeviceUserInteractionStarted:
-        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
-          mDisplayInfo.mDisplayState.mIsMounted = true;
-        }
-        break;
-      case ::vr::VREvent_TrackedDeviceUserInteractionEnded:
-        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
-          mDisplayInfo.mDisplayState.mIsMounted = false;
-        }
-        break;
-      case ::vr::EVREventType::VREvent_TrackedDeviceActivated:
-        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
-          mDisplayInfo.mDisplayState.mIsConnected = true;
-        }
-        break;
-      case ::vr::EVREventType::VREvent_TrackedDeviceDeactivated:
-        if (event.trackedDeviceIndex == ::vr::k_unTrackedDeviceIndex_Hmd) {
-          mDisplayInfo.mDisplayState.mIsConnected = false;
-        }
-        break;
-      case ::vr::EVREventType::VREvent_DriverRequestedQuit:
-      case ::vr::EVREventType::VREvent_Quit:
-      case ::vr::EVREventType::VREvent_ProcessQuit:
-      case ::vr::EVREventType::VREvent_QuitAcknowledged:
-      case ::vr::EVREventType::VREvent_QuitAborted_UserPrompt:
-        mIsHmdPresent = false;
-        break;
-      default:
-        // ignore
-        break;
-    }
-  }
-}
-
-VRHMDSensorState
-VRDisplayOpenVR::GetSensorState()
-{
-  const uint32_t posesSize = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
-  ::vr::TrackedDevicePose_t poses[posesSize];
-  // Note: We *must* call WaitGetPoses in order for any rendering to happen at all.
-  mVRCompositor->WaitGetPoses(poses, posesSize, nullptr, 0);
-  gfx::Matrix4x4 headToEyeTransforms[2];
-  UpdateEyeParameters(headToEyeTransforms);
-
-  VRHMDSensorState result{};
-
-  ::vr::Compositor_FrameTiming timing;
-  timing.m_nSize = sizeof(::vr::Compositor_FrameTiming);
-  if (mVRCompositor->GetFrameTiming(&timing)) {
-    result.timestamp = timing.m_flSystemTimeInSeconds;
-  } else {
-    // This should not happen, but log it just in case
-    NS_WARNING("OpenVR - IVRCompositor::GetFrameTiming failed");
-  }
-
-  if (poses[::vr::k_unTrackedDeviceIndex_Hmd].bDeviceIsConnected &&
-      poses[::vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid &&
-      poses[::vr::k_unTrackedDeviceIndex_Hmd].eTrackingResult == ::vr::TrackingResult_Running_OK)
-  {
-    const ::vr::TrackedDevicePose_t& pose = poses[::vr::k_unTrackedDeviceIndex_Hmd];
-
-    gfx::Matrix4x4 m;
-    // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4.  But
-    // because of its arrangement, we can copy the 12 elements in and
-    // then transpose them to the right place.  We do this so we can
-    // pull out a Quaternion.
-    memcpy(&m._11, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
-    m.Transpose();
-
-    gfx::Quaternion rot;
-    rot.SetFromRotationMatrix(m);
-    rot.Invert();
-
-    result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
-    result.pose.orientation[0] = rot.x;
-    result.pose.orientation[1] = rot.y;
-    result.pose.orientation[2] = rot.z;
-    result.pose.orientation[3] = rot.w;
-    result.pose.angularVelocity[0] = pose.vAngularVelocity.v[0];
-    result.pose.angularVelocity[1] = pose.vAngularVelocity.v[1];
-    result.pose.angularVelocity[2] = pose.vAngularVelocity.v[2];
-
-    result.flags |= VRDisplayCapabilityFlags::Cap_Position;
-    result.pose.position[0] = m._41;
-    result.pose.position[1] = m._42;
-    result.pose.position[2] = m._43;
-    result.pose.linearVelocity[0] = pose.vVelocity.v[0];
-    result.pose.linearVelocity[1] = pose.vVelocity.v[1];
-    result.pose.linearVelocity[2] = pose.vVelocity.v[2];
-  } else {
-    // default to an identity quaternion
-    result.pose.orientation[3] = 1.0f;
-  }
-
-  result.CalcViewMatrices(headToEyeTransforms);
-  result.inputFrameID = mDisplayInfo.mFrameId;
-  return result;
-}
-
-void
-VRDisplayOpenVR::StartPresentation()
-{
-  if (mIsPresenting) {
-    return;
-  }
-  mIsPresenting = true;
-  mTelemetry.Clear();
-  mTelemetry.mPresentationStart = TimeStamp::Now();
-
-  ::vr::Compositor_CumulativeStats stats;
-  mVRCompositor->GetCumulativeStats(&stats, sizeof(::vr::Compositor_CumulativeStats));
-  mTelemetry.mLastDroppedFrameCount = stats.m_nNumReprojectedFrames;
-}
-
-void
-VRDisplayOpenVR::StopPresentation()
-{
-  if (!mIsPresenting) {
-    return;
-  }
-
-  mVRCompositor->ClearLastSubmittedFrame();
-
-  mIsPresenting = false;
-  const TimeDuration duration = TimeStamp::Now() - mTelemetry.mPresentationStart;
-  Telemetry::Accumulate(Telemetry::WEBVR_USERS_VIEW_IN, 2);
-  Telemetry::Accumulate(Telemetry::WEBVR_TIME_SPENT_VIEWING_IN_OPENVR,
-                        duration.ToMilliseconds());
-
-  ::vr::Compositor_CumulativeStats stats;
-  mVRCompositor->GetCumulativeStats(&stats, sizeof(::vr::Compositor_CumulativeStats));
-  const uint32_t droppedFramesPerSec = (stats.m_nNumReprojectedFrames -
-                                        mTelemetry.mLastDroppedFrameCount) / duration.ToSeconds();
-  Telemetry::Accumulate(Telemetry::WEBVR_DROPPED_FRAMES_IN_OPENVR, droppedFramesPerSec);
-}
-
-bool
-VRDisplayOpenVR::SubmitFrameOpenVRHandle(void* aTextureHandle,
-                                         ::vr::ETextureType aTextureType,
-                                         const IntSize& aSize,
-                                         const gfx::Rect& aLeftEyeRect,
-                                         const gfx::Rect& aRightEyeRect)
-{
-  MOZ_ASSERT(mSubmitThread->GetThread() == NS_GetCurrentThread());
-  if (!mIsPresenting) {
-    return false;
-  }
-
-  ::vr::Texture_t tex;
-  tex.handle = aTextureHandle;
-  tex.eType = aTextureType;
-  tex.eColorSpace = ::vr::EColorSpace::ColorSpace_Auto;
-
-  ::vr::VRTextureBounds_t bounds;
-  bounds.uMin = aLeftEyeRect.X();
-  bounds.vMin = 1.0 - aLeftEyeRect.Y();
-  bounds.uMax = aLeftEyeRect.XMost();
-  bounds.vMax = 1.0 - aLeftEyeRect.YMost();
-
-  ::vr::EVRCompositorError err;
-  err = mVRCompositor->Submit(::vr::EVREye::Eye_Left, &tex, &bounds);
-  if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
-    printf_stderr("OpenVR Compositor Submit() failed.\n");
-  }
-
-  bounds.uMin = aRightEyeRect.X();
-  bounds.vMin = 1.0 - aRightEyeRect.Y();
-  bounds.uMax = aRightEyeRect.XMost();
-  bounds.vMax = 1.0 - aRightEyeRect.YMost();
-
-  err = mVRCompositor->Submit(::vr::EVREye::Eye_Right, &tex, &bounds);
-  if (err != ::vr::EVRCompositorError::VRCompositorError_None) {
-    printf_stderr("OpenVR Compositor Submit() failed.\n");
-  }
-
-  mVRCompositor->PostPresentHandoff();
-  return true;
-}
-
-#if defined(XP_WIN)
-
-bool
-VRDisplayOpenVR::SubmitFrame(ID3D11Texture2D* aSource,
-                             const IntSize& aSize,
-                             const gfx::Rect& aLeftEyeRect,
-                             const gfx::Rect& aRightEyeRect)
-{
-  return SubmitFrameOpenVRHandle((void *)aSource,
-                                 ::vr::ETextureType::TextureType_DirectX,
-                                 aSize, aLeftEyeRect, aRightEyeRect);
-}
-
-#elif defined(XP_MACOSX)
-
-bool
-VRDisplayOpenVR::SubmitFrame(MacIOSurface* aMacIOSurface,
-                             const IntSize& aSize,
-                             const gfx::Rect& aLeftEyeRect,
-                             const gfx::Rect& aRightEyeRect)
-{
-  const void* ioSurface = aMacIOSurface->GetIOSurfacePtr();
-  bool result = false;
-  if (ioSurface == nullptr) {
-    NS_WARNING("VRDisplayOpenVR::SubmitFrame() could not get an IOSurface");
-  } else {
-    result = SubmitFrameOpenVRHandle((void *)ioSurface,
-                                     ::vr::ETextureType::TextureType_IOSurface,
-                                     aSize, aLeftEyeRect, aRightEyeRect);
-  }
-  return result;
-}
-
-#endif
-
-VRControllerOpenVR::VRControllerOpenVR(dom::GamepadHand aHand, uint32_t aDisplayID,
-                                       uint32_t aNumButtons, uint32_t aNumTriggers,
-                                       uint32_t aNumAxes, const nsCString& aId)
-  : VRControllerHost(VRDeviceType::OpenVR, aHand, aDisplayID)
-  , mTrackedIndex(0)
-  , mVibrateThread(nullptr)
-  , mIsVibrateStopped(false)
-{
-  MOZ_COUNT_CTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
-
-  VRControllerState& state = mControllerInfo.mControllerState;
-  strncpy(state.controllerName, aId.BeginReading(), kVRControllerNameMaxLen);
-  state.numButtons = aNumButtons;
-  state.numAxes = aNumAxes;
-  state.numHaptics = kNumOpenVRHaptcs;
-}
-
-VRControllerOpenVR::~VRControllerOpenVR()
-{
-  ShutdownVibrateHapticThread();
-  MOZ_COUNT_DTOR_INHERITED(VRControllerOpenVR, VRControllerHost);
-}
-
-void
-VRControllerOpenVR::SetTrackedIndex(uint32_t aTrackedIndex)
-{
-  mTrackedIndex = aTrackedIndex;
-}
-
-uint32_t
-VRControllerOpenVR::GetTrackedIndex()
-{
-  return mTrackedIndex;
-}
-
-float
-VRControllerOpenVR::GetAxisMove(uint32_t aAxis)
-{
-  return mControllerInfo.mControllerState.axisValue[aAxis];
-}
-
-void
-VRControllerOpenVR::SetAxisMove(uint32_t aAxis, float aValue)
-{
-  mControllerInfo.mControllerState.axisValue[aAxis] = aValue;
-}
-
-void
-VRControllerOpenVR::SetTrigger(uint32_t aButton, float aValue)
-{
-  mControllerInfo.mControllerState.triggerValue[aButton] = aValue;
-}
-
-float
-VRControllerOpenVR::GetTrigger(uint32_t aButton)
-{
-  return mControllerInfo.mControllerState.triggerValue[aButton];
-}
-
-void
-VRControllerOpenVR::SetHand(dom::GamepadHand aHand)
-{
-  mControllerInfo.mControllerState.hand = aHand;
-}
-
-void
-VRControllerOpenVR::UpdateVibrateHaptic(::vr::IVRSystem* aVRSystem,
-                                        uint32_t aHapticIndex,
-                                        double aIntensity,
-                                        double aDuration,
-                                        uint64_t aVibrateIndex,
-                                        const VRManagerPromise& aPromise)
-{
-  // UpdateVibrateHaptic() only can be called by mVibrateThread
-  MOZ_ASSERT(mVibrateThread->GetThread() == NS_GetCurrentThread());
-
-  // It has been interrupted by loss focus.
-  if (mIsVibrateStopped) {
-    VibrateHapticComplete(aPromise);
-    return;
-  }
-  // Avoid the previous vibrate event to override the new one.
-  if (mVibrateIndex != aVibrateIndex) {
-    VibrateHapticComplete(aPromise);
-    return;
-  }
-
-  const double duration = (aIntensity == 0) ? 0 : aDuration;
-  // We expect OpenVR to vibrate for 5 ms, but we found it only response the
-  // commend ~ 3.9 ms. For duration time longer than 3.9 ms, we separate them
-  // to a loop of 3.9 ms for make users feel that is a continuous events.
-  const uint32_t microSec = (duration < 3.9 ? duration : 3.9) * 1000 * aIntensity;
-  aVRSystem->TriggerHapticPulse(GetTrackedIndex(),
-                                aHapticIndex, microSec);
-
-  // In OpenVR spec, it mentions TriggerHapticPulse() may not trigger another haptic pulse
-  // on this controller and axis combination for 5ms.
-  const double kVibrateRate = 5.0;
-  if (duration >= kVibrateRate) {
-    MOZ_ASSERT(mVibrateThread);
-    MOZ_ASSERT(mVibrateThread->IsActive());
-
-    RefPtr<Runnable> runnable =
-      NewRunnableMethod<::vr::IVRSystem*, uint32_t, double, double, uint64_t,
-        StoreCopyPassByConstLRef<VRManagerPromise>>(
-          "VRControllerOpenVR::UpdateVibrateHaptic",
-          this, &VRControllerOpenVR::UpdateVibrateHaptic, aVRSystem,
-          aHapticIndex, aIntensity, duration - kVibrateRate, aVibrateIndex, aPromise);
-    mVibrateThread->PostDelayedTask(runnable.forget(), kVibrateRate);
-  } else {
-    // The pulse has completed
-    VibrateHapticComplete(aPromise);
-  }
-}
-
-void
-VRControllerOpenVR::VibrateHapticComplete(const VRManagerPromise& aPromise)
-{
-  VRManager *vm = VRManager::Get();
-  CompositorThreadHolder::Loop()->PostTask(
-    NewRunnableMethod<StoreCopyPassByConstLRef<VRManagerPromise>>(
-      "VRManager::NotifyVibrateHapticCompleted",
-      vm, &VRManager::NotifyVibrateHapticCompleted, aPromise));
-}
-
-void
-VRControllerOpenVR::VibrateHaptic(::vr::IVRSystem* aVRSystem,
-                                  uint32_t aHapticIndex,
-                                  double aIntensity,
-                                  double aDuration,
-                                  const VRManagerPromise& aPromise)
-{
-  // Spinning up the haptics thread at the first haptics call.
-  if (!mVibrateThread) {
-    mVibrateThread = new VRThread(NS_LITERAL_CSTRING("OpenVR_Vibration"));
-  }
-  mVibrateThread->Start();
-  ++mVibrateIndex;
-  mIsVibrateStopped = false;
-
-  RefPtr<Runnable> runnable =
-    NewRunnableMethod<::vr::IVRSystem*, uint32_t, double, double, uint64_t,
-      StoreCopyPassByConstLRef<VRManagerPromise>>(
-        "VRControllerOpenVR::UpdateVibrateHaptic",
-        this, &VRControllerOpenVR::UpdateVibrateHaptic, aVRSystem,
-        aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromise);
-  mVibrateThread->PostTask(runnable.forget());
-}
-
-void
-VRControllerOpenVR::StopVibrateHaptic()
-{
-  mIsVibrateStopped = true;
-}
-
-void
-VRControllerOpenVR::ShutdownVibrateHapticThread()
-{
-  StopVibrateHaptic();
-  if (mVibrateThread) {
-    mVibrateThread->Shutdown();
-    mVibrateThread = nullptr;
-  }
-}
-
-VRSystemManagerOpenVR::VRSystemManagerOpenVR()
-  : mVRSystem(nullptr)
-  , mRuntimeCheckFailed(false)
-  , mIsWindowsMR(false)
-{
-}
-
-/*static*/ already_AddRefed<VRSystemManagerOpenVR>
-VRSystemManagerOpenVR::Create()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (!gfxPrefs::VREnabled() || !gfxPrefs::VROpenVREnabled()) {
-    return nullptr;
-  }
-
-  RefPtr<VRSystemManagerOpenVR> manager = new VRSystemManagerOpenVR();
-  return manager.forget();
-}
-
-void
-VRSystemManagerOpenVR::Destroy()
-{
-  Shutdown();
-}
-
-void
-VRSystemManagerOpenVR::Shutdown()
-{
-  if (mOpenVRHMD) {
-    mOpenVRHMD = nullptr;
-  }
-  RemoveControllers();
-  mVRSystem = nullptr;
-}
-
-void
-VRSystemManagerOpenVR::NotifyVSync()
-{
-  VRSystemManager::NotifyVSync();
-
-  // Avoid doing anything unless we have already
-  // successfully enumerated and loaded the OpenVR
-  // runtime.
-  if (mVRSystem == nullptr) {
-    return;
-  }
-
-  if (mOpenVRHMD) {
-    mOpenVRHMD->Refresh();
-    if (!mOpenVRHMD->GetIsHmdPresent()) {
-      // OpenVR runtime could be quit accidentally
-      // or a device could be disconnected.
-      // We free up resources and must re-initialize
-      // if a device is detected again later.
-      mOpenVRHMD = nullptr;
-      mVRSystem = nullptr;
-    }
-  }
-}
-
-void
-VRSystemManagerOpenVR::Enumerate()
-{
-  if (mOpenVRHMD) {
-    // Already enumerated, nothing more to do
-    return;
-  }
-  if (mRuntimeCheckFailed) {
-    // We have already checked for a runtime and
-    // know that its not installed.
-    return;
-  }
-  if (!::vr::VR_IsRuntimeInstalled()) {
-    // Runtime is not installed, remember so we don't
-    // continue to scan for the files
-    mRuntimeCheckFailed = true;
-    return;
-  }
-  if (!::vr::VR_IsHmdPresent()) {
-    // Avoid initializing if no headset is connected
-    return;
-  }
-
-  ::vr::HmdError err;
-
-  ::vr::VR_Init(&err, ::vr::EVRApplicationType::VRApplication_Scene);
-  if (err) {
-    return;
-  }
-
-  ::vr::IVRSystem *system = (::vr::IVRSystem *)::vr::VR_GetGenericInterface(::vr::IVRSystem_Version, &err);
-  if (err || !system) {
-    ::vr::VR_Shutdown();
-    return;
-  }
-  ::vr::IVRChaperone *chaperone = (::vr::IVRChaperone *)::vr::VR_GetGenericInterface(::vr::IVRChaperone_Version, &err);
-  if (err || !chaperone) {
-    ::vr::VR_Shutdown();
-    return;
-  }
-  ::vr::IVRCompositor *compositor = (::vr::IVRCompositor*)::vr::VR_GetGenericInterface(::vr::IVRCompositor_Version, &err);
-  if (err || !compositor) {
-    ::vr::VR_Shutdown();
-    return;
-  }
-
-  mVRSystem = system;
-  mOpenVRHMD = new VRDisplayOpenVR(system, chaperone, compositor);
-}
-
-bool
-VRSystemManagerOpenVR::ShouldInhibitEnumeration()
-{
-  if (VRSystemManager::ShouldInhibitEnumeration()) {
-    return true;
-  }
-  if (mOpenVRHMD) {
-    // When we find an a VR device, don't
-    // allow any further enumeration as it
-    // may get picked up redundantly by other
-    // API's.
-    return true;
-  }
-  return false;
-}
-
-void
-VRSystemManagerOpenVR::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
-{
-  if (mOpenVRHMD) {
-    aHMDResult.AppendElement(mOpenVRHMD);
-  }
-}
-
-bool
-VRSystemManagerOpenVR::GetIsPresenting()
-{
-  if (mOpenVRHMD) {
-    VRDisplayInfo displayInfo(mOpenVRHMD->GetDisplayInfo());
-    return displayInfo.GetPresentingGroups() != kVRGroupNone;
-  }
-
-  return false;
-}
-
-void
-VRSystemManagerOpenVR::HandleInput()
-{
-  // mVRSystem is available after VRDisplay is created
-  // at GetHMDs().
-  if (!mVRSystem) {
-    return;
-  }
-
-  RefPtr<impl::VRControllerOpenVR> controller;
-  // Compare with Edge, we have a wrong implementation for the vertical axis value.
-  // In order to not affect the current VR content, we add a workaround for yAxis.
-  const float yAxisInvert = (mIsWindowsMR) ? -1.0f : 1.0f;
-  ::vr::VRControllerState_t state;
-  ::vr::TrackedDevicePose_t poses[::vr::k_unMaxTrackedDeviceCount];
-  mVRSystem->GetDeviceToAbsoluteTrackingPose(::vr::TrackingUniverseSeated, 0.0f,
-                                             poses, ::vr::k_unMaxTrackedDeviceCount);
-  // Process OpenVR controller state
-  for (uint32_t i = 0; i < mOpenVRController.Length(); ++i) {
-    uint32_t axisIdx = 0;
-    uint32_t buttonIdx = 0;
-    uint32_t triggerIdx = 0;
-    controller = mOpenVRController[i];
-    const uint32_t trackedIndex = controller->GetTrackedIndex();
-
-    MOZ_ASSERT(mVRSystem->GetTrackedDeviceClass(trackedIndex)
-               == ::vr::TrackedDeviceClass_Controller ||
-               mVRSystem->GetTrackedDeviceClass(trackedIndex)
-               == ::vr::TrackedDeviceClass_GenericTracker);
-
-    // Sometimes, OpenVR controllers are not located by HMD at the initial time.
-    // That makes us have to update the hand info at runtime although switching controllers
-    // to the other hand does not have new changes at the current OpenVR SDK. But, it makes sense
-    // to detect hand changing at runtime.
-    const ::vr::ETrackedControllerRole role = mVRSystem->
-                                                GetControllerRoleForTrackedDeviceIndex(
-                                                trackedIndex);
-    const dom::GamepadHand hand = GetGamepadHandFromControllerRole(role);
-    if (hand != controller->GetHand()) {
-      controller->SetHand(hand);
-      NewHandChangeEvent(i, hand);
-    }
-
-    if (mVRSystem->GetControllerState(trackedIndex, &state, sizeof(state))) {
-      for (uint32_t j = 0; j < ::vr::k_unControllerStateAxisCount; ++j) {
-        const uint32_t axisType = mVRSystem->GetInt32TrackedDeviceProperty(
-                                   trackedIndex,
-                                   static_cast<::vr::TrackedDeviceProperty>(
-                                   ::vr::Prop_Axis0Type_Int32 + j));
-        switch (axisType) {
-          case ::vr::EVRControllerAxisType::k_eControllerAxis_Joystick:
-          case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
-            if (mIsWindowsMR) {
-              // Adjust the input mapping for Windows MR which has
-              // different order.
-              axisIdx = (axisIdx == 0) ? 2 : 0;
-              buttonIdx = (buttonIdx == 0) ? 4 : 0;
-            }
-
-            if (!HandleAxisMove(i, axisIdx, state.rAxis[j].x)) {
-              RemoveControllers();
-              return;
-            }
-            ++axisIdx;
-
-            if (!HandleAxisMove(i, axisIdx, state.rAxis[j].y * yAxisInvert)) {
-              RemoveControllers();
-              return;
-            }
-            ++axisIdx;
-
-            if (!HandleButtonPress(i, buttonIdx,
-                                   ::vr::ButtonMaskFromId(
-                                   static_cast<::vr::EVRButtonId>(::vr::k_EButton_Axis0 + j)),
-                                   state.ulButtonPressed, state.ulButtonTouched)) {
-              RemoveControllers();
-              return;
-            }
-            ++buttonIdx;
-
-            if (mIsWindowsMR) {
-              axisIdx = (axisIdx == 4) ? 2 : 4;
-              buttonIdx = (buttonIdx == 5) ? 1 : 2;
-            }
-            break;
-          case vr::EVRControllerAxisType::k_eControllerAxis_Trigger:
-            if (j <= 2) {
-              if (!HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].x)) {
-                RemoveControllers();
-                return;
-              }
-              ++buttonIdx;
-              ++triggerIdx;
-            } else {
-              // For SteamVR Knuckles.
-              if (!HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].x)) {
-                RemoveControllers();
-                return;
-              }
-              ++buttonIdx;
-              ++triggerIdx;
-              if (!HandleTriggerPress(i, buttonIdx, triggerIdx, state.rAxis[j].y)) {
-                RemoveControllers();
-                return;
-              }
-              ++buttonIdx;
-              ++triggerIdx;
-            }
-            break;
-        }
-      }
-      MOZ_ASSERT(axisIdx ==
-                 controller->GetControllerInfo().GetNumAxes());
-
-      const uint64_t supportedButtons = mVRSystem->GetUint64TrackedDeviceProperty(
-                                         trackedIndex, ::vr::Prop_SupportedButtons_Uint64);
-      if (supportedButtons &
-          BTN_MASK_FROM_ID(k_EButton_A)) {
-        if (!HandleButtonPress(i, buttonIdx,
-                               BTN_MASK_FROM_ID(k_EButton_A),
-                               state.ulButtonPressed, state.ulButtonTouched)) {
-          RemoveControllers();
-          return;
-        }
-        ++buttonIdx;
-      }
-      if (supportedButtons &
-          BTN_MASK_FROM_ID(k_EButton_Grip)) {
-        if (!HandleButtonPress(i, buttonIdx,
-                              BTN_MASK_FROM_ID(k_EButton_Grip),
-                              state.ulButtonPressed, state.ulButtonTouched)) {
-          RemoveControllers();
-          return;
-        }
-        ++buttonIdx;
-      }
-      if (supportedButtons &
-          BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
-        if (!HandleButtonPress(i, buttonIdx,
-                               BTN_MASK_FROM_ID(k_EButton_ApplicationMenu),
-                               state.ulButtonPressed, state.ulButtonTouched)) {
-          RemoveControllers();
-          return;
-        }
-        ++buttonIdx;
-      }
-      if (mIsWindowsMR) {
-        // button 4 in Windows MR has already been assigned
-        // to k_eControllerAxis_TrackPad.
-        ++buttonIdx;
-      }
-      if (supportedButtons &
-          BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
-        if (!HandleButtonPress(i, buttonIdx,
-                               BTN_MASK_FROM_ID(k_EButton_DPad_Left),
-                               state.ulButtonPressed, state.ulButtonTouched)) {
-          RemoveControllers();
-          return;
-        }
-        ++buttonIdx;
-      }
-      if (supportedButtons &
-          BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
-        if (!HandleButtonPress(i, buttonIdx,
-                          BTN_MASK_FROM_ID(k_EButton_DPad_Up),
-                          state.ulButtonPressed, state.ulButtonTouched)) {
-          RemoveControllers();
-          return;
-        }
-        ++buttonIdx;
-      }
-      if (supportedButtons &
-          BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
-        if (!HandleButtonPress(i, buttonIdx,
-                               BTN_MASK_FROM_ID(k_EButton_DPad_Right),
-                               state.ulButtonPressed, state.ulButtonTouched)) {
-          RemoveControllers();
-          return;
-        }
-        ++buttonIdx;
-      }
-      if (supportedButtons &
-          BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
-        if (!HandleButtonPress(i, buttonIdx,
-                               BTN_MASK_FROM_ID(k_EButton_DPad_Down),
-                               state.ulButtonPressed, state.ulButtonTouched)) {
-          RemoveControllers();
-          return;
-        }
-        ++buttonIdx;
-      }
-      MOZ_ASSERT(buttonIdx ==
-                 controller->GetControllerInfo().GetNumButtons());
-      controller->SetButtonPressed(state.ulButtonPressed);
-      controller->SetButtonTouched(state.ulButtonTouched);
-
-      // Start to process pose
-      const ::vr::TrackedDevicePose_t& pose = poses[trackedIndex];
-      GamepadPoseState poseState;
-
-      if (pose.bDeviceIsConnected) {
-        poseState.flags |= (GamepadCapabilityFlags::Cap_Orientation |
-                            GamepadCapabilityFlags::Cap_Position);
-      }
-
-      if (pose.bPoseIsValid &&
-          pose.eTrackingResult == ::vr::TrackingResult_Running_OK) {
-        gfx::Matrix4x4 m;
-
-        // NOTE! mDeviceToAbsoluteTracking is a 3x4 matrix, not 4x4.  But
-        // because of its arrangement, we can copy the 12 elements in and
-        // then transpose them to the right place.  We do this so we can
-        // pull out a Quaternion.
-        memcpy(&m.components, &pose.mDeviceToAbsoluteTracking, sizeof(pose.mDeviceToAbsoluteTracking));
-        m.Transpose();
-
-        gfx::Quaternion rot;
-        rot.SetFromRotationMatrix(m);
-        rot.Invert();
-
-        poseState.orientation[0] = rot.x;
-        poseState.orientation[1] = rot.y;
-        poseState.orientation[2] = rot.z;
-        poseState.orientation[3] = rot.w;
-        poseState.angularVelocity[0] = pose.vAngularVelocity.v[0];
-        poseState.angularVelocity[1] = pose.vAngularVelocity.v[1];
-        poseState.angularVelocity[2] = pose.vAngularVelocity.v[2];
-        poseState.isOrientationValid = true;
-
-        poseState.position[0] = m._41;
-        poseState.position[1] = m._42;
-        poseState.position[2] = m._43;
-        poseState.linearVelocity[0] = pose.vVelocity.v[0];
-        poseState.linearVelocity[1] = pose.vVelocity.v[1];
-        poseState.linearVelocity[2] = pose.vVelocity.v[2];
-        poseState.isPositionValid = true;
-      }
-      HandlePoseTracking(i, poseState, controller);
-    }
-  }
-}
-
-bool
-VRSystemManagerOpenVR::HandleButtonPress(uint32_t aControllerIdx,
-                                         uint32_t aButton,
-                                         uint64_t aButtonMask,
-                                         uint64_t aButtonPressed,
-                                         uint64_t aButtonTouched)
-{
-  RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
-  MOZ_ASSERT(controller);
-  const uint64_t pressedDiff = (controller->GetButtonPressed() ^ aButtonPressed);
-  const uint64_t touchedDiff = (controller->GetButtonTouched() ^ aButtonTouched);
-
-  if (!pressedDiff && !touchedDiff) {
-    return true;
-  }
-
-  if (pressedDiff & aButtonMask ||
-      touchedDiff & aButtonMask) {
-    // diff & (aButtonPressed, aButtonTouched) would be true while a new button pressed or
-    // touched event, otherwise it is an old event and needs to notify
-    // the button has been released.
-    if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) {
-      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
-      MOZ_CRASH_UNSAFE_PRINTF("OpenVR handleButton(aButton = %d, length = %d, controller: %s.)",
-                              aButton,
-                              controller->GetControllerInfo().GetNumButtons(),
-                              controller->GetControllerInfo().GetControllerName());
-      return false;
-    }
-    NewButtonEvent(aControllerIdx, aButton, aButtonMask & aButtonPressed,
-                   aButtonMask & aButtonTouched,
-                   (aButtonMask & aButtonPressed) ? 1.0L : 0.0L);
-  }
-  return true;
-}
-
-bool
-VRSystemManagerOpenVR::HandleTriggerPress(uint32_t aControllerIdx,
-                                          uint32_t aButton,
-                                          uint32_t aTrigger,
-                                          float aValue)
-{
-  RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
-  MOZ_ASSERT(controller);
-  const float oldValue = controller->GetTrigger(aTrigger);
-  // For OpenVR, the threshold value of ButtonPressed and ButtonTouched is 0.55.
-  // We prefer to let developers to set their own threshold for the adjustment.
-  // Therefore, we don't check ButtonPressed and ButtonTouched with ButtonMask here.
-  // we just check the button value is larger than the threshold value or not.
-  const float threshold = gfxPrefs::VRControllerTriggerThreshold();
-
-  // Avoid sending duplicated events in IPC channels.
-  if (oldValue != aValue) {
-    if (MOZ_UNLIKELY(aButton >= controller->GetControllerInfo().GetNumButtons())) {
-      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
-      MOZ_CRASH_UNSAFE_PRINTF("OpenVR handleTrigger(aButton = %d, length = %d, controller: %s.)",
-                              aButton,
-                              controller->GetControllerInfo().GetNumButtons(),
-                              controller->GetControllerInfo().GetControllerName());
-      return false;
-    }
-    NewButtonEvent(aControllerIdx, aButton, aValue > threshold,
-                   aValue > threshold, aValue);
-    controller->SetTrigger(aTrigger, aValue);
-  }
-  return true;
-}
-
-bool
-VRSystemManagerOpenVR::HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
-                                      float aValue)
-{
-  RefPtr<impl::VRControllerOpenVR> controller(mOpenVRController[aControllerIdx]);
-  MOZ_ASSERT(controller);
-
-  if (controller->GetAxisMove(aAxis) != aValue) {
-    if (MOZ_UNLIKELY(aAxis >= controller->GetControllerInfo().GetNumAxes())) {
-      // FIXME: Removing crash log for Bug 1488573 to investigate unmatched count.
-      MOZ_CRASH_UNSAFE_PRINTF("OpenVR handleAxis(aAxis = %d, length = %d, controller: %s.)",
-                              aAxis,
-                              controller->GetControllerInfo().GetNumAxes(),
-                              controller->GetControllerInfo().GetControllerName());
-      return false;
-    }
-    NewAxisMove(aControllerIdx, aAxis, aValue);
-    controller->SetAxisMove(aAxis, aValue);
-  }
-  return true;
-}
-
-void
-VRSystemManagerOpenVR::HandlePoseTracking(uint32_t aControllerIdx,
-                                          const GamepadPoseState& aPose,
-                                          VRControllerHost* aController)
-{
-  MOZ_ASSERT(aController);
-  if (aPose != aController->GetPose()) {
-    aController->SetPose(aPose);
-    NewPoseState(aControllerIdx, aPose);
-  }
-}
-
-dom::GamepadHand
-VRSystemManagerOpenVR::GetGamepadHandFromControllerRole(
-                                          ::vr::ETrackedControllerRole aRole)
-{
-  dom::GamepadHand hand;
-
-  switch(aRole) {
-    case ::vr::ETrackedControllerRole::TrackedControllerRole_Invalid:
-    case ::vr::ETrackedControllerRole::TrackedControllerRole_OptOut:
-      hand = dom::GamepadHand::_empty;
-      break;
-    case ::vr::ETrackedControllerRole::TrackedControllerRole_LeftHand:
-      hand = dom::GamepadHand::Left;
-      break;
-    case ::vr::ETrackedControllerRole::TrackedControllerRole_RightHand:
-      hand = dom::GamepadHand::Right;
-      break;
-    default:
-      hand = dom::GamepadHand::_empty;
-      MOZ_ASSERT(false);
-      break;
-  }
-
-  return hand;
-}
-
-void
-VRSystemManagerOpenVR::VibrateHaptic(uint32_t aControllerIdx,
-                                     uint32_t aHapticIndex,
-                                     double aIntensity,
-                                     double aDuration,
-                                     const VRManagerPromise& aPromise)
-{
-  // mVRSystem is available after VRDisplay is created
-  // at GetHMDs().
-  if (!mVRSystem || (aControllerIdx >= mOpenVRController.Length())) {
-    return;
-  }
-
-  RefPtr<impl::VRControllerOpenVR> controller = mOpenVRController[aControllerIdx];
-  MOZ_ASSERT(controller);
-
-  controller->VibrateHaptic(mVRSystem, aHapticIndex, aIntensity, aDuration, aPromise);
-}
-
-void
-VRSystemManagerOpenVR::StopVibrateHaptic(uint32_t aControllerIdx)
-{
-  // mVRSystem is available after VRDisplay is created
-  // at GetHMDs().
-  if (!mVRSystem || (aControllerIdx >= mOpenVRController.Length())) {
-    return;
-  }
-
-  RefPtr<impl::VRControllerOpenVR> controller = mOpenVRController[aControllerIdx];
-  MOZ_ASSERT(controller);
-
-  controller->StopVibrateHaptic();
-}
-
-void
-VRSystemManagerOpenVR::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
-{
-  aControllerResult.Clear();
-  for (uint32_t i = 0; i < mOpenVRController.Length(); ++i) {
-    aControllerResult.AppendElement(mOpenVRController[i]);
-  }
-}
-
-void
-VRSystemManagerOpenVR::ScanForControllers()
-{
-  // mVRSystem is available after VRDisplay is created
-  // at GetHMDs().
-  if (!mVRSystem) {
-    return;
-  }
-
-  ::vr::TrackedDeviceIndex_t trackedIndexArray[::vr::k_unMaxTrackedDeviceCount];
-  uint32_t newControllerCount = 0;
-  // Basically, we would have HMDs in the tracked devices,
-  // but we are just interested in the controllers.
-  for (::vr::TrackedDeviceIndex_t trackedDevice = ::vr::k_unTrackedDeviceIndex_Hmd + 1;
-       trackedDevice < ::vr::k_unMaxTrackedDeviceCount; ++trackedDevice) {
-
-    if (!mVRSystem->IsTrackedDeviceConnected(trackedDevice)) {
-      continue;
-    }
-
-    const ::vr::ETrackedDeviceClass deviceType = mVRSystem->
-                                                 GetTrackedDeviceClass(trackedDevice);
-    if (deviceType != ::vr::TrackedDeviceClass_Controller
-        && deviceType != ::vr::TrackedDeviceClass_GenericTracker) {
-      continue;
-    }
-
-    trackedIndexArray[newControllerCount] = trackedDevice;
-    ++newControllerCount;
-  }
-
-  if (newControllerCount != mControllerCount) {
-    RemoveControllers();
-
-    // Re-adding controllers to VRControllerManager.
-    for (::vr::TrackedDeviceIndex_t i = 0; i < newControllerCount; ++i) {
-      const ::vr::TrackedDeviceIndex_t trackedDevice = trackedIndexArray[i];
-      const ::vr::ETrackedDeviceClass deviceType = mVRSystem->
-                                                   GetTrackedDeviceClass(trackedDevice);
-      const ::vr::ETrackedControllerRole role = mVRSystem->
-                                                GetControllerRoleForTrackedDeviceIndex(
-                                                trackedDevice);
-      const GamepadHand hand = GetGamepadHandFromControllerRole(role);
-      uint32_t numButtons = 0;
-      uint32_t numTriggers = 0;
-      uint32_t numAxes = 0;
-
-      // Scan the axes that the controllers support
-      for (uint32_t j = 0; j < ::vr::k_unControllerStateAxisCount; ++j) {
-        const uint32_t supportAxis = mVRSystem->GetInt32TrackedDeviceProperty(trackedDevice,
-                                      static_cast<vr::TrackedDeviceProperty>(
-                                      ::vr::Prop_Axis0Type_Int32 + j));
-        switch (supportAxis) {
-          case ::vr::EVRControllerAxisType::k_eControllerAxis_Joystick:
-          case ::vr::EVRControllerAxisType::k_eControllerAxis_TrackPad:
-            numAxes += 2; // It has x and y axes.
-            ++numButtons;
-            break;
-          case ::vr::k_eControllerAxis_Trigger:
-            if (j <= 2) {
-              ++numButtons;
-              ++numTriggers;
-            } else {
-          #ifdef DEBUG
-              // SteamVR Knuckles is the only special case for using 2D axis values on triggers.
-              ::vr::ETrackedPropertyError err;
-              uint32_t requiredBufferLen;
-              char charBuf[128];
-              requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(trackedDevice,
-                                  ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
-              MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
-              nsCString deviceId(charBuf);
-              MOZ_ASSERT(deviceId.Find("knuckles") != kNotFound);
-          #endif // #ifdef DEBUG
-              numButtons += 2;
-              numTriggers += 2;
-            }
-            break;
-        }
-      }
-
-      // Scan the buttons that the controllers support
-      const uint64_t supportButtons = mVRSystem->GetUint64TrackedDeviceProperty(
-                                       trackedDevice, ::vr::Prop_SupportedButtons_Uint64);
-      if (supportButtons &
-          BTN_MASK_FROM_ID(k_EButton_A)) {
-        ++numButtons;
-      }
-      if (supportButtons &
-          BTN_MASK_FROM_ID(k_EButton_Grip)) {
-        ++numButtons;
-      }
-      if (supportButtons &
-          BTN_MASK_FROM_ID(k_EButton_ApplicationMenu)) {
-        ++numButtons;
-      }
-      if (supportButtons &
-          BTN_MASK_FROM_ID(k_EButton_DPad_Left)) {
-        ++numButtons;
-      }
-      if (supportButtons &
-          BTN_MASK_FROM_ID(k_EButton_DPad_Up)) {
-        ++numButtons;
-      }
-      if (supportButtons &
-          BTN_MASK_FROM_ID(k_EButton_DPad_Right)) {
-        ++numButtons;
-      }
-      if (supportButtons &
-          BTN_MASK_FROM_ID(k_EButton_DPad_Down)) {
-        ++numButtons;
-      }
-
-      nsCString deviceId;
-      GetControllerDeviceId(deviceType, trackedDevice, deviceId);
-      RefPtr<VRControllerOpenVR> openVRController =
-        new VRControllerOpenVR(hand, mOpenVRHMD->GetDisplayInfo().GetDisplayID(),
-                               numButtons, numTriggers, numAxes, deviceId);
-      openVRController->SetTrackedIndex(trackedDevice);
-      mOpenVRController.AppendElement(openVRController);
-
-      // If the Windows MR controller doesn't has the amount
-      // of buttons or axes as our expectation, switching off
-      // the workaround for Windows MR.
-      if (mIsWindowsMR && (numAxes < 4 || numButtons < 5)) {
-        mIsWindowsMR = false;
-        NS_WARNING("OpenVR - Switching off Windows MR mode.");
-      }
-      // Not already present, add it.
-      AddGamepad(openVRController->GetControllerInfo());
-      ++mControllerCount;
-    }
-  }
-}
-
-void
-VRSystemManagerOpenVR::RemoveControllers()
-{
-  // The controller count is changed, removing the existing gamepads first.
-  for (uint32_t i = 0; i < mOpenVRController.Length(); ++i) {
-    mOpenVRController[i]->ShutdownVibrateHapticThread();
-    RemoveGamepad(i);
-  }
-  mOpenVRController.Clear();
-  mControllerCount = 0;
-}
-
-void
-VRSystemManagerOpenVR::GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType,
-                                             ::vr::TrackedDeviceIndex_t aDeviceIndex, nsCString& aId)
-{
-  switch (aDeviceType) {
-    case ::vr::TrackedDeviceClass_Controller:
-    {
-      ::vr::ETrackedPropertyError err;
-      uint32_t requiredBufferLen;
-      bool isFound = false;
-      char charBuf[128];
-      requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(aDeviceIndex,
-                          ::vr::Prop_RenderModelName_String, charBuf, 128, &err);
-      if (requiredBufferLen > 128) {
-        MOZ_CRASH("Larger than the buffer size.");
-      }
-      MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
-      nsCString deviceId(charBuf);
-      if (deviceId.Find("knuckles") != kNotFound) {
-        aId.AssignLiteral("OpenVR Knuckles");
-        isFound = true;
-      }
-      requiredBufferLen = mVRSystem->GetStringTrackedDeviceProperty(aDeviceIndex,
-        ::vr::Prop_SerialNumber_String, charBuf, 128, &err);
-      if (requiredBufferLen > 128) {
-        MOZ_CRASH("Larger than the buffer size.");
-      }
-      MOZ_ASSERT(requiredBufferLen && err == ::vr::TrackedProp_Success);
-      deviceId.Assign(charBuf);
-      if (deviceId.Find("MRSOURCE") != kNotFound) {
-        aId.AssignLiteral("Spatial Controller (Spatial Interaction Source) ");
-        mIsWindowsMR = true;
-        isFound = true;
-      }
-      if (!isFound) {
-        aId.AssignLiteral("OpenVR Gamepad");
-      }
-      break;
-    }
-    case ::vr::TrackedDeviceClass_GenericTracker:
-    {
-      aId.AssignLiteral("OpenVR Tracker");
-      break;
-    }
-    default:
-      MOZ_ASSERT(false);
-      break;
-  }
-}
deleted file mode 100644
--- a/gfx/vr/gfxVROpenVR.h
+++ /dev/null
@@ -1,181 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef GFX_VR_OPENVR_H
-#define GFX_VR_OPENVR_H
-
-#include "nsTArray.h"
-#include "nsIScreen.h"
-#include "nsCOMPtr.h"
-#include "mozilla/RefPtr.h"
-
-#include "mozilla/gfx/2D.h"
-#include "mozilla/EnumeratedArray.h"
-
-#include "openvr.h"
-#include "gfxVR.h"
-#include "VRDisplayLocal.h"
-
-#if defined(XP_MACOSX)
-class MacIOSurface;
-#endif
-namespace mozilla {
-namespace gfx {
-class VRThread;
-
-namespace impl {
-
-class VRDisplayOpenVR : public VRDisplayLocal
-{
-public:
-  void ZeroSensor() override;
-  bool GetIsHmdPresent();
-
-protected:
-  virtual VRHMDSensorState GetSensorState() override;
-  virtual void StartPresentation() override;
-  virtual void StopPresentation() override;
-#if defined(XP_WIN)
-  virtual bool SubmitFrame(ID3D11Texture2D* aSource,
-                           const IntSize& aSize,
-                           const gfx::Rect& aLeftEyeRect,
-                           const gfx::Rect& aRightEyeRect) override;
-#elif defined(XP_MACOSX)
-  virtual bool SubmitFrame(MacIOSurface* aMacIOSurface,
-                           const IntSize& aSize,
-                           const gfx::Rect& aLeftEyeRect,
-                           const gfx::Rect& aRightEyeRect) override;
-#endif
-
-public:
-  explicit VRDisplayOpenVR(::vr::IVRSystem *aVRSystem,
-                           ::vr::IVRChaperone *aVRChaperone,
-                           ::vr::IVRCompositor *aVRCompositor);
-  void Refresh();
-protected:
-  virtual ~VRDisplayOpenVR();
-  void Destroy();
-
-  // not owned by us; global from OpenVR
-  ::vr::IVRSystem *mVRSystem;
-  ::vr::IVRChaperone *mVRChaperone;
-  ::vr::IVRCompositor *mVRCompositor;
-
-  VRTelemetry mTelemetry;
-  bool mIsPresenting;
-  bool mIsHmdPresent;
-
-  void UpdateStageParameters();
-  void UpdateEyeParameters(gfx::Matrix4x4* aHeadToEyeTransforms = nullptr);
-  bool SubmitFrameOpenVRHandle(void* aTextureHandle,
-                               ::vr::ETextureType aTextureType,
-                               const IntSize& aSize,
-                               const gfx::Rect& aLeftEyeRect,
-                               const gfx::Rect& aRightEyeRect);
-};
-
-class VRControllerOpenVR : public VRControllerHost
-{
-public:
-  explicit VRControllerOpenVR(dom::GamepadHand aHand, uint32_t aDisplayID, uint32_t aNumButtons,
-                              uint32_t aNumTriggers, uint32_t aNumAxes,
-                              const nsCString& aId);
-  void SetTrackedIndex(uint32_t aTrackedIndex);
-  uint32_t GetTrackedIndex();
-  float GetAxisMove(uint32_t aAxis);
-  void SetAxisMove(uint32_t aAxis, float aValue);
-  void SetTrigger(uint32_t aButton, float aValue);
-  float GetTrigger(uint32_t aButton);
-  void SetHand(dom::GamepadHand aHand);
-  void VibrateHaptic(::vr::IVRSystem* aVRSystem,
-                     uint32_t aHapticIndex,
-                     double aIntensity,
-                     double aDuration,
-                     const VRManagerPromise& aPromise);
-  void StopVibrateHaptic();
-  void ShutdownVibrateHapticThread();
-
-protected:
-  virtual ~VRControllerOpenVR();
-
-private:
-  void UpdateVibrateHaptic(::vr::IVRSystem* aVRSystem,
-                           uint32_t aHapticIndex,
-                           double aIntensity,
-                           double aDuration,
-                           uint64_t aVibrateIndex,
-                           const VRManagerPromise& aPromise);
-  void VibrateHapticComplete(const VRManagerPromise& aPromise);
-
-  // The index of tracked devices from ::vr::IVRSystem.
-  uint32_t mTrackedIndex;
-  RefPtr<VRThread> mVibrateThread;
-  Atomic<bool> mIsVibrateStopped;
-};
-
-} // namespace impl
-
-class VRSystemManagerOpenVR : public VRSystemManager
-{
-public:
-  static already_AddRefed<VRSystemManagerOpenVR> Create();
-
-  virtual void Destroy() override;
-  virtual void Shutdown() override;
-  virtual void NotifyVSync() override;
-  virtual void Enumerate() override;
-  virtual bool ShouldInhibitEnumeration() override;
-  virtual void GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult) override;
-  virtual bool GetIsPresenting() override;
-  virtual void HandleInput() override;
-  virtual void GetControllers(nsTArray<RefPtr<VRControllerHost>>&
-                              aControllerResult) override;
-  virtual void ScanForControllers() override;
-  virtual void RemoveControllers() override;
-  virtual void VibrateHaptic(uint32_t aControllerIdx,
-                             uint32_t aHapticIndex,
-                             double aIntensity,
-                             double aDuration,
-                             const VRManagerPromise& aPromise) override;
-  virtual void StopVibrateHaptic(uint32_t aControllerIdx) override;
-
-protected:
-  VRSystemManagerOpenVR();
-
-private:
-  bool HandleButtonPress(uint32_t aControllerIdx,
-                         uint32_t aButton,
-                         uint64_t aButtonMask,
-                         uint64_t aButtonPressed,
-                         uint64_t aButtonTouched);
-  bool HandleTriggerPress(uint32_t aControllerIdx,
-                          uint32_t aButton,
-                          uint32_t aTrigger,
-                          float aValue);
-  bool HandleAxisMove(uint32_t aControllerIdx, uint32_t aAxis,
-                      float aValue);
-  void HandlePoseTracking(uint32_t aControllerIdx,
-                          const dom::GamepadPoseState& aPose,
-                          VRControllerHost* aController);
-  dom::GamepadHand GetGamepadHandFromControllerRole(
-                          ::vr::ETrackedControllerRole aRole);
-  void GetControllerDeviceId(::vr::ETrackedDeviceClass aDeviceType,
-                             ::vr::TrackedDeviceIndex_t aDeviceIndex,
-                             nsCString& aId);
-
-  // there can only be one
-  RefPtr<impl::VRDisplayOpenVR> mOpenVRHMD;
-  nsTArray<RefPtr<impl::VRControllerOpenVR>> mOpenVRController;
-  ::vr::IVRSystem *mVRSystem;
-  bool mRuntimeCheckFailed;
-  bool mIsWindowsMR;
-};
-
-} // namespace gfx
-} // namespace mozilla
-
-
-#endif /* GFX_VR_OPENVR_H */
--- a/gfx/vr/moz.build
+++ b/gfx/vr/moz.build
@@ -56,63 +56,33 @@ UNIFIED_SOURCES += [
 # often conflict with our own types.
 SOURCES += [
     'gfxVRExternal.cpp',
     'gfxVRPuppet.cpp',
     'VRDisplayHost.cpp',
     'VRDisplayLocal.cpp',
 ]
 
-# Build OpenVR on Windows, Linux, and macOS desktop targets
-if CONFIG['OS_TARGET'] in ('WINNT', 'Linux', 'Darwin'):
-    DIRS += [
-        'openvr',
-    ]
-    SOURCES += [
-        'gfxVROpenVR.cpp',
-    ]
-
-if CONFIG['OS_TARGET'] == 'WINNT':
-    SOURCES += [
-        'gfxVROculus.cpp',
-    ]
-
 if CONFIG['OS_TARGET'] == 'Android':
     LOCAL_INCLUDES += ['/widget/android']
 else:
     DIRS += [
         'service',
     ]
 
 IPDL_SOURCES = [
     'ipc/PVR.ipdl',
     'ipc/PVRGPU.ipdl',
     'ipc/PVRLayer.ipdl',
     'ipc/PVRManager.ipdl',
 ]
 
-# For building with the real SDK instead of our local hack
-#SOURCES += [
-#    'OVR_CAPI_Util.cpp',
-#    'OVR_CAPIShim.c',
-#    'OVR_StereoProjection.cpp',
-#]
-#
-#CXXFLAGS += ["-Ic:/proj/ovr/OculusSDK-0.6.0-beta/LibOVR/Include"]
-#CFLAGS += ["-Ic:/proj/ovr/OculusSDK-0.6.0-beta/LibOVR/Include"]
-
 CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
 CXXFLAGS += CONFIG['TK_CFLAGS']
 CFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
 CFLAGS += CONFIG['TK_CFLAGS']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
-# This is intended as a temporary hack to enable VS2015 builds.
-if CONFIG['CC_TYPE'] in ('msvc', 'clang-cl'):
-    # ovr_capi_dynamic.h '<unnamed-tag>': Alignment specifier is less than
-    # actual alignment (8), and will be ignored
-    CXXFLAGS += ['-wd4359']
-
 if CONFIG['CC_TYPE'] == 'clang-cl':
     AllowCompilerWarnings()  # workaround for bug 1090497
--- a/gfx/vr/service/OculusSession.h
+++ b/gfx/vr/service/OculusSession.h
@@ -7,17 +7,17 @@
 #ifndef GFX_VR_SERVICE_OCULUSSESSION_H
 #define GFX_VR_SERVICE_OCULUSSESSION_H
 
 #include "VRSession.h"
 
 #include "mozilla/gfx/2D.h"
 #include "moz_external_vr.h"
 #include "nsTArray.h"
-#include "../ovr_capi_dynamic.h"
+#include "oculus/ovr_capi_dynamic.h"
 #include "prlink.h"
 #include "ShaderDefinitionsD3D11.h" // for VertexShaderConstants and PixelShaderConstants
 
 struct ID3D11Device;
 
 namespace mozilla {
 namespace layers {
 struct VertexShaderConstants;
--- a/gfx/vr/service/moz.build
+++ b/gfx/vr/service/moz.build
@@ -16,16 +16,19 @@ if CONFIG['OS_TARGET'] != 'Android':
         'OSVRSession.cpp',
         'VRService.cpp',
         'VRSession.cpp',
     ]
     include('/ipc/chromium/chromium-config.mozbuild')
 
 # Build OpenVR on Windows, Linux, and macOS desktop targets
 if CONFIG['OS_TARGET'] in ('WINNT', 'Linux', 'Darwin'):
+    DIRS += [
+        'openvr',
+    ]
     LOCAL_INCLUDES += [
         '/dom/base',
         '/gfx/layers/d3d11'
     ]
 
     # OpenVRSession includes MacIOSurface.h which includes Mac headers
     # which define Size and Points types in the root namespace that
     # often conflict with our own types.
rename from gfx/vr/ovr_capi_dynamic.h
rename to gfx/vr/service/oculus/ovr_capi_dynamic.h
rename from gfx/vr/openvr/LICENSE
rename to gfx/vr/service/openvr/LICENSE
rename from gfx/vr/openvr/README.md
rename to gfx/vr/service/openvr/README.md
rename from gfx/vr/openvr/README.mozilla
rename to gfx/vr/service/openvr/README.mozilla
--- a/gfx/vr/openvr/README.mozilla
+++ b/gfx/vr/service/openvr/README.mozilla
@@ -21,25 +21,26 @@ We only use some files from the SDK:
   files to configure the library.
 
 - The "src/jsoncpp.cpp" file and the "src/json" directory can be skipped. OpenVR
   uses the jsoncpp library, which we have already imported elsewhere.
 
 
 Steps to update the library:
 
-- Copy "README.md" from the root of the openvr repo to the "gfx/vr/openvr" directory.
+- Copy "README.md" from the root of the openvr repo to the "gfx/vr/service/openvr"
+  directory.
 
-- Copy "headers/openvr.h" to "gfx/vr/openvr/headers" directory. The other files
+- Copy "headers/openvr.h" to "gfx/vr/service/openvr/headers" directory. The other files
   in this directory can be ignored.
 
 - The rest of the files in the "src" directory and the "src/vrcommon" are copied
-  to the "gfx/vr/openvr/src" directory.
+  to the "gfx/vr/service/openvr/src" directory.
 
-- Update "gfx/vr/openvr/moz.build" when files are added or removed.
+- Update "gfx/vr/service/openvr/moz.build" when files are added or removed.
 
 - Update the "strtools_public.h" and "strtools_public.cpp" files, commenting out
   the "Uint64ToString", "wcsncpy_s", and "strncpy_s" functions.
   The "Uint64ToString" function name conflicts with another used in Gecko and
   the "errno_t" return type returned by the other functions is not defined in
   Mozilla's macOS continuous integration build environments.  Fortunately, the
   OpenVR SDK does not use these functions.
 
rename from gfx/vr/openvr/headers/openvr.h
rename to gfx/vr/service/openvr/headers/openvr.h
rename from gfx/vr/openvr/moz.build
rename to gfx/vr/service/openvr/moz.build
rename from gfx/vr/openvr/src/README
rename to gfx/vr/service/openvr/src/README
rename from gfx/vr/openvr/src/dirtools_public.cpp
rename to gfx/vr/service/openvr/src/dirtools_public.cpp
rename from gfx/vr/openvr/src/dirtools_public.h
rename to gfx/vr/service/openvr/src/dirtools_public.h
rename from gfx/vr/openvr/src/envvartools_public.cpp
rename to gfx/vr/service/openvr/src/envvartools_public.cpp
rename from gfx/vr/openvr/src/envvartools_public.h
rename to gfx/vr/service/openvr/src/envvartools_public.h
rename from gfx/vr/openvr/src/hmderrors_public.cpp
rename to gfx/vr/service/openvr/src/hmderrors_public.cpp
rename from gfx/vr/openvr/src/hmderrors_public.h
rename to gfx/vr/service/openvr/src/hmderrors_public.h
rename from gfx/vr/openvr/src/ivrclientcore.h
rename to gfx/vr/service/openvr/src/ivrclientcore.h
rename from gfx/vr/openvr/src/openvr_api_public.cpp
rename to gfx/vr/service/openvr/src/openvr_api_public.cpp
rename from gfx/vr/openvr/src/pathtools_public.cpp
rename to gfx/vr/service/openvr/src/pathtools_public.cpp
rename from gfx/vr/openvr/src/pathtools_public.h
rename to gfx/vr/service/openvr/src/pathtools_public.h
rename from gfx/vr/openvr/src/sharedlibtools_public.cpp
rename to gfx/vr/service/openvr/src/sharedlibtools_public.cpp
rename from gfx/vr/openvr/src/sharedlibtools_public.h
rename to gfx/vr/service/openvr/src/sharedlibtools_public.h
rename from gfx/vr/openvr/src/strtools_public.cpp
rename to gfx/vr/service/openvr/src/strtools_public.cpp
rename from gfx/vr/openvr/src/strtools_public.h
rename to gfx/vr/service/openvr/src/strtools_public.h
rename from gfx/vr/openvr/src/vrpathregistry_public.cpp
rename to gfx/vr/service/openvr/src/vrpathregistry_public.cpp
rename from gfx/vr/openvr/src/vrpathregistry_public.h
rename to gfx/vr/service/openvr/src/vrpathregistry_public.h
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -541,18 +541,39 @@ public:
     AxisEdgeType aEdge,
     const FlexboxAxisTracker& aAxisTracker,
     bool aUseFirstLineBaseline) const;
 
   float GetShareOfWeightSoFar() const { return mShareOfWeightSoFar; }
 
   bool IsFrozen() const            { return mIsFrozen; }
 
-  bool HadMinViolation() const     { return mHadMinViolation; }
-  bool HadMaxViolation() const     { return mHadMaxViolation; }
+  bool HadMinViolation() const
+  {
+    MOZ_ASSERT(!mIsFrozen, "min violation has no meaning for frozen items.");
+    return mHadMinViolation;
+  }
+
+  bool HadMaxViolation() const
+  {
+    MOZ_ASSERT(!mIsFrozen, "max violation has no meaning for frozen items.");
+    return mHadMaxViolation;
+  }
+
+  bool WasMinClamped() const
+  {
+    MOZ_ASSERT(mIsFrozen, "min clamping has no meaning for unfrozen items.");
+    return mHadMinViolation;
+  }
+
+  bool WasMaxClamped() const
+  {
+    MOZ_ASSERT(mIsFrozen, "max clamping has no meaning for unfrozen items.");
+    return mHadMaxViolation;
+  }
 
   // Indicates whether this item received a preliminary "measuring" reflow
   // before its actual reflow.
   bool HadMeasuringReflow() const  { return mHadMeasuringReflow; }
 
   // Indicates whether this item's computed cross-size property is 'auto'.
   bool IsCrossSizeAuto() const;
 
@@ -735,34 +756,67 @@ public:
   void SetShareOfWeightSoFar(float aNewShare)
   {
     MOZ_ASSERT(!mIsFrozen || aNewShare == 0.0f,
                "shouldn't be giving this item any share of the weight "
                "after it's frozen");
     mShareOfWeightSoFar = aNewShare;
   }
 
-  void Freeze() { mIsFrozen = true; }
+  void Freeze()
+  {
+    mIsFrozen = true;
+    // Now that we are frozen, the meaning of mHadMinViolation and
+    // mHadMaxViolation changes to indicate min and max clamping. Clear
+    // both of the member variables so that they are ready to be set
+    // as clamping state later, if necessary.
+    mHadMinViolation = false;
+    mHadMaxViolation = false;
+  }
 
   void SetHadMinViolation()
   {
     MOZ_ASSERT(!mIsFrozen,
                "shouldn't be changing main size & having violations "
                "after we're frozen");
     mHadMinViolation = true;
   }
   void SetHadMaxViolation()
   {
     MOZ_ASSERT(!mIsFrozen,
                "shouldn't be changing main size & having violations "
                "after we're frozen");
     mHadMaxViolation = true;
   }
   void ClearViolationFlags()
-  { mHadMinViolation = mHadMaxViolation = false; }
+  {
+    MOZ_ASSERT(!mIsFrozen,
+               "shouldn't be altering violation flags after we're "
+               "frozen");
+    mHadMinViolation = mHadMaxViolation = false;
+  }
+
+  void SetWasMinClamped()
+  {
+    MOZ_ASSERT(!mHadMinViolation && !mHadMaxViolation, "only clamp once");
+    // This reuses the mHadMinViolation member variable to track clamping
+    // events. This is allowable because mHadMinViolation only reflects
+    // a violation up until the item is frozen.
+    MOZ_ASSERT(mIsFrozen, "shouldn't set clamping state when we are unfrozen");
+    mHadMinViolation = true;
+  }
+  void SetWasMaxClamped()
+  {
+    MOZ_ASSERT(!mHadMinViolation && !mHadMaxViolation, "only clamp once");
+    // This reuses the mHadMaxViolation member variable to track clamping
+    // events. This is allowable because mHadMaxViolation only reflects
+    // a violation up until the item is frozen.
+    MOZ_ASSERT(mIsFrozen, "shouldn't set clamping state when we are unfrozen");
+    mHadMaxViolation = true;
+  }
 
   // Setters for values that are determined after we've resolved our main size
   // -------------------------------------------------------------------------
 
   // Sets the main-axis position of our flex item's content-box.
   // (This is the distance between the main-start edge of the flex container
   // and the main-start edge of the flex item's content-box.)
   void SetMainPosition(nscoord aPosn) {
@@ -1398,16 +1452,21 @@ nsFlexContainerFrame::GenerateFlexItemFo
                                    crossMinSize, crossMaxSize,
                                    aAxisTracker);
 
   // If we're inflexible, we can just freeze to our hypothetical main-size
   // up-front. Similarly, if we're a fixed-size widget, we only have one
   // valid size, so we freeze to keep ourselves from flexing.
   if (isFixedSizeWidget || (flexGrow == 0.0f && flexShrink == 0.0f)) {
     item->Freeze();
+    if (flexBaseSize < mainMinSize) {
+      item->SetWasMinClamped();
+    } else if (flexBaseSize > mainMaxSize) {
+      item->SetWasMaxClamped();
+    }
   }
 
   // Resolve "flex-basis:auto" and/or "min-[width|height]:auto" (which might
   // require us to reflow the item to measure content height)
   ResolveAutoFlexBasisAndMinSize(aPresContext, *item,
                                  childRI, aAxisTracker);
   return item;
 }
@@ -2549,16 +2608,21 @@ FlexLine::FreezeItemsEarly(bool aIsUsing
           if (item->GetFlexBaseSize() < item->GetMainSize()) {
             shouldFreeze = true;
           }
         }
       }
       if (shouldFreeze) {
         // Freeze item! (at its hypothetical main size)
         item->Freeze();
+        if (item->GetFlexBaseSize() < item->GetMainSize()) {
+          item->SetWasMinClamped();
+        } else if (item->GetFlexBaseSize() > item->GetMainSize()) {
+          item->SetWasMaxClamped();
+        }
         mNumFrozenItems++;
       }
     }
   }
 }
 
 // Based on the sign of aTotalViolation, this function freezes a subset of our
 // flexible sizes, and restores the remaining ones to their initial pref sizes.
@@ -2588,39 +2652,48 @@ FlexLine::FreezeOrRestoreEachFlexibleSiz
        numUnfrozenItemsToBeSeen > 0; item = item->getNext()) {
     MOZ_ASSERT(item, "numUnfrozenItemsToBeSeen says items remain to be seen");
     if (!item->IsFrozen()) {
       numUnfrozenItemsToBeSeen--;
 
       MOZ_ASSERT(!item->HadMinViolation() || !item->HadMaxViolation(),
                  "Can have either min or max violation, but not both");
 
+      bool hadMinViolation = item->HadMinViolation();
+      bool hadMaxViolation = item->HadMaxViolation();
       if (eFreezeEverything == freezeType ||
-          (eFreezeMinViolations == freezeType && item->HadMinViolation()) ||
-          (eFreezeMaxViolations == freezeType && item->HadMaxViolation())) {
+          (eFreezeMinViolations == freezeType && hadMinViolation) ||
+          (eFreezeMaxViolations == freezeType && hadMaxViolation)) {
 
         MOZ_ASSERT(item->GetMainSize() >= item->GetMainMinSize(),
                    "Freezing item at a size below its minimum");
         MOZ_ASSERT(item->GetMainSize() <= item->GetMainMaxSize(),
                    "Freezing item at a size above its maximum");
 
         item->Freeze();
+        if (hadMinViolation) {
+          item->SetWasMinClamped();
+        } else if (hadMaxViolation) {
+          item->SetWasMaxClamped();
+        }
         mNumFrozenItems++;
       } else if (MOZ_UNLIKELY(aIsFinalIteration)) {
         // XXXdholbert If & when bug 765861 is fixed, we should upgrade this
         // assertion to be fatal except in documents with enormous lengths.
         NS_ERROR("Final iteration still has unfrozen items, this shouldn't"
                  " happen unless there was nscoord under/overflow.");
         item->Freeze();
         mNumFrozenItems++;
       } // else, we'll reset this item's main size to its flex base size on the
         // next iteration of this algorithm.
 
-      // Clear this item's violation(s), now that we've dealt with them
-      item->ClearViolationFlags();
+      if (!item->IsFrozen()) {
+        // Clear this item's violation(s), now that we've dealt with them
+        item->ClearViolationFlags();
+      }
     }
   }
 }
 
 void
 FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize,
                                  ComputedFlexLineInfo* aLineInfo)
 {
@@ -4806,16 +4879,38 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
   for (FlexLine* line = lines.getFirst(); line; line = line->getNext(),
                                                 ++lineIndex) {
     ComputedFlexLineInfo* lineInfo = containerInfo ?
                                      &containerInfo->mLines[lineIndex] :
                                      nullptr;
     line->ResolveFlexibleLengths(aContentBoxMainSize, lineInfo);
   }
 
+  // If needed, capture the final clamp state from all the items.
+  if (containerInfo) {
+    uint32_t lineIndex = 0;
+    for (const FlexLine* line = lines.getFirst(); line;
+         line = line->getNext(), ++lineIndex) {
+      ComputedFlexLineInfo* lineInfo = &containerInfo->mLines[lineIndex];
+
+      uint32_t itemIndex = 0;
+      for (const FlexItem* item = line->GetFirstItem(); item;
+           item = item->getNext(), ++itemIndex) {
+        ComputedFlexItemInfo* itemInfo = &lineInfo->mItems[itemIndex];
+
+        itemInfo->mClampState =
+          item->WasMinClamped() ?
+          mozilla::dom::FlexItemClampState::Clamped_to_min :
+          (item->WasMaxClamped() ?
+           mozilla::dom::FlexItemClampState::Clamped_to_max :
+           mozilla::dom::FlexItemClampState::Unclamped);
+      }
+    }
+  }
+
   // Cross Size Determination - Flexbox spec section 9.4
   // ===================================================
   // Calculate the hypothetical cross size of each item:
 
   // 'sumLineCrossSizes' includes the size of all gaps between lines
   nscoord sumLineCrossSizes = 0;
   for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
     for (FlexItem* item = line->GetFirstItem(); item; item = item->getNext()) {
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -49,16 +49,17 @@ struct ComputedFlexItemInfo
    * algorithm proceeds linearly, the mMainDeltaSize for an item only
    * respects the resolved size of items already frozen.
    */
   nscoord mMainDeltaSize;
   nscoord mMainMinSize;
   nscoord mMainMaxSize;
   nscoord mCrossMinSize;
   nscoord mCrossMaxSize;
+  mozilla::dom::FlexItemClampState mClampState;
 };
 
 struct ComputedFlexLineInfo
 {
   nsTArray<ComputedFlexItemInfo> mItems;
   nscoord mCrossStart;
   nscoord mCrossSize;
   nscoord mFirstBaselineOffset;
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5500,18 +5500,18 @@ pref("urlclassifier.phishTable", "googpu
 // Tables for application reputation
 pref("urlclassifier.downloadAllowTable", "goog-downloadwhite-proto");
 pref("urlclassifier.downloadBlockTable", "goog-badbinurl-proto");
 
 // Tables for login reputation
 pref("urlclassifier.passwordAllowTable", "goog-passwordwhite-proto");
 
 // Tables for anti-tracking features
-#ifdef NIGHTLY_BUILD
-// Use the strict list for the default cookie restrictions in Nightly
+#ifdef EARLY_BETA_OR_EARLIER
+// Use the strict list for the default cookie restrictions in Nightly and early betas
 pref("urlclassifier.trackingAnnotationTable", "test-track-simple,base-track-digest256,content-track-digest256");
 #else
 pref("urlclassifier.trackingAnnotationTable", "test-track-simple,base-track-digest256");
 #endif
 pref("urlclassifier.trackingAnnotationWhitelistTable", "test-trackwhite-simple,mozstd-trackwhite-digest256");
 pref("urlclassifier.trackingTable", "test-track-simple,base-track-digest256");
 pref("urlclassifier.trackingWhitelistTable", "test-trackwhite-simple,mozstd-trackwhite-digest256");
 
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -1640,26 +1640,34 @@ HttpChannelParent::OnStopRequest(nsIRequ
 
     isLocal = isLocal || IsLoopBackAddress(&peerAddr);
 
     if (!isLocal) {
       if (!mHasSuspendedByBackPressure) {
         AccumulateCategorical(Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::NotSuspended);
       } else {
         AccumulateCategorical(Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::Suspended);
+
+        // Only analyze non-local suspended cases, which we are interested in.
+        nsCOMPtr<nsILoadInfo> loadInfo;
+        if (NS_SUCCEEDED(mChannel->GetLoadInfo(getter_AddRefs(loadInfo)))) {
+          nsContentPolicyType type = loadInfo ?
+                                     loadInfo->InternalContentPolicyType() :
+                                     nsIContentPolicy::TYPE_OTHER;
+          Telemetry::Accumulate(Telemetry::NETWORK_BACK_PRESSURE_SUSPENSION_CP_TYPE, type);
+        }
       }
     } else {
       if (!mHasSuspendedByBackPressure) {
         AccumulateCategorical(Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::NotSuspendedLocal);
       } else {
         AccumulateCategorical(Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::SuspendedLocal);
       }
     }
   }
-
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsIStreamListener
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
@@ -1715,16 +1723,21 @@ HttpChannelParent::OnDataAvailable(nsIRe
 
   if (NeedFlowControl()) {
     // We're going to run out of sending window size
     if (mSendWindowSize > 0 && mSendWindowSize <= count) {
       MOZ_ASSERT(!mSuspendedForFlowControl);
       Unused << mChannel->Suspend();
       mSuspendedForFlowControl = true;
       mHasSuspendedByBackPressure = true;
+    } else if (!mResumedTimestamp.IsNull()) {
+      // Calculate the delay when the first packet arrived after resume
+      Telemetry::AccumulateTimeDelta(Telemetry::NETWORK_BACK_PRESSURE_SUSPENSION_DELAY_TIME_MS,
+                                     mResumedTimestamp);
+      mResumedTimestamp = TimeStamp();
     }
     mSendWindowSize -= count;
   }
 
   return NS_OK;
 }
 
 bool
@@ -1763,16 +1776,18 @@ HttpChannelParent::RecvBytesRead(const i
   }
 
   LOG(("HttpChannelParent::RecvBytesRead [this=%p count=%" PRId32 "]\n", this, aCount));
 
   if (mSendWindowSize <= 0 && mSendWindowSize + aCount > 0) {
     MOZ_ASSERT(mSuspendedForFlowControl);
     Unused << mChannel->Resume();
     mSuspendedForFlowControl = false;
+
+    mResumedTimestamp = TimeStamp::Now();
   }
   mSendWindowSize += aCount;
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 HttpChannelParent::RecvOpenOriginalCacheInputStream()
 {
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -294,16 +294,19 @@ private:
 
   RefPtr<HttpBackgroundChannelParent> mBgParent;
 
   MozPromiseHolder<GenericPromise> mPromise;
   MozPromiseRequestHolder<GenericPromise> mRequest;
 
   dom::TabId mNestedFrameId;
 
+  // To calculate the delay caused by the e10s back-pressure suspension
+  TimeStamp mResumedTimestamp;
+
   Atomic<bool> mIPCClosed; // PHttpChannel actor has been Closed()
 
   // Corresponding redirect channel registrar Id. 0 means redirection is not started.
   uint32_t mRedirectRegistrarId = 0;
 
   PBOverrideStatus mPBOverride;
 
   // Set to the canceled status value if the main channel was canceled.
--- a/python/mozbuild/mozbuild/action/test_archive.py
+++ b/python/mozbuild/mozbuild/action/test_archive.py
@@ -298,17 +298,25 @@ ARCHIVE_FILES = {
             ],
             'dest': 'bin/components',
         },
         {
             'source': buildconfig.topsrcdir,
             'base': 'build/pgo/certs',
             'pattern': '**',
             'dest': 'certs',
-        }
+        },
+        {
+            'source': buildconfig.topobjdir,
+            'base': 'build/unix/elfhack',
+            'patterns': [
+                'elfhack%s' % buildconfig.substs['BIN_SUFFIX'],
+            ],
+            'dest': 'bin',
+        },
     ],
     'cppunittest': [
         {
             'source': STAGE,
             'base': '',
             'pattern': 'cppunittest/**',
         },
         # We don't ship these files if startup cache is disabled, which is
--- a/python/mozbuild/mozbuild/controller/building.py
+++ b/python/mozbuild/mozbuild/controller/building.py
@@ -1326,16 +1326,17 @@ class BuildDriver(MozbuildObject):
 
         return status
 
     def configure(self, options=None, buildstatus_messages=False,
                   line_handler=None):
         # Disable indexing in objdir because it is not necessary and can slow
         # down builds.
         mkdir(self.topobjdir, not_indexed=True)
+        self._write_mozconfig_json()
 
         def on_line(line):
             self.log(logging.INFO, 'build_output', {'line': line}, '{line}')
 
         line_handler = line_handler or on_line
 
         options = ' '.join(shell_quote(o) for o in options or ())
         append_env = {b'CONFIGURE_ARGS': options.encode('utf-8')}
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -1699,24 +1699,18 @@ class StaticAnalysis(MachCommandBase):
             StaticAnalysisFooter,
             StaticAnalysisOutputManager,
         )
 
         self._set_log_level(verbose)
         self.log_manager.enable_all_structured_loggers()
 
         rc = self._build_compile_db(verbose=verbose)
-        if rc != 0:
-            return rc
-
-        rc = self._build_export(jobs=jobs, verbose=verbose)
-        if rc != 0:
-            return rc
-
-        rc = self._get_clang_tools(verbose=verbose)
+        rc = rc or self._build_export(jobs=jobs, verbose=verbose)
+        rc = rc or self._get_clang_tools(verbose=verbose)
         if rc != 0:
             return rc
 
         compile_db = json.loads(open(self._compile_db, 'r').read())
         total = 0
         import re
         name_re = re.compile('(' + ')|('.join(source) + ')')
         for f in compile_db:
@@ -1771,16 +1765,19 @@ class StaticAnalysis(MachCommandBase):
                    task='compileLocalWithGeckoBinariesNoMinApiDebugSources',
                    skip_export=False):
         self._set_log_level(verbose)
         self.log_manager.enable_all_structured_loggers()
         if self.substs['MOZ_BUILD_APP'] != 'mobile/android':
             self.log(logging.WARNING, 'static-analysis', {},
                      'Cannot check java source code unless you are building for android!')
             return 1
+        rc = self._check_for_java()
+        if rc != 0:
+            return 1
         # if source contains the whole mobile folder, then we just have to
         # analyze everything
         check_all = any(i.rstrip(os.sep).split(os.sep)[-1] == 'mobile' for i in source)
         # gather all java sources from the source variable
         java_sources = []
         if not check_all:
             java_sources = self._get_java_files(source)
             if not java_sources:
@@ -1795,31 +1792,25 @@ class StaticAnalysis(MachCommandBase):
                      'This command is only available for linux64!')
             return rc
         # which checkers to use, and which folders to exclude
         all_checkers, third_party_path = self._get_infer_config()
         checkers, excludes = self._get_infer_args(
             checks=checks or all_checkers,
             third_party_path=third_party_path
         )
-        gradlew = mozpath.join(self.topsrcdir, 'gradlew')
+        rc = rc or self._gradle(['clean'])  # clean so that we can recompile
         # infer capture command
-        capture_cmd = [self._infer_path, 'capture'] + excludes + \
-            ['--', gradlew, task]
+        capture_cmd = [self._infer_path, 'capture'] + excludes + ['--']
+        rc = rc or self._gradle([task], infer_args=capture_cmd, verbose=verbose)
         tmp_file, args = self._get_infer_source_args(java_sources)
         # infer analyze command
         analysis_cmd = [self._infer_path, 'analyze', '--keep-going'] +  \
             checkers + args
-        # capture, then analyze the sources
-        for args in [[gradlew, 'clean'], capture_cmd, analysis_cmd]:
-            rc = self.run_process(args=args, cwd=self.topsrcdir,
-                                  pass_thru=True)
-            # if a command fails, break and close the tmp file before returning
-            if rc != 0:
-                break
+        rc = rc or self.run_process(args=analysis_cmd, cwd=self.topsrcdir, pass_thru=True)
         if tmp_file:
             tmp_file.close()
         return rc
 
     def _get_java_files(self, sources):
         java_sources = []
         for i in sources:
             f = mozpath.join(self.topsrcdir, i)
@@ -1913,16 +1904,57 @@ class StaticAnalysis(MachCommandBase):
         if fix:
             common_args += '-fix'
 
         return [
             self.virtualenv_manager.python_path, self._run_clang_tidy_path, '-j',
             str(jobs), '-p', self._compilation_commands_path
         ] + common_args + sources
 
+    def _check_for_java(self):
+        '''Check if javac can be found.'''
+        import distutils
+        java = self.substs.get('JAVA')
+        java = java or os.getenv('JAVA_HOME')
+        java = java or distutils.spawn.find_executable('javac')
+        error = 'javac was not found! Please install javac and either add it to your PATH, '
+        error += 'set JAVA_HOME, or add the following to your mozconfig:\n'
+        error += '  --with-java-bin-path=/path/to/java/bin/'
+        if not java:
+            self.log(logging.ERROR, 'ERROR: static-analysis', {}, error)
+            return 1
+        return 0
+
+    def _gradle(self, args, infer_args=None, verbose=False, autotest=False,
+                suppress_output=True):
+        infer_args = infer_args or []
+        if autotest:
+            cwd = mozpath.join(self.topsrcdir, 'tools', 'infer', 'test')
+            gradle = mozpath.join(cwd, 'gradlew')
+        else:
+            gradle = self.substs['GRADLE']
+            cwd = self.topsrcdir
+        extra_env = {
+            'GRADLE_OPTS': '-Dfile.encoding=utf-8',  # see mobile/android/mach_commands.py
+            'JAVA_TOOL_OPTIONS': '-Dfile.encoding=utf-8',
+        }
+        if suppress_output:
+            devnull = open(os.devnull, 'w')
+            return subprocess.call(
+                infer_args + [gradle] + args,
+                env=dict(os.environ, **extra_env),
+                cwd=cwd, stdout=devnull, stderr=subprocess.STDOUT, close_fds=True)
+        else:
+            return self.run_process(
+                infer_args + [gradle] + args,
+                append_env=extra_env,
+                pass_thru=True,  # Allow user to run gradle interactively.
+                ensure_exit_code=False,  # Don't throw on non-zero exit code.
+                cwd=cwd)
+
     @StaticAnalysisSubCommand('static-analysis', 'autotest',
                               'Run the auto-test suite in order to determine that'
                               ' the analysis did not regress.')
     @CommandArgument('--dump-results', '-d', default=False, action='store_true',
                      help='Generate the baseline for the regression test. Based on'
                      ' this baseline we will test future results.')
     @CommandArgument('--intree-tool', '-i', default=False, action='store_true',
                      help='Use a pre-aquired in-tree clang-tidy package.')
@@ -1930,32 +1962,30 @@ class StaticAnalysis(MachCommandBase):
                      help='Checkers that are going to be auto-tested.')
     def autotest(self, verbose=False, dump_results=False, intree_tool=False, checker_names=[]):
         # If 'dump_results' is True than we just want to generate the issues files for each
         # checker in particulat and thus 'force_download' becomes 'False' since we want to
         # do this on a local trusted clang-tidy package.
         self._set_log_level(verbose)
         self._dump_results = dump_results
 
-        force_download = True
-
-        if self._dump_results:
-            force_download = False
+        force_download = not self._dump_results
 
         # Function return codes
         self.TOOLS_SUCCESS = 0
         self.TOOLS_FAILED_DOWNLOAD = 1
         self.TOOLS_UNSUPORTED_PLATFORM = 2
         self.TOOLS_CHECKER_NO_TEST_FILE = 3
         self.TOOLS_CHECKER_RETURNED_NO_ISSUES = 4
         self.TOOLS_CHECKER_RESULT_FILE_NOT_FOUND = 5
         self.TOOLS_CHECKER_DIFF_FAILED = 6
         self.TOOLS_CHECKER_NOT_FOUND = 7
         self.TOOLS_CHECKER_FAILED_FILE = 8
         self.TOOLS_CHECKER_LIST_EMPTY = 9
+        self.TOOLS_GRADLE_FAILED = 10
 
         # Configure the tree or download clang-tidy package, depending on the option that we choose
         if intree_tool:
             _, config, _ = self._get_config_environment()
             clang_tools_path = self.topsrcdir
             self._clang_tidy_path = mozpath.join(
                 clang_tools_path, "clang-tidy", "bin",
                 "clang-tidy" + config.substs.get('BIN_SUFFIX', ''))
@@ -2053,17 +2083,17 @@ class StaticAnalysis(MachCommandBase):
                 ret_val = self._run_analysis_batch(checkers_test_batch)
                 if ret_val != self.TOOLS_SUCCESS:
                     shutil.rmtree(self._compilation_commands_path)
                     return ret_val
 
         self.log(logging.INFO, 'static-analysis', {}, "SUCCESS: clang-tidy all tests passed.")
         # Also delete the tmp folder
         shutil.rmtree(self._compilation_commands_path)
-        return self.TOOLS_SUCCESS
+        return self._autotest_infer(intree_tool, force_download, verbose)
 
     def _run_analysis(self, checks, header_filter, sources, jobs=1, fix=False, print_out=False):
         cmd = self._get_clang_tidy_command(
             checks=checks, header_filter=header_filter,
             sources=sources,
             jobs=jobs, fix=fix)
 
         try:
@@ -2106,25 +2136,148 @@ class StaticAnalysis(MachCommandBase):
             compile_commands = []
             director = mozpath.join(self.topsrcdir, 'tools', 'clang-tidy', 'test')
             for item in config['clang_checkers']:
                 if item['name'] in ['-*', 'mozilla-*']:
                     continue
                 file = item['name'] + '.cpp'
                 element = {}
                 element["directory"] = director
-                element["command"] = 'cpp '+ file
+                element["command"] = 'cpp ' + file
                 element["file"] = mozpath.join(director, file)
                 compile_commands.append(element)
 
             json.dump(compile_commands, file_handler)
             file_handler.flush()
 
             return directory
 
+    def _autotest_infer(self, intree_tool, force_download, verbose):
+        # infer is not available on other platforms, but autotest should work even without
+        # it being installed
+        if self.platform[0] == 'linux64':
+            rc = self._check_for_java()
+            if rc != 0:
+                return 1
+            rc = self._get_infer(force=force_download, verbose=verbose, intree_tool=intree_tool)
+            if rc != 0:
+                self.log(logging.ERROR, 'ERROR: static-analysis', {},
+                         'infer unable to locate package.')
+                return self.TOOLS_FAILED_DOWNLOAD
+            self.__infer_tool = mozpath.join(self.topsrcdir, 'tools', 'infer')
+            self.__infer_test_folder = mozpath.join(self.__infer_tool, 'test')
+
+            import concurrent.futures
+            import multiprocessing
+            max_workers = multiprocessing.cpu_count()
+            self.log(logging.INFO, 'static-analysis', {},
+                     "RUNNING: infer autotest for platform {0} with {1} workers.".format(
+                         self.platform[0], max_workers))
+            # clean previous autotest if it exists
+            rc = self._gradle(['autotest:clean'], autotest=True)
+            if rc != 0:
+                return rc
+            import yaml
+            with open(mozpath.join(self.__infer_tool, 'config.yaml')) as f:
+                config = yaml.safe_load(f)
+            with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
+                futures = []
+                for item in config['infer_checkers']:
+                    if item['publish']:
+                        futures.append(executor.submit(self._verify_infer_checker, item))
+                # this is always included in check-java, but not in config.yaml
+                futures.append(executor.submit(self._verify_infer_checker,
+                                               {'name': 'checkers'}))
+                for future in concurrent.futures.as_completed(futures):
+                    ret_val = future.result()
+                    if ret_val != self.TOOLS_SUCCESS:
+                        return ret_val
+            self.log(logging.INFO, 'static-analysis', {}, "SUCCESS: infer all tests passed.")
+        else:
+            self.log(logging.WARNING, 'static-analysis', {},
+                     "Skipping infer autotest, because it is only available on linux64!")
+        return self.TOOLS_SUCCESS
+
+    def _verify_infer_checker(self, item):
+        '''Given a checker, this method verifies the following:
+          1. if there is a `checker`.json and `checker`.java file in
+             `tools/infer/test/autotest/src`
+          2. if running infer on `checker`.java yields the same result as `checker`.json
+        An `item` is simply a dictionary, which needs to have a `name` field set, which is the
+        name of the checker.
+        '''
+        def to_camelcase(str):
+            return ''.join([s.capitalize() for s in str.split('-')])
+        check = item['name']
+        test_file_path = mozpath.join(self.__infer_tool, 'test', 'autotest', 'src',
+                                      'main', 'java', to_camelcase(check))
+        test_file_path_java = test_file_path + '.java'
+        test_file_path_json = test_file_path + '.json'
+        self.log(logging.INFO, 'static-analysis', {}, "RUNNING: infer check {}.".format(check))
+        # Verify if the test file exists for this checker
+        if not os.path.exists(test_file_path_java):
+            self.log(logging.ERROR, 'static-analysis', {},
+                     "ERROR: infer check {} doesn't have a test file.".format(check))
+            return self.TOOLS_CHECKER_NO_TEST_FILE
+        # run infer on a particular test file
+        out_folder = mozpath.join(self.__infer_test_folder, 'test-infer-{}'.format(check))
+        if check == 'checkers':
+            check_arg = ['-a', 'checkers']
+        else:
+            check_arg = ['--{}-only'.format(check)]
+        infer_args = [self._infer_path, 'run'] + check_arg + ['-o', out_folder, '--']
+        gradle_args = ['autotest:compileInferTest{}'.format(to_camelcase(check))]
+        rc = self._gradle(gradle_args, infer_args=infer_args, autotest=True)
+        if rc != 0:
+            self.log(logging.ERROR, 'static-analysis', {},
+                     "ERROR: infer failed to execute gradle {}.".format(gradle_args))
+            return self.TOOLS_GRADLE_FAILED
+        issues = json.load(open(mozpath.join(out_folder, 'report.json')))
+        # remove folder that infer creates because the issues are loaded into memory
+        import shutil
+        shutil.rmtree(out_folder)
+        # Verify to see if we got any issues, if not raise exception
+        if not issues:
+            self.log(
+                logging.ERROR, 'static-analysis', {},
+                "ERROR: infer check {0} did not find any issues in its associated test suite."
+                .format(check)
+            )
+            return self.TOOLS_CHECKER_RETURNED_NO_ISSUES
+        if self._dump_results:
+            self._build_autotest_result(test_file_path_json, issues)
+        else:
+            if not os.path.exists(test_file_path_json):
+                # Result file for test not found maybe regenerate it?
+                self.log(
+                    logging.ERROR, 'static-analysis', {},
+                    "ERROR: infer result file not found for check {0}".format(check)
+                )
+                return self.TOOLS_CHECKER_RESULT_FILE_NOT_FOUND
+            # Read the pre-determined issues
+            baseline_issues = self._get_autotest_stored_issues(test_file_path_json)
+
+            def ordered(obj):
+                if isinstance(obj, dict):
+                    return sorted((k, ordered(v)) for k, v in obj.items())
+                if isinstance(obj, list):
+                    return sorted(ordered(x) for x in obj)
+                return obj
+            # Compare the two lists
+            if ordered(issues) != ordered(baseline_issues):
+                error_str = "ERROR: in check {} Expected: ".format(check)
+                error_str += '\n' + json.dumps(baseline_issues, indent=2)
+                error_str += '\n Got:\n' + json.dumps(issues, indent=2)
+                self.log(logging.ERROR, 'static-analysis', {},
+                         'ERROR: infer autotest for check {} failed, check stdout for more details'
+                         .format(check))
+                print(error_str)
+                return self.TOOLS_CHECKER_DIFF_FAILED
+        return self.TOOLS_SUCCESS
+
     @StaticAnalysisSubCommand('static-analysis', 'install',
                               'Install the static analysis helper tool')
     @CommandArgument('source', nargs='?', type=str,
                      help='Where to fetch a local archive containing the static-analysis and '
                      'format helper tool.'
                           'It will be installed in ~/.mozbuild/clang-tools and ~/.mozbuild/infer.'
                           'Can be omitted, in which case the latest clang-tools and infer '
                           'helper for the platform would be automatically detected and installed.')
@@ -2408,17 +2561,16 @@ class StaticAnalysis(MachCommandBase):
             "clang-format" + config.substs.get('BIN_SUFFIX', ''))
         self._clang_apply_replacements = mozpath.join(
             clang_tools_path, "clang-tidy", "bin",
             "clang-apply-replacements" + config.substs.get('BIN_SUFFIX', ''))
         self._run_clang_tidy_path = mozpath.join(clang_tools_path, "clang-tidy", "share", "clang",
                                                  "run-clang-tidy.py")
         self._clang_format_diff = mozpath.join(clang_tools_path, "clang-tidy", "share", "clang",
                                                "clang-format-diff.py")
-
         if os.path.exists(self._clang_tidy_path) and \
            os.path.exists(self._clang_format_path) and \
            os.path.exists(self._clang_apply_replacements) and \
            os.path.exists(self._run_clang_tidy_path) and \
            not force:
             return 0
         else:
             if os.path.isdir(clang_tools_path) and download_if_needed:
@@ -2509,25 +2661,27 @@ class StaticAnalysis(MachCommandBase):
                         continue  # empty or comment
                     magics = ['exclude']
                     if pattern.startswith('^'):
                         magics += ['top']
                         pattern = pattern[1:]
                     args += [':({0}){1}'.format(','.join(magics), pattern)]
         return args
 
-    def _get_infer(self, force=False, skip_cache=False,
-                   download_if_needed=True, verbose=False):
+    def _get_infer(self, force=False, skip_cache=False, download_if_needed=True,
+                   verbose=False, intree_tool=False):
         rc, config, _ = self._get_config_environment()
         if rc != 0:
             return rc
-        infer_path = mozpath.join(self._mach_context.state_dir, 'infer')
-        self._infer_path = mozpath.join(infer_path, 'infer', 'bin',
-                                        'infer' +
+        infer_path = self.topsrcdir if intree_tool else \
+            mozpath.join(self._mach_context.state_dir, 'infer')
+        self._infer_path = mozpath.join(infer_path, 'infer', 'bin', 'infer' +
                                         config.substs.get('BIN_SUFFIX', ''))
+        if intree_tool:
+            return not os.path.exists(self._infer_path)
         if os.path.exists(self._infer_path) and not force:
             return 0
         else:
             if os.path.isdir(infer_path) and download_if_needed:
                 # The directory exists, perhaps it's corrupted?  Delete it
                 # and start from scratch.
                 import shutil
                 shutil.rmtree(infer_path)
--- a/security/manager/ssl/OSReauthenticator.cpp
+++ b/security/manager/ssl/OSReauthenticator.cpp
@@ -199,24 +199,77 @@ ReauthenticateUserWindows(const nsACStri
               ("Login successfully (correct user)."));
       reauthenticated = true;
       return NS_OK;
   }
   MOZ_LOG(gCredentialManagerSecretLog, LogLevel::Debug,
           ("Login failed (wrong user)."));
   return NS_ERROR_FAILURE;
 }
-#endif
+#endif // XP_WIN
+
+#ifdef XP_MACOSX
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+
+static nsresult
+ReauthenticateUserMacOS(const nsACString& aPrompt,
+              /* out */ bool& aReauthenticated)
+{
+  // The idea here is that we ask to be authorized to unlock the user's session.
+  // This should cause a prompt to come up for the user asking them for their
+  // password. If they correctly enter it, we'll return a successful result. If
+  // they cancel the prompt or otherwise fail to provide their password, we
+  // return a failing result.
+  AuthorizationItem authorizationItems[] = {
+    { "system.login.screensaver", 0, NULL, 0 },
+  };
+  AuthorizationRights authorizationRights = {
+    ArrayLength(authorizationItems),
+    authorizationItems,
+  };
+  const nsCString& promptFlat = PromiseFlatCString(aPrompt);
+  // All "kAuthorization..." constants come from the MacOS SDK.
+  AuthorizationItem environmentItems[] =  {
+    { kAuthorizationEnvironmentPrompt,
+      promptFlat.Length(),
+      (void*)promptFlat.get(),
+      0
+    },
+  };
+  AuthorizationEnvironment environment = {
+    ArrayLength(environmentItems),
+    environmentItems,
+  };
+  AuthorizationFlags flags = kAuthorizationFlagDefaults |
+                             kAuthorizationFlagInteractionAllowed |
+                             kAuthorizationFlagExtendRights |
+                             kAuthorizationFlagPreAuthorize |
+                             kAuthorizationFlagDestroyRights;
+  AuthorizationRef authorizationRef = nullptr;
+  OSStatus result = AuthorizationCreate(&authorizationRights,
+                                        &environment,
+                                        flags,
+                                        &authorizationRef);
+  aReauthenticated = result == errAuthorizationSuccess;
+  if (authorizationRef) {
+    AuthorizationFree(authorizationRef, kAuthorizationFlagDestroyRights);
+  }
+  return NS_OK;
+}
+#endif // XP_MACOSX
 
 static nsresult
 ReauthenticateUser(const nsACString& prompt, /* out */ bool& reauthenticated)
 {
   reauthenticated = false;
 #if defined(XP_WIN)
   return ReauthenticateUserWindows(prompt, reauthenticated);
+#elif defined(XP_MACOSX)
+  return ReauthenticateUserMacOS(prompt, reauthenticated);
 #endif // Reauthentication is not implemented for this platform.
   return NS_OK;
 }
 
 static void
 BackgroundReauthenticateUser(RefPtr<Promise>& aPromise,
                              const nsACString& aPrompt)
 {
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -137,19 +137,20 @@ run-sequentially = hardcoded ports
 [test_ocsp_stapling_with_intermediate.js]
 run-sequentially = hardcoded ports
 [test_ocsp_timeout.js]
 run-sequentially = hardcoded ports
 [test_ocsp_url.js]
 run-sequentially = hardcoded ports
 [test_oskeystore.js]
 [test_osreauthenticator.js]
-# Reauthentication has been implemented on Windows, so running this test results in
-# the OS popping up a dialog, which means we can't run it in automation.
-skip-if = os == 'win'
+# Reauthentication has been implemented on Windows and MacOS, so running this
+# test results in the OS popping up a dialog, which means we can't run it in
+# automation.
+skip-if = os == 'win' || os == 'mac'
 [test_password_prompt.js]
 [test_pinning.js]
 run-sequentially = hardcoded ports
 # This test can take longer than 300 seconds on B2G emulator debug builds, so
 # give it enough time to finish. See bug 1081128.
 requesttimeoutfactor = 2
 [test_pinning_dynamic.js]
 [test_pinning_header_parsing.js]
--- a/taskcluster/ci/docker-image/kind.yml
+++ b/taskcluster/ci/docker-image/kind.yml
@@ -81,19 +81,19 @@ jobs:
       - deb9-mercurial
       - deb9-python-zstandard
   android-build:
     symbol: I(agb)
     parent: debian9-base
   fetch:
     symbol: I(fetch)
     parent: debian9-base
-  infer-build:
-    symbol: I(infer)
-    parent: debian9-base
+  static-analysis-build:
+    symbol: I(static-analysis-build)
+    parent: android-build
   mingw32-build:
     symbol: I(mingw)
     parent: debian9-base
   index-task:
     symbol: I(idx)
   funsize-update-generator:
     symbol: I(pg)
   google-play-strings:
--- a/taskcluster/ci/release-partner-repack-chunking-dummy/kind.yml
+++ b/taskcluster/ci/release-partner-repack-chunking-dummy/kind.yml
@@ -6,17 +6,17 @@ loader: taskgraph.loader.single_dep:load
 
 transforms:
    - taskgraph.transforms.chunk_partners:transforms
    - taskgraph.transforms.name_sanity:transforms
    # This transform sets build_platform to the same thing as the upstream task.
    # We'd do it here, except single_dep doesn't pay attention to any
    # per platform things that we set.
    - taskgraph.transforms.copy_attributes_from_dependent_task:transforms
-   # This transform is needed because task.py doesn't allow "dependent-task" to be
+   # This transform is needed because task.py doesn't allow "primary-dependency" to be
    # set, but the single_dep loader sets it (and we need it for chunk_partners,
    # name_sanity, and copy_build_platform_from_dependent_task to work).
    - taskgraph.transforms.strip_dependent_task:transforms
    - taskgraph.transforms.release_deps:transforms
    - taskgraph.transforms.task:transforms
 
 kind-dependencies:
    - release-partner-repack
--- a/taskcluster/ci/static-analysis-autotest/kind.yml
+++ b/taskcluster/ci/static-analysis-autotest/kind.yml
@@ -35,20 +35,22 @@ jobs:
     linux64-st-autotest/debug:
         description: "Linux64 Debug Static Analysis Autotest"
         index:
             job-name: linux64-st-autotest-debug
         treeherder:
             platform: linux64/debug
         worker-type: aws-provisioner-v1/gecko-t-linux-large
         worker:
+            docker-image: {in-tree: static-analysis-build}
             env:
                 # clang-tidy needs a recent libstdc++, which can be found in the clang
                 # toolchain.
                 LD_LIBRARY_PATH: /builds/worker/workspace/build/src/clang/lib
+                PERFHERDER_EXTRA_OPTIONS: static-analysis-autotest
         run:
             config:
                 - builds/releng_base_firefox.py
                 - builds/releng_sub_linux_configs/64_stat_and_debug.py
             tooltool-downloads: public
             keep-artifacts: false
         toolchains:
             - linux64-clang
--- a/taskcluster/ci/toolchain/linux.yml
+++ b/taskcluster/ci/toolchain/linux.yml
@@ -180,17 +180,17 @@ linux64-infer:
         job-name: linux64-infer
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TL(infer)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        docker-image: {in-tree: infer-build}
+        docker-image: {in-tree: static-analysis-build}
         max-run-time: 3600
     run:
         using: toolchain-script
         script: build-infer-linux.sh
         resources:
             - 'build/build-infer/build-infer.py'
             - 'build/build-infer/infer-linux64.json'
         toolchain-artifact: public/build/infer.tar.xz
new file mode 100644
--- /dev/null
+++ b/taskcluster/docker/static-analysis-build/Dockerfile
@@ -0,0 +1,58 @@
+# %ARG DOCKER_IMAGE_PARENT
+FROM $DOCKER_IMAGE_PARENT
+MAINTAINER Robert Bartlensky <rbartlensky@mozilla.com>
+
+VOLUME /builds/worker/checkouts
+VOLUME /builds/worker/workspace
+VOLUME /builds/worker/tooltool-cache
+
+ENV XZ_OPT=-T0
+
+RUN apt-get update && \
+    apt-get install \
+      autoconf2.13 \
+      automake \
+      bison \
+      bzip2 \
+      cmake \
+      flex \
+      curl \
+      opam \
+      libsqlite3-dev \
+      file \
+      gawk \
+      gcc-multilib \
+      gnupg \
+      libc6-dev \
+      openjdk-8-jdk-headless \
+      pkg-config \
+      patch \
+      p7zip-full \
+      procps \
+      python-pip \
+      python-setuptools \
+      python-virtualenv \
+      rsync \
+      screen \
+      tar \
+      unzip \
+      uuid \
+      valgrind \
+      wget \
+      yasm \
+      zip \
+      zlib1g-dev \
+      x11-utils \
+      xvfb \
+      linux-libc-dev \
+      libdbus-glib-1-dev \
+      libfontconfig1-dev \
+      libfreetype6-dev \
+      libgconf2-dev \
+      libgtk-3-dev \
+      libgtk2.0-dev \
+      libpango1.0-dev \
+      libpulse-dev \
+      libx11-xcb-dev \
+      libxt-dev \
+      lib32z1
--- a/taskcluster/docs/parameters.rst
+++ b/taskcluster/docs/parameters.rst
@@ -35,19 +35,16 @@ Push Information
    The revision to check out; this can be a short revision string
 
 ``head_ref``
    For Mercurial repositories, this is the same as ``head_rev``.  For
    git repositories, which do not allow pulling explicit revisions, this gives
    the symbolic ref containing ``head_rev`` that should be pulled from
    ``head_repository``.
 
-``include_nightly``
-   Include nightly builds and tests in the graph.
-
 ``owner``
    Email address indicating the person who made the push.  Note that this
    value may be forged and *must not* be relied on for authentication.
 
 ``message``
    The commit message
 
 ``pushlog_id``
@@ -110,19 +107,16 @@ syntax or reading a project-specific con
     List of filter functions (from ``taskcluster/taskgraph/filter_tasks.py``) to
     apply. This is usually defined internally, as filters are typically
     global.
 
 ``target_tasks_method``
     The method to use to determine the target task set.  This is the suffix of
     one of the functions in ``taskcluster/taskgraph/target_tasks.py``.
 
-``include_nightly``
-    If true, then nightly tasks are eligible for optimization.
-
 ``release_history``
    History of recent releases by platform and locale, used when generating
    partial updates for nightly releases.
    Suitable contents can be generated with ``mach release-history``,
    which will print to the console by default.
 
 Optimization
 ------------
--- a/taskcluster/taskgraph/decision.py
+++ b/taskcluster/taskgraph/decision.py
@@ -26,102 +26,82 @@ logger = logging.getLogger(__name__)
 
 ARTIFACTS_DIR = 'artifacts'
 
 # For each project, this gives a set of parameters specific to the project.
 # See `taskcluster/docs/parameters.rst` for information on parameters.
 PER_PROJECT_PARAMETERS = {
     'try': {
         'target_tasks_method': 'try_tasks',
-        # By default, the `try_option_syntax` `target_task_method` ignores this
-        # parameter, and enables/disables nightlies depending whether
-        # `--include-nightly` is specified in the commit message.
-        # We're setting the `include_nightly` parameter to True here for when
-        # we submit decision tasks against Try that use other
-        # `target_task_method`s, like `nightly_fennec` or `mozilla_beta_tasks`,
-        # which reference the `include_nightly` parameter.
-        'include_nightly': True,
     },
 
     'try-comm-central': {
         'target_tasks_method': 'try_tasks',
-        'include_nightly': True,
     },
 
     'ash': {
         'target_tasks_method': 'ash_tasks',
         'optimize_target_tasks': True,
-        'include_nightly': False,
     },
 
     'cedar': {
         'target_tasks_method': 'cedar_tasks',
         'optimize_target_tasks': True,
-        'include_nightly': False,
     },
 
     'graphics': {
         'target_tasks_method': 'graphics_tasks',
         'optimize_target_tasks': True,
-        'include_nightly': False,
     },
 
     'mozilla-central': {
         'target_tasks_method': 'default',
         'optimize_target_tasks': True,
-        'include_nightly': False,
         'release_type': 'nightly',
     },
 
     'mozilla-beta': {
         'target_tasks_method': 'mozilla_beta_tasks',
         'optimize_target_tasks': True,
-        'include_nightly': True,
         'release_type': 'beta',
     },
 
     'mozilla-release': {
         'target_tasks_method': 'mozilla_release_tasks',
         'optimize_target_tasks': True,
-        'include_nightly': True,
         'release_type': 'release',
     },
 
     'mozilla-esr60': {
         'target_tasks_method': 'mozilla_esr60_tasks',
         'optimize_target_tasks': True,
-        'include_nightly': True,
         'release_type': 'esr60',
     },
 
     'comm-beta': {
         'target_tasks_method': 'mozilla_beta_tasks',
         'optimize_target_tasks': True,
-        'include_nightly': True,
         'release_type': 'beta',
     },
 
     'comm-esr60': {
         'target_tasks_method': 'mozilla_esr60_tasks',
         'optimize_target_tasks': True,
-        'include_nightly': True,
         'release_type': 'release',
     },
 
     'pine': {
         'target_tasks_method': 'pine_tasks',
         'optimize_target_tasks': True,
-        'include_nightly': False,
     },
 
     # the default parameters are used for projects that do not match above.
     'default': {
         'target_tasks_method': 'default',
         'optimize_target_tasks': True,
-        'include_nightly': False,
     }
 }
 
 
 def full_task_graph_to_runnable_jobs(full_task_json):
     runnable_jobs = {}
     for label, node in full_task_json.iteritems():
         if not ('extra' in node['task'] and 'treeherder' in node['task']['extra']):
--- a/taskcluster/taskgraph/loader/build_signing.py
+++ b/taskcluster/taskgraph/loader/build_signing.py
@@ -22,12 +22,12 @@ NON_NIGHTLY_LABELS_WHICH_SHOULD_SIGN_BUI
     'release-partner-repack-macosx64-nightly',
 )
 
 
 def loader(kind, path, config, params, loaded_tasks):
     jobs = base_loader(kind, path, config, params, loaded_tasks)
 
     for job in jobs:
-        dependent_task = job['dependent-task']
+        dependent_task = job['primary-dependency']
         if dependent_task.attributes.get('nightly') or \
                 dependent_task.label in NON_NIGHTLY_LABELS_WHICH_SHOULD_SIGN_BUILDS:
             yield job
--- a/taskcluster/taskgraph/loader/multi_dep.py
+++ b/taskcluster/taskgraph/loader/multi_dep.py
@@ -1,16 +1,26 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import copy
 
+from voluptuous import Required
+
+from ..task import Task
+from ..util.schema import Schema
+
+schema = Schema({
+    Required('primary-dependency', 'primary dependency task'): Task,
+    Required('dependent-tasks', 'dictionary of dependent tasks, keyed by kind'): {basestring: Task},
+})
+
 
 # Define a collection of group_by functions
 GROUP_BY_MAP = {}
 
 
 def group_by(name):
     def wrapper(func):
         GROUP_BY_MAP[name] = func
--- a/taskcluster/taskgraph/loader/single_dep.py
+++ b/taskcluster/taskgraph/loader/single_dep.py
@@ -1,16 +1,25 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import copy
 
+from voluptuous import Required
+
+from ..task import Task
+from ..util.schema import Schema
+
+schema = Schema({
+    Required('primary-dependency', 'primary dependency task'): Task,
+})
+
 
 def loader(kind, path, config, params, loaded_tasks):
     """
     Load tasks based on the jobs dependant kinds.
 
     The `only-for-build-platforms` kind configuration, if specified, will limit
     the build platforms for which a job will be created. Alternatively there is
     'not-for-build-platforms' kind configuration which will be consulted only after
@@ -45,17 +54,19 @@ def loader(kind, path, config, params, l
                 continue
 
         if only_attributes:
             config_attrs = set(only_attributes)
             if config_attrs - set(task.attributes):
                 # make sure all attributes exist
                 continue
 
-        job = {'dependent-task': task}
+        job = {
+            'primary-dependency': task,
+        }
 
         if job_template:
             job.update(copy.deepcopy(job_template))
 
         # copy shipping_product from upstream
         product = task.attributes.get(
             'shipping_product', task.task.get('shipping-product')
         )
--- a/taskcluster/taskgraph/parameters.py
+++ b/taskcluster/taskgraph/parameters.py
@@ -54,17 +54,16 @@ PARAMETERS = {
     'build_date': lambda: int(time.time()),
     'build_number': 1,
     'do_not_optimize': [],
     'existing_tasks': {},
     'filters': ['check_servo', 'target_tasks_method'],
     'head_ref': get_head_ref,
     'head_repository': 'https://hg.mozilla.org/mozilla-central',
     'head_rev': get_head_ref,
-    'include_nightly': False,
     'level': '3',
     'message': '',
     'moz_build_date': lambda: datetime.now().strftime("%Y%m%d%H%M%S"),
     'next_version': None,
     'optimize_target_tasks': True,
     'owner': 'nobody@mozilla.com',
     'project': 'mozilla-central',
     'pushdate': lambda: int(time.time()),
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -20,17 +20,17 @@ def _target_task(name):
 
 
 def get_method(method):
     """Get a target_task_method to pass to a TaskGraphGenerator."""
     return _target_task_methods[method]
 
 
 def filter_out_nightly(task, parameters):
-    return not task.attributes.get('nightly') or parameters.get('include_nightly')
+    return not task.attributes.get('nightly')
 
 
 def filter_out_cron(task, parameters):
     """
     Filter out tasks that run via cron.
     """
     return not task.attributes.get('cron')
 
@@ -42,28 +42,17 @@ def filter_for_project(task, parameters)
 
 
 def filter_on_platforms(task, platforms):
     """Filter tasks on the given platform"""
     platform = task.attributes.get('build_platform')
     return (platform in platforms)
 
 
-def filter_beta_release_tasks(task, parameters, ignore_kinds=None, allow_l10n=False):
-    if not standard_filter(task, parameters):
-        return False
-    if ignore_kinds is None:
-        ignore_kinds = [
-            'balrog',
-            'beetmover', 'beetmover-checksums', 'beetmover-l10n',
-            'beetmover-repackage', 'beetmover-repackage-signing',
-            'checksums-signing',
-            'nightly-l10n', 'nightly-l10n-signing',
-            'push-apk', 'repackage-l10n',
-        ]
+def filter_release_tasks(task, parameters):
     platform = task.attributes.get('build_platform')
     if platform in (
             # On beta, Nightly builds are already PGOs
             'linux-pgo', 'linux64-pgo',
             'win32-pgo', 'win64-pgo',
             # ASAN is central-only
             'linux64-asan-reporter-nightly',
             'win64-asan-reporter-nightly',
@@ -78,32 +67,26 @@ def filter_beta_release_tasks(task, para
         if task.attributes['kind'] == 'l10n':
             # This is on-change l10n
             return True
         if task.attributes['build_type'] == 'opt' and \
            task.attributes.get('unittest_suite') != 'talos' and \
            task.attributes.get('unittest_suite') != 'raptor':
             return False
 
-    # skip l10n, beetmover, balrog
-    if task.kind in ignore_kinds:
-        return False
-
-    # No l10n repacks per push. They may be triggered by kinds which depend
-    # on l10n builds/repacks. For instance: "repackage-signing"
-    if not allow_l10n and task.attributes.get('locale', '') != '':
+    if task.attributes.get('shipping_phase') not in (None, 'build'):
         return False
 
     return True
 
 
 def standard_filter(task, parameters):
     return all(
         filter_func(task, parameters) for filter_func in
-        (filter_out_nightly, filter_out_cron, filter_for_project)
+        (filter_out_cron, filter_for_project)
     )
 
 
 def _try_task_config(full_task_graph, parameters, graph_config):
     requested_tasks = parameters['try_task_config']['tasks']
     return list(set(requested_tasks) & full_task_graph.graph.nodes)
 
 
@@ -173,17 +156,18 @@ def target_tasks_try(full_task_graph, pa
         return []
 
 
 @_target_task('default')
 def target_tasks_default(full_task_graph, parameters, graph_config):
     """Target the tasks which have indicated they should be run on this project
     via the `run_on_projects` attributes."""
     return [l for l, t in full_task_graph.tasks.iteritems()
-            if standard_filter(t, parameters)]
+            if standard_filter(t, parameters)
+            and filter_out_nightly(t, parameters)]
 
 
 @_target_task('ash_tasks')
 def target_tasks_ash(full_task_graph, parameters, graph_config):
     """Target tasks that only run on the ash branch."""
     def filter(task):
         platform = task.attributes.get('build_platform')
         # Early return if platform is None
@@ -268,38 +252,43 @@ def target_tasks_valgrind(full_task_grap
 
 
 @_target_task('mozilla_beta_tasks')
 def target_tasks_mozilla_beta(full_task_graph, parameters, graph_config):
     """Select the set of tasks required for a promotable beta or release build
     of desktop, plus android CI. The candidates build process involves a pipeline
     of builds and signing, but does not include beetmover or balrog jobs."""
 
-    return [l for l, t in full_task_graph.tasks.iteritems() if
-            filter_beta_release_tasks(t, parameters)]
+    return [l for l, t in full_task_graph.tasks.iteritems()
+            if filter_release_tasks(t, parameters)
+            and standard_filter(t, parameters)]
 
 
 @_target_task('mozilla_release_tasks')
 def target_tasks_mozilla_release(full_task_graph, parameters, graph_config):
     """Select the set of tasks required for a promotable beta or release build
     of desktop, plus android CI. The candidates build process involves a pipeline
     of builds and signing, but does not include beetmover or balrog jobs."""
 
-    return [l for l, t in full_task_graph.tasks.iteritems() if
-            filter_beta_release_tasks(t, parameters)]
+    return [l for l, t in full_task_graph.tasks.iteritems()
+            if filter_release_tasks(t, parameters)
+            and standard_filter(t, parameters)]
 
 
 @_target_task('mozilla_esr60_tasks')
 def target_tasks_mozilla_esr60(full_task_graph, parameters, graph_config):
     """Select the set of tasks required for a promotable beta or release build
     of desktop, plus android CI. The candidates build process involves a pipeline
     of builds and signing, but does not include beetmover or balrog jobs."""
 
     def filter(task):
-        if not filter_beta_release_tasks(task, parameters):
+        if not filter_release_tasks(task, parameters):
+            return False
+
+        if not standard_filter(task, parameters):
             return False
 
         platform = task.attributes.get('build_platform')
 
         # Android is not built on esr.
         if platform and 'android' in platform:
             return False
 
@@ -438,17 +427,17 @@ def target_tasks_pine(full_task_graph, p
         platform = task.attributes.get('build_platform')
         # disable mobile jobs
         if str(platform).startswith('android'):
             return False
         # disable asan
         if platform == 'linux64-asan':
             return False
         # disable non-pine and nightly tasks
-        if standard_filter(task, parameters):
+        if standard_filter(task, parameters) or filter_out_nightly(task, parameters):
             return True
     return [l for l, t in full_task_graph.tasks.iteritems() if filter(t)]
 
 
 @_target_task('nightly_fennec')
 def target_tasks_nightly_fennec(full_task_graph, parameters, graph_config):
     """Select the set of tasks required for a nightly build of fennec. The
     nightly build process involves a pipeline of builds, signing,
@@ -591,8 +580,25 @@ def target_tasks_staging_release(full_ta
     def filter(task):
         if not task.attributes.get('shipping_product'):
             return False
         if task.attributes.get('shipping_phase') == 'build':
             return True
         return False
 
     return [l for l, t in full_task_graph.tasks.iteritems() if filter(t)]
+
+
+@_target_task('beta_simulation')
+def target_tasks_beta_simulation(full_task_graph, parameters, graph_config):
+    """
+    Select builds that would run on mozilla-beta.
+    """
+
+    def filter_for_beta(task):
+        """Filter tasks by project.  Optionally enable nightlies."""
+        run_on_projects = set(task.attributes.get('run_on_projects', []))
+        return match_run_on_projects('mozilla-beta', run_on_projects)
+
+    return [l for l, t in full_task_graph.tasks.iteritems()
+            if filter_release_tasks(t, parameters)
+            and filter_out_cron(t, parameters)
+            and filter_for_beta(t)]
--- a/taskcluster/taskgraph/test/test_util_schema.py
+++ b/taskcluster/taskgraph/test/test_util_schema.py
@@ -4,18 +4,18 @@
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import unittest
 from mozunit import main
 from taskgraph.util.schema import (
     validate_schema,
     resolve_keyed_by,
+    Schema,
 )
-from voluptuous import Schema
 
 schema = Schema({
     'x': int,
     'y': basestring,
 })
 
 
 class TestValidateSchema(unittest.TestCase):
@@ -26,16 +26,34 @@ class TestValidateSchema(unittest.TestCa
     def test_invalid(self):
         try:
             validate_schema(schema, {'x': 'not-int'}, "pfx")
             self.fail("no exception raised")
         except Exception as e:
             self.failUnless(str(e).startswith("pfx\n"))
 
 
+class TestCheckSchema(unittest.TestCase):
+
+    def test_schema(self):
+        "Creating a schema applies taskgraph checks."
+        with self.assertRaises(Exception):
+            Schema({"camelCase": int})
+
+    def test_extend_schema(self):
+        "Extending a schema applies taskgraph checks."
+        with self.assertRaises(Exception):
+            Schema({"kebab-case": int}).extend({"camelCase": int})
+
+    def test_extend_schema_twice(self):
+        "Extending a schema twice applies taskgraph checks."
+        with self.assertRaises(Exception):
+            Schema({"kebab-case": int}).extend({'more-kebab': int}).extend({"camelCase": int})
+
+
 class TestResolveKeyedBy(unittest.TestCase):
 
     def test_no_by(self):
         self.assertEqual(
             resolve_keyed_by({'x': 10}, 'z', 'n'),
             {'x': 10})
 
     def test_no_by_dotted(self):
--- a/taskcluster/taskgraph/transforms/balrog_submit.py
+++ b/taskcluster/taskgraph/transforms/balrog_submit.py
@@ -2,19 +2,20 @@
 # 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/.
 """
 Transform the per-locale balrog task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.schema import validate_schema
 from taskgraph.util.scriptworker import (
     get_balrog_server_scope, get_worker_type_for_scope
 )
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required, Optional
 
 
 # Voluptuous uses marker objects as dictionary *keys*, but they are not
@@ -23,20 +24,17 @@ task_description_schema = {str(k): v for
 
 transforms = TransformSequence()
 
 # shortcut for a string where task references are allowed
 taskref_or_string = Any(
     basestring,
     {Required('task-reference'): basestring})
 
-balrog_description_schema = Schema({
-    # the dependent task (object) for this balrog job, used to inform balrogworker.
-    Required('dependent-task'): object,
-
+balrog_description_schema = schema.extend({
     # unique label to describe this balrog task, defaults to balrog-{dep.label}
     Optional('label'): basestring,
 
     # treeherder is allowed here to override any defaults we use for beetmover.  See
     # taskcluster/taskgraph/transforms/task.py for the schema details, and the
     # below transforms for defaults of various values.
     Optional('treeherder'): task_description_schema['treeherder'],
 
@@ -44,28 +42,28 @@ balrog_description_schema = Schema({
     Optional('shipping-product'): task_description_schema['shipping-product'],
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             balrog_description_schema, job,
             "In balrog ({!r} kind) task for {!r}:".format(config.kind, label))
 
         yield job
 
 
 @transforms.add
 def make_task_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
 
         treeherder = job.get('treeherder', {})
         treeherder.setdefault('symbol', 'c-Up(N)')
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
         treeherder.setdefault('platform',
                               "{}/opt".format(dep_th_platform))
         treeherder.setdefault(
--- a/taskcluster/taskgraph/transforms/beetmover.py
+++ b/taskcluster/taskgraph/transforms/beetmover.py
@@ -2,19 +2,20 @@
 # 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/.
 """
 Transform the beetmover task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.schema import validate_schema
 from taskgraph.util.scriptworker import (get_beetmover_bucket_scope,
                                          get_beetmover_action_scope,
                                          get_worker_type_for_scope)
 from taskgraph.util.taskcluster import get_artifact_prefix
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required, Optional
 
 
@@ -116,20 +117,17 @@ task_description_schema = {str(k): v for
 
 transforms = TransformSequence()
 
 # shortcut for a string where task references are allowed
 taskref_or_string = Any(
     basestring,
     {Required('task-reference'): basestring})
 
-beetmover_description_schema = Schema({
-    # the dependent task (object) for this beetmover job, used to inform beetmover.
-    Required('dependent-task'): object,
-
+beetmover_description_schema = schema.extend({
     # depname is used in taskref's to identify the taskID of the unsigned things
     Required('depname', default='build'): basestring,
 
     # unique label to describe this beetmover task, defaults to {dep.label}-beetmover
     Optional('label'): basestring,
 
     # treeherder is allowed here to override any defaults we use for beetmover.  See
     # taskcluster/taskgraph/transforms/task.py for the schema details, and the
@@ -142,27 +140,27 @@ beetmover_description_schema = Schema({
     Required('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             beetmover_description_schema, job,
             "In beetmover ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def make_task_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         attributes = dep_job.attributes
 
         treeherder = job.get('treeherder', {})
         treeherder.setdefault('symbol', 'BM-S')
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
         treeherder.setdefault('platform',
                               "{}/opt".format(dep_th_platform))
--- a/taskcluster/taskgraph/transforms/beetmover_checksums.py
+++ b/taskcluster/taskgraph/transforms/beetmover_checksums.py
@@ -2,61 +2,61 @@
 # 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/.
 """
 Transform the checksums signing task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.transforms.beetmover import craft_release_properties
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.schema import validate_schema
 from taskgraph.util.scriptworker import (get_beetmover_bucket_scope,
                                          get_beetmover_action_scope,
                                          get_worker_type_for_scope)
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required, Optional
 
 # Voluptuous uses marker objects as dictionary *keys*, but they are not
 # comparable, so we cast all of the keys back to regular strings
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 transforms = TransformSequence()
 
 taskref_or_string = Any(
     basestring,
     {Required('task-reference'): basestring})
 
-beetmover_checksums_description_schema = Schema({
-    Required('dependent-task'): object,
+beetmover_checksums_description_schema = schema.extend({
     Required('depname', default='build'): basestring,
     Optional('label'): basestring,
     Optional('treeherder'): task_description_schema['treeherder'],
     Optional('locale'): basestring,
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             beetmover_checksums_description_schema, job,
             "In checksums-signing ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def make_beetmover_checksums_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         attributes = dep_job.attributes
 
         treeherder = job.get('treeherder', {})
         treeherder.setdefault('symbol', 'BMcs(N)')
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
         treeherder.setdefault('platform',
                               "{}/opt".format(dep_th_platform))
@@ -83,19 +83,16 @@ def make_beetmover_checksums_description
             extra['product'] = 'fennec'
         elif 'devedition' in build_platform:
             extra['product'] = 'devedition'
         else:
             extra['product'] = 'firefox'
 
         dependent_kind = str(dep_job.kind)
         dependencies = {dependent_kind: dep_job.label}
-        for k, v in dep_job.dependencies.items():
-            if k.startswith('beetmover'):
-                dependencies[k] = v
 
         attributes = copy_attributes_from_dependent_job(dep_job)
 
         if dep_job.attributes.get('locale'):
             treeherder['symbol'] = 'BMcs({})'.format(dep_job.attributes.get('locale'))
             attributes['locale'] = dep_job.attributes.get('locale')
 
         bucket_scope = get_beetmover_bucket_scope(config)
@@ -140,35 +137,22 @@ def generate_upstream_artifacts(refs, pl
     }]
 
     return upstream_artifacts
 
 
 @transforms.add
 def make_beetmover_checksums_worker(config, jobs):
     for job in jobs:
-        valid_beetmover_job = (len(job["dependencies"]) == 2)
-        if not valid_beetmover_job:
-            raise NotImplementedError("Beetmover checksums must have two dependencies.")
-
         locale = job["attributes"].get("locale")
         platform = job["attributes"]["build_platform"]
 
         refs = {
-            "beetmover": None,
-            "signing": None,
+            "signing": "<checksums-signing>",
         }
-        for dependency in job["dependencies"].keys():
-            if dependency.startswith("beetmover"):
-                refs['beetmover'] = "<{}>".format(dependency)
-            else:
-                refs['signing'] = "<{}>".format(dependency)
-        if None in refs.values():
-            raise NotImplementedError(
-                "Beetmover checksums must have a beetmover and signing dependency!")
 
         worker = {
             'implementation': 'beetmover',
             'release-properties': craft_release_properties(config, job),
             'upstream-artifacts': generate_upstream_artifacts(
                 refs, platform, locale
             ),
         }
--- a/taskcluster/taskgraph/transforms/beetmover_emefree_checksums.py
+++ b/taskcluster/taskgraph/transforms/beetmover_emefree_checksums.py
@@ -2,57 +2,57 @@
 # 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/.
 """
 Transform release-beetmover-source-checksums into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.transforms.beetmover import craft_release_properties
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.schema import validate_schema
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required, Optional
 
 # Voluptuous uses marker objects as dictionary *keys*, but they are not
 # comparable, so we cast all of the keys back to regular strings
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 transforms = TransformSequence()
 
 taskref_or_string = Any(
     basestring,
     {Required('task-reference'): basestring})
 
-beetmover_checksums_description_schema = Schema({
-    Required('dependent-task'): object,
+beetmover_checksums_description_schema = schema.extend({
     Required('depname', default='build'): basestring,
     Optional('label'): basestring,
     Optional('extra'): object,
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             beetmover_checksums_description_schema, job,
             "In checksums-signing ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def make_beetmover_checksums_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         attributes = dep_job.attributes
         build_platform = attributes.get("build_platform")
         if not build_platform:
             raise Exception("Cannot find build platform!")
         repack_id = dep_job.task.get('extra', {}).get('repack_id')
         if not repack_id:
             raise Exception("Cannot find repack id!")
 
--- a/taskcluster/taskgraph/transforms/beetmover_geckoview.py
+++ b/taskcluster/taskgraph/transforms/beetmover_geckoview.py
@@ -2,21 +2,22 @@
 # 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/.
 """
 Transform the beetmover task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.transforms.beetmover import \
     craft_release_properties as beetmover_craft_release_properties
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema, resolve_keyed_by, optionally_keyed_by
+from taskgraph.util.schema import validate_schema, resolve_keyed_by, optionally_keyed_by
 from taskgraph.util.scriptworker import get_worker_type_for_scope
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Required, Optional
 
 
 _ARTIFACT_ID_PER_PLATFORM = {
     'android-aarch64': 'geckoview{update_channel}-arm64-v8a',
     'android-api-16': 'geckoview{update_channel}-armeabi-v7a',
@@ -31,42 +32,41 @@ from voluptuous import Required, Optiona
     'try': '-nightly-try',
     'maple': '-nightly-maple',
 }
 
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 transforms = TransformSequence()
 
-beetmover_description_schema = Schema({
-    Required('dependent-task'): object,
+beetmover_description_schema = schema.extend({
     Required('depname', default='build'): basestring,
     Optional('label'): basestring,
     Optional('treeherder'): task_description_schema['treeherder'],
 
     Optional('bucket-scope'): optionally_keyed_by('release-level', basestring),
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             beetmover_description_schema, job,
             "In beetmover-geckoview ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def make_task_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         attributes = dep_job.attributes
 
         treeherder = job.get('treeherder', {})
         treeherder.setdefault('symbol', 'BM-gv')
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
         treeherder.setdefault('platform',
                               '{}/opt'.format(dep_th_platform))
--- a/taskcluster/taskgraph/transforms/beetmover_l10n.py
+++ b/taskcluster/taskgraph/transforms/beetmover_l10n.py
@@ -11,27 +11,27 @@ from taskgraph.transforms.base import Tr
 from taskgraph.util.treeherder import join_symbol
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def make_beetmover_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         for locale in dep_job.attributes.get('chunk_locales', []):
 
             group = 'BM-L10n'
 
             # add the locale code
             symbol = locale
 
             treeherder = {
                 'symbol': join_symbol(group, symbol),
             }
 
             beet_description = {
-                'dependent-task': dep_job,
+                'primary-dependency': dep_job,
                 'treeherder': treeherder,
                 'locale': locale,
                 'shipping-phase': job['shipping-phase'],
             }
             yield beet_description
--- a/taskcluster/taskgraph/transforms/beetmover_langpack_checksums.py
+++ b/taskcluster/taskgraph/transforms/beetmover_langpack_checksums.py
@@ -2,61 +2,61 @@
 # 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/.
 """
 Transform release-beetmover-langpack-checksums into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.transforms.beetmover import craft_release_properties
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.schema import validate_schema
 from taskgraph.util.scriptworker import (get_beetmover_bucket_scope,
                                          get_beetmover_action_scope,
                                          get_worker_type_for_scope)
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required, Optional
 
 # Voluptuous uses marker objects as dictionary *keys*, but they are not
 # comparable, so we cast all of the keys back to regular strings
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 transforms = TransformSequence()
 
 taskref_or_string = Any(
     basestring,
     {Required('task-reference'): basestring})
 
-beetmover_checksums_description_schema = Schema({
-    Required('dependent-task'): object,
+beetmover_checksums_description_schema = schema.extend({
     Required('depname', default='build'): basestring,
     Optional('label'): basestring,
     Optional('treeherder'): task_description_schema['treeherder'],
     Optional('locale'): basestring,
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             beetmover_checksums_description_schema, job,
             "In checksums-signing ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def make_beetmover_checksums_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         attributes = dep_job.attributes
 
         treeherder = job.get('treeherder', {})
         treeherder.setdefault(
             'symbol',
             'BMcslang(N{})'.format(attributes.get('l10n_chunk', ''))
             )
         dep_th_platform = dep_job.task.get('extra', {}).get(
--- a/taskcluster/taskgraph/transforms/beetmover_repackage.py
+++ b/taskcluster/taskgraph/transforms/beetmover_repackage.py
@@ -2,23 +2,24 @@
 # 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/.
 """
 Transform the beetmover task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.multi_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.transforms.beetmover import craft_release_properties
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.partials import (get_balrog_platform_name,
                                      get_partials_artifacts,
                                      get_partials_artifact_map)
-from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.schema import validate_schema
 from taskgraph.util.scriptworker import (get_beetmover_bucket_scope,
                                          get_beetmover_action_scope,
                                          get_worker_type_for_scope)
 from taskgraph.util.taskcluster import get_artifact_prefix
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required, Optional
 
 import logging
@@ -146,23 +147,17 @@ task_description_schema = {str(k): v for
 
 transforms = TransformSequence()
 
 # shortcut for a string where task references are allowed
 taskref_or_string = Any(
     basestring,
     {Required('task-reference'): basestring})
 
-beetmover_description_schema = Schema({
-    # dictionary of dependent task objects, keyed by kind.
-    Required('dependent-tasks'): {basestring: object},
-
-    # The primary dependency; we key our task info off this dep task
-    Required('primary-dependency'): object,
-
+beetmover_description_schema = schema.extend({
     # depname is used in taskref's to identify the taskID of the unsigned things
     Required('depname', default='build'): basestring,
 
     # unique label to describe this beetmover task, defaults to {dep.label}-beetmover
     Required('label'): basestring,
 
     # treeherder is allowed here to override any defaults we use for beetmover.  See
     # taskcluster/taskgraph/transforms/task.py for the schema details, and the
--- a/taskcluster/taskgraph/transforms/beetmover_repackage_partner.py
+++ b/taskcluster/taskgraph/transforms/beetmover_repackage_partner.py
@@ -2,26 +2,26 @@
 # 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/.
 """
 Transform the beetmover task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.transforms.beetmover import craft_release_properties
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.partners import (
     check_if_partners_enabled,
     get_ftp_platform,
     get_partner_config_by_kind,
 )
 from taskgraph.util.schema import (
-    Schema,
     optionally_keyed_by,
     resolve_keyed_by,
     validate_schema,
 )
 from taskgraph.util.scriptworker import (
     add_scope_prefix,
     get_beetmover_bucket_scope,
     get_worker_type_for_scope,
@@ -42,20 +42,17 @@ task_description_schema = {str(k): v for
 
 transforms = TransformSequence()
 
 # shortcut for a string where task references are allowed
 taskref_or_string = Any(
     basestring,
     {Required('task-reference'): basestring})
 
-beetmover_description_schema = Schema({
-    # the dependent task (object) for this beetmover job, used to inform beetmover.
-    Required('dependent-task'): object,
-
+beetmover_description_schema = schema.extend({
     # depname is used in taskref's to identify the taskID of the unsigned things
     Required('depname', default='build'): basestring,
 
     # unique label to describe this beetmover task, defaults to {dep.label}-beetmover
     Optional('label'): basestring,
 
     Required('partner-bucket-scope'): optionally_keyed_by('release-level', basestring),
     Required('partner-public-path'): Any(None, basestring),
@@ -67,17 +64,17 @@ beetmover_description_schema = Schema({
 })
 
 transforms.add(check_if_partners_enabled)
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             beetmover_description_schema, job,
             "In beetmover ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def resolve_keys(config, jobs):
@@ -87,17 +84,17 @@ def resolve_keys(config, jobs):
             **{'release-level': config.params.release_level()}
         )
         yield job
 
 
 @transforms.add
 def make_task_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         repack_id = dep_job.task.get('extra', {}).get('repack_id')
         if not repack_id:
             raise Exception("Cannot find repack id!")
 
         attributes = dep_job.attributes
         build_platform = attributes.get("build_platform")
         if not build_platform:
             raise Exception("Cannot find build platform!")
--- a/taskcluster/taskgraph/transforms/beetmover_source_checksums.py
+++ b/taskcluster/taskgraph/transforms/beetmover_source_checksums.py
@@ -2,61 +2,61 @@
 # 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/.
 """
 Transform release-beetmover-source-checksums into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.transforms.beetmover import craft_release_properties
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.schema import validate_schema
 from taskgraph.util.scriptworker import (get_beetmover_bucket_scope,
                                          get_beetmover_action_scope,
                                          get_worker_type_for_scope)
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required, Optional
 
 # Voluptuous uses marker objects as dictionary *keys*, but they are not
 # comparable, so we cast all of the keys back to regular strings
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 transforms = TransformSequence()
 
 taskref_or_string = Any(
     basestring,
     {Required('task-reference'): basestring})
 
-beetmover_checksums_description_schema = Schema({
-    Required('dependent-task'): object,
+beetmover_checksums_description_schema = schema.extend({
     Required('depname', default='build'): basestring,
     Optional('label'): basestring,
     Optional('treeherder'): task_description_schema['treeherder'],
     Optional('locale'): basestring,
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             beetmover_checksums_description_schema, job,
             "In checksums-signing ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def make_beetmover_checksums_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         attributes = dep_job.attributes
 
         treeherder = job.get('treeherder', {})
         treeherder.setdefault('symbol', 'BMcss(N)')
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
         treeherder.setdefault('platform',
                               "{}/opt".format(dep_th_platform))
--- a/taskcluster/taskgraph/transforms/build_signing.py
+++ b/taskcluster/taskgraph/transforms/build_signing.py
@@ -16,17 +16,17 @@ transforms = TransformSequence()
 
 
 @transforms.add
 def add_signed_routes(config, jobs):
     """Add routes corresponding to the routes of the build task
        this corresponds to, with .signed inserted, for all gecko.v2 routes"""
 
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
 
         job['routes'] = []
         if dep_job.attributes.get('nightly'):
             for dep_route in dep_job.task.get('routes', []):
                 if not dep_route.startswith('index.gecko.v2'):
                     continue
                 branch = dep_route.split(".")[3]
                 rest = ".".join(dep_route.split(".")[4:])
@@ -34,17 +34,17 @@ def add_signed_routes(config, jobs):
                     'index.gecko.v2.{}.signed-nightly.{}'.format(branch, rest))
 
         yield job
 
 
 @transforms.add
 def define_upstream_artifacts(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         build_platform = dep_job.attributes.get('build_platform')
 
         artifacts_specifications = generate_specifications_of_artifacts_to_sign(
             dep_job,
             keep_locale_template=False,
             kind=config.kind,
         )
 
--- a/taskcluster/taskgraph/transforms/checksums_signing.py
+++ b/taskcluster/taskgraph/transforms/checksums_signing.py
@@ -2,19 +2,20 @@
 # 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/.
 """
 Transform the checksums signing task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.schema import validate_schema
 from taskgraph.util.scriptworker import (
     get_signing_cert_scope,
     get_worker_type_for_scope,
     add_scope_prefix,
 )
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required, Optional
 
@@ -23,40 +24,39 @@ from voluptuous import Any, Required, Op
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 transforms = TransformSequence()
 
 taskref_or_string = Any(
     basestring,
     {Required('task-reference'): basestring})
 
-checksums_signing_description_schema = Schema({
-    Required('dependent-task'): object,
+checksums_signing_description_schema = schema.extend({
     Required('depname', default='beetmover'): basestring,
     Optional('label'): basestring,
     Optional('treeherder'): task_description_schema['treeherder'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             checksums_signing_description_schema, job,
             "In checksums-signing ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def make_checksums_signing_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         attributes = dep_job.attributes
 
         treeherder = job.get('treeherder', {})
         treeherder.setdefault('symbol', 'cs(N)')
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
         treeherder.setdefault('platform',
                               "{}/opt".format(dep_th_platform))
--- a/taskcluster/taskgraph/transforms/chunk_partners.py
+++ b/taskcluster/taskgraph/transforms/chunk_partners.py
@@ -25,17 +25,17 @@ def _check_repack_ids_by_platform(platfo
     repack_ids_by_platform.setdefault(platform, {})['repack_id'] = True
 
 
 @transforms.add
 def chunk_partners(config, jobs):
     partner_configs = get_partner_config_by_kind(config, config.kind)
 
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         build_platform = dep_job.attributes["build_platform"]
         # already chunked
         if dep_job.task.get('extra', {}).get('repack_id'):
             repack_id = dep_job.task['extra']['repack_id']
             if _check_repack_ids_by_platform(build_platform, repack_id):
                 continue
             partner_job = copy.deepcopy(job)
             partner_job.setdefault('extra', {}).setdefault('repack_id', repack_id)
--- a/taskcluster/taskgraph/transforms/copy_attributes_from_dependent_task.py
+++ b/taskcluster/taskgraph/transforms/copy_attributes_from_dependent_task.py
@@ -12,11 +12,11 @@ from taskgraph.util.attributes import co
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def copy_attributes(config, jobs):
     for job in jobs:
         job.setdefault('attributes', {})
-        job['attributes'].update(copy_attributes_from_dependent_job(job['dependent-task']))
+        job['attributes'].update(copy_attributes_from_dependent_job(job['primary-dependency']))
 
         yield job
--- a/taskcluster/taskgraph/transforms/l10n.py
+++ b/taskcluster/taskgraph/transforms/l10n.py
@@ -6,24 +6,24 @@ Do transforms specific to l10n kind
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import copy
 import json
 
 from mozbuild.chunkify import chunkify
+from taskgraph.loader.multi_dep import schema
 from taskgraph.transforms.base import (
     TransformSequence,
 )
 from taskgraph.util.schema import (
     validate_schema,
     optionally_keyed_by,
     resolve_keyed_by,
-    Schema,
 )
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.taskcluster import get_artifact_prefix
 from taskgraph.util.treeherder import add_suffix
 from taskgraph.transforms.job import job_description_schema
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import (
     Any,
@@ -41,17 +41,17 @@ taskref_or_string = Any(
     basestring,
     {Required('task-reference'): basestring})
 
 # Voluptuous uses marker objects as dictionary *keys*, but they are not
 # comparable, so we cast all of the keys back to regular strings
 job_description_schema = {str(k): v for k, v in job_description_schema.schema.iteritems()}
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
-l10n_description_schema = Schema({
+l10n_description_schema = schema.extend({
     # Name for this job, inferred from the dependent job before validation
     Required('name'): basestring,
 
     # build-platform, inferred from dependent job before validation
     Required('build-platform'): basestring,
 
     # max run time of the task
     Required('run-time'): _by_platform(int),
@@ -92,22 +92,16 @@ l10n_description_schema = Schema({
         # Type of index
         Optional('type'): basestring,
     },
     # Description of the localized task
     Required('description'): _by_platform(basestring),
 
     Optional('run-on-projects'): job_description_schema['run-on-projects'],
 
-    # dictionary of dependent task objects, keyed by kind.
-    Required('dependent-tasks'): {basestring: object},
-
-    # primary dependency task
-    Required('primary-dependency'): object,
-
     # worker-type to utilize
     Required('worker-type'): _by_platform(basestring),
 
     # File which contains the used locales
     Required('locales-file'): _by_platform(basestring),
 
     # Tooltool visibility required for task.
     Required('tooltool'): _by_platform(Any('internal', 'public')),
@@ -142,17 +136,17 @@ l10n_description_schema = Schema({
     },
 
     # Extra environment values to pass to the worker
     Optional('env'): _by_platform({basestring: taskref_or_string}),
 
     # Max number locales per chunk
     Optional('locales-per-chunk'): _by_platform(int),
 
-    # Task deps to chain this task with, added in transforms from dependent-task
+    # Task deps to chain this task with, added in transforms from primary-dependency
     # if this is a nightly
     Optional('dependencies'): {basestring: basestring},
 
     # Run the task when the listed files change (if present).
     Optional('when'): {
         'files-changed': [basestring]
     },
 
--- a/taskcluster/taskgraph/transforms/name_sanity.py
+++ b/taskcluster/taskgraph/transforms/name_sanity.py
@@ -1,14 +1,14 @@
 # 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/.
 """
 Generate labels for tasks without names, consistently.
-Uses attributes from `dependent-task` or `primary-dependency`.
+Uses attributes from `primary-dependency` or `primary-dependency`.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
 
 
 transforms = TransformSequence()
@@ -17,17 +17,17 @@ transforms = TransformSequence()
 @transforms.add
 def make_label(config, jobs):
     """ Generate a sane label for a new task constructed from a dependency
     Using attributes from the dependent job and the current task kind"""
     for job in jobs:
         if 'primary-dependency' in job:
             dep_job = job['primary-dependency']
         else:
-            dep_job = job['dependent-task']
+            dep_job = job['primary-dependency']
         attr = dep_job.attributes.get
 
         if attr('locale', job.get('locale')):
             template = "{kind}-{locale}-{build_platform}/{build_type}"
         elif attr('l10n_chunk'):
             template = "{kind}-{build_platform}-{l10n_chunk}/{build_type}"
         elif config.kind.startswith("release-eme-free") or \
                 config.kind.startswith("release-partner-repack"):
--- a/taskcluster/taskgraph/transforms/nightly_l10n_signing.py
+++ b/taskcluster/taskgraph/transforms/nightly_l10n_signing.py
@@ -14,33 +14,33 @@ from taskgraph.util.treeherder import jo
 transforms = TransformSequence()
 
 
 @transforms.add
 def make_signing_description(config, jobs):
     for job in jobs:
         job['depname'] = 'unsigned-repack'
 
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
 
         # add the chunk number to the TH symbol
         symbol = 'Ns{}'.format(dep_job.attributes.get('l10n_chunk'))
         group = 'L10n'
 
         job['treeherder'] = {
             'symbol': join_symbol(group, symbol),
         }
 
         yield job
 
 
 @transforms.add
 def define_upstream_artifacts(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
 
         locale_specifications = generate_specifications_of_artifacts_to_sign(
             dep_job,
             keep_locale_template=True,
         )
 
         upstream_artifacts = []
         for spec in locale_specifications:
--- a/taskcluster/taskgraph/transforms/partials.py
+++ b/taskcluster/taskgraph/transforms/partials.py
@@ -37,17 +37,17 @@ def _generate_task_output_files(job, fil
 
 
 @transforms.add
 def make_task_description(config, jobs):
     # If no balrog release history, then don't generate partials
     if not config.params.get('release_history'):
         return
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
 
         treeherder = job.get('treeherder', {})
         treeherder.setdefault('symbol', 'p(N)')
 
         label = job.get('label', "partials-{}".format(dep_job.label))
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
 
--- a/taskcluster/taskgraph/transforms/partials_signing.py
+++ b/taskcluster/taskgraph/transforms/partials_signing.py
@@ -60,17 +60,17 @@ def generate_upstream_artifacts(job, rel
         upstream_artifacts.append(old_mar_upstream_artifacts)
 
     return upstream_artifacts
 
 
 @transforms.add
 def make_task_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
 
         treeherder = job.get('treeherder', {})
         treeherder.setdefault('symbol', 'ps(N)')
 
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
         label = job.get('label', "partials-signing-{}".format(dep_job.label))
         dep_th_platform = dep_job.task.get('extra', {}).get(
--- a/taskcluster/taskgraph/transforms/partner_signing.py
+++ b/taskcluster/taskgraph/transforms/partner_signing.py
@@ -18,17 +18,17 @@ transforms.add(check_if_partners_enabled
 
 @transforms.add
 def define_upstream_artifacts(config, jobs):
     partner_configs = get_partner_config_by_kind(config, config.kind)
     if not partner_configs:
         return
 
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         repack_id = job['extra']['repack_id']
         artifacts_specifications = generate_specifications_of_artifacts_to_sign(
             dep_job,
             keep_locale_template=True,
             kind=config.kind,
         )
         job['upstream-artifacts'] = [{
             'taskId': {'task-reference': '<{}>'.format(job['depname'])},
--- a/taskcluster/taskgraph/transforms/per_platform_dummy.py
+++ b/taskcluster/taskgraph/transforms/per_platform_dummy.py
@@ -11,19 +11,19 @@ from taskgraph.transforms.base import Tr
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def one_task_per_product_and_platform(config, jobs):
     unique_products_and_platforms = set()
     for job in jobs:
-        dep_task = job["dependent-task"]
-        if 'dependent-task' in job:
-            del job['dependent-task']
+        dep_task = job["primary-dependency"]
+        if 'primary-dependency' in job:
+            del job['primary-dependency']
         product = dep_task.attributes.get("shipping_product")
         platform = dep_task.attributes.get("build_platform")
         if (product, platform) not in unique_products_and_platforms:
             job.setdefault("attributes", {})
             job["attributes"]["shipping_product"] = product
             job["attributes"]["build_platform"] = platform
             job["name"] = "{}-{}".format(product, platform)
             yield job
--- a/taskcluster/taskgraph/transforms/release_beetmover_signed_addons.py
+++ b/taskcluster/taskgraph/transforms/release_beetmover_signed_addons.py
@@ -2,20 +2,21 @@
 # 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/.
 """
 Transform the beetmover task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.transforms.beetmover import craft_release_properties
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema, optionally_keyed_by, resolve_keyed_by
+from taskgraph.util.schema import validate_schema, optionally_keyed_by, resolve_keyed_by
 from taskgraph.util.scriptworker import (get_beetmover_bucket_scope,
                                          get_beetmover_action_scope)
 from taskgraph.transforms.task import task_description_schema
 from taskgraph.transforms.release_sign_and_push_langpacks import get_upstream_task_ref
 from voluptuous import Required, Optional
 
 import logging
 import copy
@@ -24,20 +25,17 @@ logger = logging.getLogger(__name__)
 
 
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 
 transforms = TransformSequence()
 
 
-beetmover_description_schema = Schema({
-    # the dependent task (object) for this beetmover job, used to inform beetmover.
-    Required('dependent-task'): object,
-
+beetmover_description_schema = schema.extend({
     # depname is used in taskref's to identify the taskID of the unsigned things
     Required('depname', default='build'): basestring,
 
     # unique label to describe this beetmover task, defaults to {dep.label}-beetmover
     Optional('label'): basestring,
 
     # treeherder is allowed here to override any defaults we use for beetmover.  See
     # taskcluster/taskgraph/transforms/task.py for the schema details, and the
@@ -53,17 +51,17 @@ beetmover_description_schema = Schema({
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
 })
 
 
 @transforms.add
 def set_label(config, jobs):
     for job in jobs:
-        job['label'] = job['dependent-task'].label.replace(
+        job['label'] = job['primary-dependency'].label.replace(
             'sign-and-push-langpacks', 'beetmover-signed-langpacks'
         )
 
         yield job
 
 
 @transforms.add
 def validate(config, jobs):
@@ -83,17 +81,17 @@ def resolve_keys(config, jobs):
             **{'release-level': config.params.release_level()}
         )
         yield job
 
 
 @transforms.add
 def make_task_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         attributes = dep_job.attributes
 
         treeherder = job.get('treeherder', {})
         treeherder.setdefault('symbol', 'langpack(BM{})'.format(attributes.get('l10n_chunk', '')))
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
         treeherder.setdefault('platform',
                               "{}/opt".format(dep_th_platform))
@@ -153,17 +151,17 @@ def generate_upstream_artifacts(upstream
             'public/build/{}/target.langpack.xpi'.format(locale)
         ],
     } for locale in locales]
 
 
 @transforms.add
 def strip_unused_data(config, jobs):
     for job in jobs:
-        del job['dependent-task']
+        del job['primary-dependency']
 
         yield job
 
 
 @transforms.add
 def yield_all_platform_jobs(config, jobs):
     # Even though langpacks are now platform independent, we keep beetmoving them at old
     # platform-specific locations. That's why this transform exist
--- a/taskcluster/taskgraph/transforms/release_generate_checksums_beetmover.py
+++ b/taskcluster/taskgraph/transforms/release_generate_checksums_beetmover.py
@@ -1,19 +1,20 @@
 # 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/.
 """
 Transform the `release-generate-checksums-beetmover` task to also append `build` as dependency
 """
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.schema import validate_schema
 from taskgraph.util.scriptworker import (get_beetmover_bucket_scope,
                                          get_beetmover_action_scope,
                                          get_worker_type_for_scope,
                                          )
 from taskgraph.util.taskcluster import get_artifact_prefix
 from taskgraph.transforms.beetmover import craft_release_properties
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Required, Optional
@@ -35,20 +36,17 @@ CHECKSUMS_SIGNING_ARTIFACTS = [
 
 
 # Voluptuous uses marker objects as dictionary *keys*, but they are not
 # comparable, so we cast all of the keys back to regular strings
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 transforms = TransformSequence()
 
-release_generate_checksums_beetmover_schema = Schema({
-    # the dependent task (object) for this beetmover job, used to inform beetmover.
-    Required('dependent-task'): object,
-
+release_generate_checksums_beetmover_schema = schema.extend({
     # depname is used in taskref's to identify the taskID of the unsigned things
     Required('depname', default='build'): basestring,
 
     # unique label to describe this beetmover task, defaults to {dep.label}-beetmover
     Optional('label'): basestring,
 
     # treeherder is allowed here to override any defaults we use for beetmover.  See
     # taskcluster/taskgraph/transforms/task.py for the schema details, and the
@@ -58,27 +56,27 @@ release_generate_checksums_beetmover_sch
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             release_generate_checksums_beetmover_schema, job,
             "In ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def make_task_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         attributes = copy_attributes_from_dependent_job(dep_job)
 
         treeherder = job.get('treeherder', {})
         treeherder.setdefault('symbol', 'BM-SGenChcks')
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
         treeherder.setdefault('platform',
                               "{}/opt".format(dep_th_platform))
--- a/taskcluster/taskgraph/transforms/release_generate_checksums_signing.py
+++ b/taskcluster/taskgraph/transforms/release_generate_checksums_signing.py
@@ -2,58 +2,58 @@
 # 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/.
 """
 Transform the release-generate-checksums-signing task into task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.schema import validate_schema
 from taskgraph.util.scriptworker import (
     get_signing_cert_scope,
     get_worker_type_for_scope,
     add_scope_prefix,
 )
 from taskgraph.util.taskcluster import get_artifact_path
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Required, Optional
 
 # Voluptuous uses marker objects as dictionary *keys*, but they are not
 # comparable, so we cast all of the keys back to regular strings
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 transforms = TransformSequence()
 
-release_generate_checksums_signing_schema = Schema({
-    Required('dependent-task'): object,
+release_generate_checksums_signing_schema = schema.extend({
     Required('depname', default='release-generate-checksums'): basestring,
     Optional('label'): basestring,
     Optional('treeherder'): task_description_schema['treeherder'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             release_generate_checksums_signing_schema, job,
             "In ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def make_release_generate_checksums_signing_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         attributes = copy_attributes_from_dependent_job(dep_job)
 
         treeherder = job.get('treeherder', {})
         treeherder.setdefault('symbol', 'SGenChcks')
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
         treeherder.setdefault('platform',
                               "{}/opt".format(dep_th_platform))
--- a/taskcluster/taskgraph/transforms/release_sign_and_push_langpacks.py
+++ b/taskcluster/taskgraph/transforms/release_sign_and_push_langpacks.py
@@ -2,33 +2,33 @@
 # 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/.
 """
 Transform the release-sign-and-push task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema, resolve_keyed_by, optionally_keyed_by
+from taskgraph.util.schema import validate_schema, resolve_keyed_by, optionally_keyed_by
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required
 
 
 transforms = TransformSequence()
 
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 
 transforms = TransformSequence()
 
 
-langpack_sign_push_description_schema = Schema({
-    Required('dependent-task'): object,
+langpack_sign_push_description_schema = schema.extend({
     Required('label'): basestring,
     Required('description'): basestring,
     Required('worker-type'): optionally_keyed_by('release-level', basestring),
     Required('worker'): {
         Required('implementation'): 'sign-and-push-addons',
         Required('channel'): optionally_keyed_by(
             'project',
             optionally_keyed_by('platform', Any('listed', 'unlisted'))),
@@ -40,17 +40,17 @@ langpack_sign_push_description_schema = 
     Required('shipping-phase'): task_description_schema['shipping-phase'],
     Required('shipping-product'): task_description_schema['shipping-product'],
 })
 
 
 @transforms.add
 def set_label(config, jobs):
     for job in jobs:
-        label = 'sign-and-push-langpacks-{}'.format(job['dependent-task'].label)
+        label = 'sign-and-push-langpacks-{}'.format(job['primary-dependency'].label)
         job['label'] = label
 
         yield job
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
@@ -70,53 +70,53 @@ def resolve_keys(config, jobs):
         )
         resolve_keyed_by(
             job, 'scopes', item_name=job['label'],
             **{'release-level': config.params.release_level()}
         )
         resolve_keyed_by(
             job, 'worker.channel', item_name=job['label'],
             project=config.params['project'],
-            platform=job['dependent-task'].attributes['build_platform'],
+            platform=job['primary-dependency'].attributes['build_platform'],
         )
 
         yield job
 
 
 @transforms.add
 def copy_attributes(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         job['attributes'] = copy_attributes_from_dependent_job(dep_job)
         job['attributes']['chunk_locales'] = dep_job.attributes.get('chunk_locales', ['en-US'])
 
         yield job
 
 
 @transforms.add
 def filter_out_macos_jobs_but_mac_only_locales(config, jobs):
     for job in jobs:
-        build_platform = job['dependent-task'].attributes.get('build_platform')
+        build_platform = job['primary-dependency'].attributes.get('build_platform')
 
         if build_platform in ('linux64-nightly', 'linux64-devedition-nightly'):
             yield job
         elif build_platform in ('macosx64-nightly', 'macosx64-devedition-nightly') and \
                 'ja-JP-mac' in job['attributes']['chunk_locales']:
             # Other locales of the same job shouldn't be processed
             job['attributes']['chunk_locales'] = ['ja-JP-mac']
             job['label'] = job['label'].replace(
                 job['attributes']['l10n_chunk'], 'ja-JP-mac'
             )
             yield job
 
 
 @transforms.add
 def make_task_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
 
         treeherder = job.get('treeherder', {})
         treeherder.setdefault('symbol', 'langpack(SnP{})'.format(
             job['attributes'].get('l10n_chunk', '')
         ))
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
         treeherder.setdefault('platform', '{}/opt'.format(dep_th_platform))
@@ -171,11 +171,11 @@ def get_upstream_task_ref(job, expected_
         raise Exception('Only one dependency expected')
 
     return '<{}>'.format(upstream_tasks[0])
 
 
 @transforms.add
 def strip_unused_data(config, jobs):
     for job in jobs:
-        del job['dependent-task']
+        del job['primary-dependency']
 
         yield job
--- a/taskcluster/taskgraph/transforms/repackage.py
+++ b/taskcluster/taskgraph/transforms/repackage.py
@@ -4,23 +4,23 @@
 """
 Transform the repackage task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import copy
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.schema import (
     validate_schema,
     optionally_keyed_by,
     resolve_keyed_by,
-    Schema,
 )
 from taskgraph.util.taskcluster import get_artifact_prefix
 from taskgraph.util.platforms import archive_format, executable_extension
 from taskgraph.util.workertypes import worker_type_implementation
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required, Optional
 
 transforms = TransformSequence()
@@ -30,20 +30,17 @@ transforms = TransformSequence()
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 
 # shortcut for a string where task references are allowed
 taskref_or_string = Any(
     basestring,
     {Required('task-reference'): basestring})
 
-packaging_description_schema = Schema({
-    # the dependant task (object) for this  job, used to inform repackaging.
-    Required('dependent-task'): object,
-
+packaging_description_schema = schema.extend({
     # depname is used in taskref's to identify the taskID of the signed things
     Required('depname', default='build'): basestring,
 
     # unique label to describe this repackaging task
     Optional('label'): basestring,
 
     # treeherder is allowed here to override any defaults we use for repackaging.  See
     # taskcluster/taskgraph/transforms/task.py for the schema details, and the
@@ -139,28 +136,28 @@ PACKAGE_FORMATS = {
         'output': 'target.stub-installer.exe',
     },
 }
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             packaging_description_schema, job,
             "In packaging ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def copy_in_useful_magic(config, jobs):
     """Copy attributes from upstream task to be used for keyed configuration."""
     for job in jobs:
-        dep = job['dependent-task']
+        dep = job['primary-dependency']
         job['build-platform'] = dep.attributes.get("build_platform")
         yield job
 
 
 @transforms.add
 def handle_keyed_by(config, jobs):
     """Resolve fields that can be keyed by platform, etc."""
     fields = [
@@ -176,29 +173,29 @@ def handle_keyed_by(config, jobs):
                 item_name="?",
             )
         yield job
 
 
 @transforms.add
 def make_repackage_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
 
         label = job.get('label',
                         dep_job.label.replace("signing-", "repackage-"))
         job['label'] = label
 
         yield job
 
 
 @transforms.add
 def make_job_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         dependencies = {dep_job.attributes.get('kind'): dep_job.label}
         if len(dep_job.dependencies) > 1:
             raise NotImplementedError(
                 "Can't repackage a signing task with multiple dependencies")
         signing_dependencies = dep_job.dependencies
         # This is so we get the build task in our dependencies to
         # have better beetmover support.
         dependencies.update(signing_dependencies)
--- a/taskcluster/taskgraph/transforms/repackage_l10n.py
+++ b/taskcluster/taskgraph/transforms/repackage_l10n.py
@@ -12,15 +12,15 @@ import copy
 from taskgraph.transforms.base import TransformSequence
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def split_locales(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         for locale in dep_job.attributes.get('chunk_locales', []):
             locale_job = copy.deepcopy(job)  # don't overwrite dict values here
             treeherder = locale_job.setdefault('treeherder', {})
             treeherder['symbol'] = 'L10n-Rpk({})'.format(locale)
             locale_job['locale'] = locale
             yield locale_job
--- a/taskcluster/taskgraph/transforms/repackage_partner.py
+++ b/taskcluster/taskgraph/transforms/repackage_partner.py
@@ -4,23 +4,23 @@
 """
 Transform the repackage task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import copy
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.schema import (
     validate_schema,
     optionally_keyed_by,
     resolve_keyed_by,
-    Schema,
 )
 from taskgraph.util.taskcluster import get_artifact_prefix
 from taskgraph.util.partners import check_if_partners_enabled
 from taskgraph.util.platforms import archive_format, executable_extension
 from taskgraph.util.workertypes import worker_type_implementation
 from taskgraph.transforms.task import task_description_schema
 from taskgraph.transforms.repackage import PACKAGE_FORMATS
 from voluptuous import Any, Required, Optional
@@ -36,20 +36,17 @@ def _by_platform(arg):
     return optionally_keyed_by('build-platform', arg)
 
 
 # shortcut for a string where task references are allowed
 taskref_or_string = Any(
     basestring,
     {Required('task-reference'): basestring})
 
-packaging_description_schema = Schema({
-    # the dependant task (object) for this  job, used to inform repackaging.
-    Required('dependent-task'): object,
-
+packaging_description_schema = schema.extend({
     # depname is used in taskref's to identify the taskID of the signed things
     Required('depname', default='build'): basestring,
 
     # unique label to describe this repackaging task
     Optional('label'): basestring,
 
     # Routes specific to this task, if defined
     Optional('routes'): [basestring],
@@ -79,28 +76,28 @@ packaging_description_schema = Schema({
 })
 
 transforms.add(check_if_partners_enabled)
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             packaging_description_schema, job,
             "In packaging ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def copy_in_useful_magic(config, jobs):
     """Copy attributes from upstream task to be used for keyed configuration."""
     for job in jobs:
-        dep = job['dependent-task']
+        dep = job['primary-dependency']
         job['build-platform'] = dep.attributes.get("build_platform")
         yield job
 
 
 @transforms.add
 def handle_keyed_by(config, jobs):
     """Resolve fields that can be keyed by platform, etc."""
     fields = [
@@ -112,29 +109,29 @@ def handle_keyed_by(config, jobs):
         for field in fields:
             resolve_keyed_by(item=job, field=field, item_name="?")
         yield job
 
 
 @transforms.add
 def make_repackage_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
 
         label = job.get('label',
                         dep_job.label.replace("signing-", "repackage-"))
         job['label'] = label
 
         yield job
 
 
 @transforms.add
 def make_job_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         attributes = copy_attributes_from_dependent_job(dep_job)
         build_platform = attributes['build_platform']
 
         if job['build-platform'].startswith('win'):
             if dep_job.kind.endswith('signing'):
                 continue
         if job['build-platform'].startswith('macosx'):
             if dep_job.kind.endswith('repack'):
--- a/taskcluster/taskgraph/transforms/repackage_signing.py
+++ b/taskcluster/taskgraph/transforms/repackage_signing.py
@@ -4,35 +4,35 @@
 """
 Transform the repackage signing task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 import os
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.schema import validate_schema
 from taskgraph.util.scriptworker import (
     add_scope_prefix,
     get_signing_cert_scope_per_platform,
     get_worker_type_for_scope,
 )
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Required, Optional
 
 # Voluptuous uses marker objects as dictionary *keys*, but they are not
 # comparable, so we cast all of the keys back to regular strings
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 transforms = TransformSequence()
 
-repackage_signing_description_schema = Schema({
-    Required('dependent-task'): object,
+repackage_signing_description_schema = schema.extend({
     Required('depname', default='repackage'): basestring,
     Optional('label'): basestring,
     Optional('treeherder'): task_description_schema['treeherder'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
 })
 
 SIGNING_FORMATS = {
@@ -41,27 +41,27 @@ SIGNING_FORMATS = {
     "target.installer.exe": ["sha2signcode"],
     "target.stub-installer.exe": ["sha2signcodestub"],
 }
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             repackage_signing_description_schema, job,
             "In repackage-signing ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def make_repackage_signing_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         attributes = copy_attributes_from_dependent_job(dep_job)
         attributes['repackage_type'] = 'repackage-signing'
 
         treeherder = job.get('treeherder', {})
         if attributes.get('nightly'):
             treeherder.setdefault('symbol', 'rs(N)')
         else:
             treeherder.setdefault('symbol', 'rs(B)')
--- a/taskcluster/taskgraph/transforms/repackage_signing_partner.py
+++ b/taskcluster/taskgraph/transforms/repackage_signing_partner.py
@@ -2,60 +2,60 @@
 # 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/.
 """
 Transform the repackage signing task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.partners import check_if_partners_enabled
-from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.schema import validate_schema
 from taskgraph.util.scriptworker import (
     add_scope_prefix,
     get_signing_cert_scope_per_platform,
 )
 from taskgraph.util.taskcluster import get_artifact_path
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Required, Optional
 
 # Voluptuous uses marker objects as dictionary *keys*, but they are not
 # comparable, so we cast all of the keys back to regular strings
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 transforms = TransformSequence()
 
-repackage_signing_description_schema = Schema({
-    Required('dependent-task'): object,
+repackage_signing_description_schema = schema.extend({
     Required('depname', default='repackage'): basestring,
     Optional('label'): basestring,
     Optional('extra'): object,
     Optional('shipping-product'): task_description_schema['shipping-product'],
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
 })
 
 transforms.add(check_if_partners_enabled)
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             repackage_signing_description_schema, job,
             "In repackage-signing ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def make_repackage_signing_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         repack_id = dep_job.task['extra']['repack_id']
         attributes = dep_job.attributes
         build_platform = dep_job.attributes.get('build_platform')
         is_nightly = dep_job.attributes.get('nightly')
 
         # Mac & windows
         label = dep_job.label.replace("repackage-", "repackage-signing-")
         # Linux
--- a/taskcluster/taskgraph/transforms/signing.py
+++ b/taskcluster/taskgraph/transforms/signing.py
@@ -2,19 +2,20 @@
 # 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/.
 """
 Transform the signing task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.schema import validate_schema
 from taskgraph.util.scriptworker import (
     add_scope_prefix,
     get_signing_cert_scope_per_platform,
     get_worker_type_for_scope,
 )
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required, Optional
 
@@ -25,20 +26,17 @@ task_description_schema = {str(k): v for
 
 transforms = TransformSequence()
 
 # shortcut for a string where task references are allowed
 taskref_or_string = Any(
     basestring,
     {Required('task-reference'): basestring})
 
-signing_description_schema = Schema({
-    # the dependant task (object) for this signing job, used to inform signing.
-    Required('dependent-task'): object,
-
+signing_description_schema = schema.extend({
     # Artifacts from dep task to sign - Sync with taskgraph/transforms/task.py
     # because this is passed directly into the signingscript worker
     Required('upstream-artifacts'): [{
         # taskId of the task with the artifact
         Required('taskId'): taskref_or_string,
 
         # type of signing task (for CoT)
         Required('taskType'): basestring,
@@ -78,27 +76,27 @@ def set_defaults(config, jobs):
     for job in jobs:
         job.setdefault('depname', 'build')
         yield job
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             signing_description_schema, job,
             "In signing ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def make_task_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         attributes = dep_job.attributes
 
         signing_format_scopes = []
         formats = set([])
         for artifacts in job['upstream-artifacts']:
             for f in artifacts['formats']:
                 formats.add(f)  # Add each format only once
         for format in formats:
--- a/taskcluster/taskgraph/transforms/source_checksums_signing.py
+++ b/taskcluster/taskgraph/transforms/source_checksums_signing.py
@@ -2,19 +2,20 @@
 # 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/.
 """
 Transform the checksums signing task into an actual task description.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
+from taskgraph.loader.single_dep import schema
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
-from taskgraph.util.schema import validate_schema, Schema
+from taskgraph.util.schema import validate_schema
 from taskgraph.util.scriptworker import (
     get_signing_cert_scope,
     get_worker_type_for_scope,
     add_scope_prefix,
 )
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required, Optional
 
@@ -23,40 +24,39 @@ from voluptuous import Any, Required, Op
 task_description_schema = {str(k): v for k, v in task_description_schema.schema.iteritems()}
 
 transforms = TransformSequence()
 
 taskref_or_string = Any(
     basestring,
     {Required('task-reference'): basestring})
 
-checksums_signing_description_schema = Schema({
-    Required('dependent-task'): object,
+checksums_signing_description_schema = schema.extend({
     Required('depname', default='beetmover'): basestring,
     Optional('label'): basestring,
     Optional('treeherder'): task_description_schema['treeherder'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
     Optional('shipping-phase'): task_description_schema['shipping-phase'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
-        label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
+        label = job.get('primary-dependency', object).__dict__.get('label', '?no-label?')
         validate_schema(
             checksums_signing_description_schema, job,
             "In checksums-signing ({!r} kind) task for {!r}:".format(config.kind, label))
         yield job
 
 
 @transforms.add
 def make_checksums_signing_description(config, jobs):
     for job in jobs:
-        dep_job = job['dependent-task']
+        dep_job = job['primary-dependency']
         attributes = dep_job.attributes
 
         treeherder = job.get('treeherder', {})
         treeherder.setdefault('symbol', 'css(N)')
         dep_th_platform = dep_job.task.get('extra', {}).get(
             'treeherder', {}).get('machine', {}).get('platform', '')
         treeherder.setdefault('platform',
                               "{}/opt".format(dep_th_platform))
--- a/taskcluster/taskgraph/transforms/strip_dependent_task.py
+++ b/taskcluster/taskgraph/transforms/strip_dependent_task.py
@@ -10,10 +10,10 @@ from __future__ import absolute_import, 
 from taskgraph.transforms.base import TransformSequence
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def strip_dependent_task(config, jobs):
     for job in jobs:
-        del job['dependent-task']
+        del job['primary-dependency']
         yield job
--- a/taskcluster/taskgraph/transforms/upload_generated_sources.py
+++ b/taskcluster/taskgraph/transforms/upload_generated_sources.py
@@ -13,18 +13,18 @@ from taskgraph.util.taskcluster import g
 
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def add_task_info(config, jobs):
     for job in jobs:
-        dep_task = job['dependent-task']
-        del job['dependent-task']
+        dep_task = job['primary-dependency']
+        del job['primary-dependency']
 
         # Add a dependency on the build task.
         job['dependencies'] = {'build': dep_task.label}
         # Label the job to match the build task it's uploading from.
         job['label'] = dep_task.label.replace("build-", "upload-generated-sources-")
         # Copy over some bits of metdata from the build task.
         dep_th = dep_task.task['extra']['treeherder']
         job.setdefault('attributes', {})
--- a/taskcluster/taskgraph/transforms/upload_symbols.py
+++ b/taskcluster/taskgraph/transforms/upload_symbols.py
@@ -21,29 +21,29 @@ transforms = TransformSequence()
 @transforms.add
 def check_nightlies(config, tasks):
     """Ensure that we upload symbols for all nightly builds, so that crash-stats can
     resolve any reports sent to it. Try may enable full symbols but not upload them.
 
     Putting this check here (instead of the transforms for the build kind) lets us
     leverage the any not-for-build-platforms set in the update-symbols kind."""
     for task in tasks:
-        dep = task['dependent-task']
+        dep = task['primary-dependency']
         if config.params['project'] in RELEASE_PROJECTS and \
                 dep.attributes.get('nightly') and \
                 not dep.attributes.get('enable-full-crashsymbols'):
             raise Exception('Nightly job %s should have enable-full-crashsymbols attribute '
                             'set to true to enable symbol upload to crash-stats' % dep.label)
         yield task
 
 
 @transforms.add
 def fill_template(config, tasks):
     for task in tasks:
-        dep = task['dependent-task']
+        dep = task['primary-dependency']
 
         # Fill out the dynamic fields in the task description
         task['label'] = dep.label + '-upload-symbols'
 
         # Skip tasks where we don't have the full crashsymbols enabled
         if not dep.attributes.get('enable-full-crashsymbols'):
             logger.debug("Skipping upload symbols task for %s", task['label'])
             continue
@@ -75,11 +75,11 @@ def fill_template(config, tasks):
         # Disambiguate the treeherder symbol.
         sym = 'Sym' + (th_symbol[1:] if th_symbol.startswith('B') else th_symbol)
         treeherder.setdefault(
             'symbol', join_symbol(th_groupsymbol, sym)
         )
         task['treeherder'] = treeherder
 
         # clear out the stuff that's not part of a task description
-        del task['dependent-task']
+        del task['primary-dependency']
 
         yield task
--- a/taskcluster/taskgraph/util/schema.py
+++ b/taskcluster/taskgraph/util/schema.py
@@ -194,24 +194,31 @@ def check_schema(schema):
             for i, v in enumerate(sch):
                 iter("{}[{}]".format(path, i), v)
         elif isinstance(sch, voluptuous.Any):
             for v in sch.validators:
                 iter(path, v)
     iter('schema', schema.schema)
 
 
-def Schema(*args, **kwargs):
+class Schema(voluptuous.Schema):
     """
     Operates identically to voluptuous.Schema, but applying some taskgraph-specific checks
     in the process.
     """
-    schema = voluptuous.Schema(*args, **kwargs)
-    check_schema(schema)
-    return schema
+    def __init__(self, *args, **kwargs):
+        super(Schema, self).__init__(*args, **kwargs)
+        check_schema(self)
+
+    def extend(self, *args, **kwargs):
+        schema = super(Schema, self).extend(*args, **kwargs)
+        check_schema(schema)
+        # We want twice extend schema to be checked too.
+        schema.__class__ = Schema
+        return schema
 
 
 OptimizationSchema = voluptuous.Any(
     # always run this task (default)
     None,
     # search the index for the given index namespaces, and replace this task if found
     # the search occurs in order, with the first match winning
     {'index-search': [basestring]},
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py
+++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py
@@ -1092,48 +1092,60 @@ or run without that action (ie: --no-{ac
         self._run_mach_command_in_build_env(['build', '-v'])
 
         self.generate_build_props(console_output=True, halt_on_failure=True)
         self._generate_build_stats()
 
     def static_analysis_autotest(self):
         """Run mach static-analysis autotest, in order to make sure we dont regress"""
         self.preflight_build()
-        self._run_mach_command_in_build_env(['static-analysis', 'autotest', '--intree-tool'])
+        self._run_mach_command_in_build_env(['configure'])
+        self._run_mach_command_in_build_env(['static-analysis', 'autotest',
+                                             '--intree-tool'],
+                                            use_subprocess=True)
 
     def _query_mach(self):
         dirs = self.query_abs_dirs()
 
         if 'MOZILLABUILD' in os.environ:
             # We found many issues with intermittent build failures when not
             # invoking mach via bash.
             # See bug 1364651 before considering changing.
             mach = [
                 os.path.join(os.environ['MOZILLABUILD'], 'msys', 'bin', 'bash.exe'),
                 os.path.join(dirs['abs_src_dir'], 'mach')
             ]
         else:
             mach = [sys.executable, 'mach']
         return mach
 
-    def _run_mach_command_in_build_env(self, args):
+    def _run_mach_command_in_build_env(self, args, use_subprocess=False):
         """Run a mach command in a build context."""
         env = self.query_build_env()
         env.update(self.query_mach_build_env())
 
         dirs = self.query_abs_dirs()
 
         mach = self._query_mach()
 
-        return_code = self.run_command(
-            command=mach + ['--log-no-times'] + args,
-            cwd=dirs['abs_src_dir'],
-            env=env,
-            output_timeout=self.config.get('max_build_output_timeout', 60 * 40)
-        )
+        # XXX See bug 1483883
+        # Work around an interaction between Gradle and mozharness
+        # Not using `subprocess` causes gradle to hang
+        if use_subprocess:
+            import subprocess
+            return_code = subprocess.call(mach + ['--log-no-times'] + args,
+                                          env=env, cwd=dirs['abs_src_dir'])
+        else:
+            return_code = self.run_command(
+                command=mach + ['--log-no-times'] + args,
+                cwd=dirs['abs_src_dir'],
+                env=env,
+                output_timeout=self.config.get('max_build_output_timeout',
+                                               60 * 40)
+            )
 
         if return_code:
             self.return_code = self.worst_level(
                 EXIT_STATUS_DICT[TBPL_FAILURE], self.return_code,
                 AUTOMATION_EXIT_CODES[::-1]
             )
             self.fatal("'mach %s' did not run successfully. Please check "
                        "log for errors." % ' '.join(args))
rename from testing/web-platform/meta/beacon/beacon-error.window.js.ini
rename to testing/web-platform/meta/beacon/beacon-error.sub.window.js.ini
--- a/testing/web-platform/meta/beacon/beacon-error.window.js.ini
+++ b/testing/web-platform/meta/beacon/beacon-error.sub.window.js.ini
@@ -1,9 +1,9 @@
-[beacon-error.window.html]
+[beacon-error.sub.window.html]
   [Verify calling 'navigator.sendBeacon()' with a large payload returns 'false'.]
     expected: FAIL
 
   [Verify calling 'navigator.sendBeacon()' with a small payload fails while Quota is completely utilized.]
     expected: FAIL
 
   [Verify the behavior after the quota is exhausted.]
     expected: FAIL
rename from testing/web-platform/tests/beacon/beacon-error.window.js
rename to testing/web-platform/tests/beacon/beacon-error.sub.window.js
--- a/testing/web-platform/tests/beacon/beacon-error.window.js
+++ b/testing/web-platform/tests/beacon/beacon-error.sub.window.js
@@ -2,17 +2,17 @@
 // META: script=beacon-common.sub.js
 
 "use strict";
 
 test(function() {
     // Payload that should cause sendBeacon to return false because it exceeds the maximum payload size.
     var exceedPayload = Array(maxPayloadSize + 1).fill('z').join("");
 
-    var success = navigator.sendBeacon("http://doesnotmatter", exceedPayload);
+    var success = navigator.sendBeacon("http://{{hosts[][nonexistent]}}", exceedPayload);
     assert_false(success, "calling 'navigator.sendBeacon()' with payload size exceeding the maximum size must fail");
 }, "Verify calling 'navigator.sendBeacon()' with a large payload returns 'false'.");
 
 test(function() {
     var invalidUrl = "http://invalid:url";
     assert_throws(new TypeError(), function() { navigator.sendBeacon(invalidUrl, smallPayload); },
         `calling 'navigator.sendBeacon()' with an invalid URL '${invalidUrl}' must throw a TypeError`);
 }, "Verify calling 'navigator.sendBeacon()' with an invalid URL throws an exception.");
--- a/testing/xpcshell/remotexpcshelltests.py
+++ b/testing/xpcshell/remotexpcshelltests.py
@@ -425,22 +425,19 @@ class XPCShellRemote(xpcshell.XPCShellTe
             remoteFile = posixpath.join(self.remoteBinDir,
                                         os.path.basename(self.options['localAPK']))
             self.device.push(self.options['localAPK'], remoteFile)
             self.device.chmod(remoteFile, root=True)
 
         self.pushLibs()
 
     def pushLibs(self):
-        elfhack = None
-        xrePath = self.options.get('xrePath')
-        if xrePath:
-            elfhack = os.path.join(xrePath, 'elfhack')
-            if not os.path.exists(elfhack):
-                elfhack = None
+        elfhack = os.path.join(self.localBin, 'elfhack')
+        if not os.path.exists(elfhack):
+            elfhack = None
         pushed_libs_count = 0
         if self.options['localAPK']:
             try:
                 dir = tempfile.mkdtemp()
                 for info in self.localAPKContents.infolist():
                     if info.filename.endswith(".so"):
                         print("Pushing %s.." % info.filename, file=sys.stderr)
                         remoteFile = posixpath.join(self.remoteBinDir,
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -2002,16 +2002,35 @@
     "record_in_processes": ["main"],
     "expires_in_version": "68",
     "alert_emails": ["necko@mozilla.com", "junior@mozilla.com"],
     "bug_numbers": [1280629, 1494133],
     "kind": "categorical",
     "labels": ["Suspended", "NotSuspended", "SuspendedLocal", "NotSuspendedLocal"],
     "description": "Collect whether the resource is suspended by back pressure. And split by local-ness."
   },
+  "NETWORK_BACK_PRESSURE_SUSPENSION_CP_TYPE": {
+    "record_in_processes": ["main"],
+    "expires_in_version": "68",
+    "alert_emails": ["necko@mozilla.com", "junior@mozilla.com"],
+    "bug_numbers": [1487559],
+    "kind": "enumerated",
+    "n_values": 64,
+    "description": "Collect the content policy when the resource is non-local and suspended by back pressure (0-44 from nsContentPolicyType in nsIContentPolicy.idl)."
+  },
+  "NETWORK_BACK_PRESSURE_SUSPENSION_DELAY_TIME_MS": {
+    "record_in_processes": ["main"],
+    "expires_in_version": "68",
+    "alert_emails": ["necko@mozilla.com", "junior@mozilla.com"],
+    "bug_numbers": [1498434],
+    "kind": "exponential",
+    "high": 60000,
+    "n_buckets": 100,
+    "description": "The delay caused by the e10s back pressure suspension(ms)"
+  },
   "NETWORK_HTTP_REDIRECT_TO_SCHEME" :{
     "record_in_processes": ["main"],
     "alert_emails": ["necko@mozilla.com", "seceng-telemetry@mozilla.com", "jkt@mozilla.com"],
     "bug_numbers": [1413512],
     "expires_in_version": "64",
     "kind": "categorical",
     "keyed": true,
     "description": "Count of the HTTP redirection that triggered by top-level document or by subresource, keyed by the URL scheme redirected to.",
--- a/toolkit/content/license.html
+++ b/toolkit/content/license.html
@@ -4978,17 +4978,17 @@ PERFORMANCE OF THIS SOFTWARE.
 
 #if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX)
 
     <hr>
 
     <h1><a id="openvr"></a>OpenVR License</h1>
 
     <p>This license applies to certain files in the directory
-    <code>gfx/vr/openvr</code>.</p>
+    <code>gfx/vr/service/openvr</code>.</p>
 <pre>
 Copyright (c) 2015, Valve Corporation
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification,
 are permitted provided that the following conditions are met:
 
 1. Redistributions of source code must retain the above copyright notice, this
--- a/tools/infer/config.yaml
+++ b/tools/infer/config.yaml
@@ -1,23 +1,31 @@
 ---
 target: obj-x86_64-pc-linux-gnu
 # It is used by 'mach static-analysis' and 'mozreview static-analysis bot'
 # in order to have consistency across the used checkers.
 platforms:
   - linux64
 infer_checkers:
+  # no issues were ever trigger by this
   - name: check-nullable
+    publish: !!bool no
+  - name: biabduction
     publish: !!bool yes
+  # very very noisy
+  # it could be useful, but it won't be part of the default enabled checkers
   - name: eradicate
     publish: !!bool no
+  # hard to use, not useful
   - name: quandary
-    publish: !!bool yes
+    publish: !!bool no
   - name: starvation
     publish: !!bool yes
+  # experimental
   - name: litho
-    publish: !!bool yes
+    publish: !!bool no
   - name: racerd
     publish: !!bool yes
+  # I think this is only for c++, can't trigger these errors in Java
   - name: liveness
-    publish: !!bool yes
+    publish: !!bool no
 # Third party files from mozilla-central
 third_party: tools/rewriting/ThirdPartyPaths.txt
new file mode 100644
--- /dev/null
+++ b/tools/infer/test/autotest/build.gradle
@@ -0,0 +1,25 @@
+buildDir "${topobjdir}/gradle/build/tools/infer/test/autotest"
+
+apply plugin: 'java'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    compile "com.google.code.findbugs:jsr305:3.0.2"
+}
+
+def createSingleTask = { name ->
+    task("compileInferTest${name}", type: JavaCompile) {
+        source = fileTree(dir: '.', include: "src/main/java/${name}.java")
+        classpath = project.configurations.compileClasspath
+        destinationDir = file("${topobjdir}/gradle/build/tools/infer/test/autotest")
+    }
+}
+
+createSingleTask('Biabduction')
+createSingleTask('Checkers')
+createSingleTask('Eradicate')
+createSingleTask('Racerd')
+createSingleTask('Starvation')
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/tools/infer/test/autotest/src/main/java/Biabduction.java
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import javax.annotation.Nullable;
+import java.util.List;
+
+public class Biabduction {
+    private String get() { return null; }
+
+    public void f1() {
+        get().length(); // error
+    }
+
+    public void f2() {
+        try {
+            get().length(); // error
+        } catch (NullPointerException e) {
+
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/tools/infer/test/autotest/src/main/java/Biabduction.json
@@ -0,0 +1,114 @@
+[
+    {
+        "bug_class": "PROVER",
+        "bug_trace": [
+            {
+                "column_number": -1,
+                "description": "start of procedure f1()",
+                "filename": "autotest/src/main/java/Biabduction.java",
+                "level": 0,
+                "line_number": 11
+            },
+            {
+                "column_number": -1,
+                "description": "",
+                "filename": "autotest/src/main/java/Biabduction.java",
+                "level": 0,
+                "line_number": 12
+            },
+            {
+                "column_number": -1,
+                "description": "start of procedure get()",
+                "filename": "autotest/src/main/java/Biabduction.java",
+                "level": 1,
+                "line_number": 9
+            },
+            {
+                "column_number": -1,
+                "description": "return from a call to String Biabduction.get()",
+                "filename": "autotest/src/main/java/Biabduction.java",
+                "level": 1,
+                "line_number": 9
+            },
+            {
+                "column_number": -1,
+                "description": "",
+                "filename": "autotest/src/main/java/Biabduction.java",
+                "level": 0,
+                "line_number": 12
+            }
+        ],
+        "bug_type": "NULL_DEREFERENCE",
+        "bug_type_hum": "Null Dereference",
+        "censored_reason": "",
+        "column": -1,
+        "file": "autotest/src/main/java/Biabduction.java",
+        "hash": "918d7eaedf45f651f04c55554c72478c",
+        "key": "Biabduction.java|f1|NULL_DEREFERENCE",
+        "kind": "ERROR",
+        "line": 12,
+        "node_key": "9afcdcc9d4253c36267a0d34b98c337d",
+        "procedure": "void Biabduction.f1()",
+        "procedure_id": "Biabduction.f1():void.4b49520e7621606a0d5661886ff0b098",
+        "procedure_start_line": 11,
+        "qualifier": "object returned by `get(this)` could be null and is dereferenced at line 12.",
+        "severity": "HIGH",
+        "visibility": "user"
+    },
+    {
+        "bug_class": "PROVER",
+        "bug_trace": [
+            {
+                "column_number": -1,
+                "description": "start of procedure f2()",
+                "filename": "autotest/src/main/java/Biabduction.java",
+                "level": 0,
+                "line_number": 15
+            },
+            {
+                "column_number": -1,
+                "description": "",
+                "filename": "autotest/src/main/java/Biabduction.java",
+                "level": 0,
+                "line_number": 17
+            },
+            {
+                "column_number": -1,
+                "description": "start of procedure get()",
+                "filename": "autotest/src/main/java/Biabduction.java",
+                "level": 1,
+                "line_number": 9
+            },
+            {
+                "column_number": -1,
+                "description": "return from a call to String Biabduction.get()",
+                "filename": "autotest/src/main/java/Biabduction.java",
+                "level": 1,
+                "line_number": 9
+            },
+            {
+                "column_number": -1,
+                "description": "",
+                "filename": "autotest/src/main/java/Biabduction.java",
+                "level": 0,
+                "line_number": 17
+            }
+        ],
+        "bug_type": "NULL_DEREFERENCE",
+        "bug_type_hum": "Null Dereference",
+        "censored_reason": "",
+        "column": -1,
+        "file": "autotest/src/main/java/Biabduction.java",
+        "hash": "bc952ce8bad58dac5cb6672dc3150524",
+        "key": "Biabduction.java|f2|NULL_DEREFERENCE",
+        "kind": "ERROR",
+        "line": 17,
+        "node_key": "9afcdcc9d4253c36267a0d34b98c337d",
+        "procedure": "void Biabduction.f2()",
+        "procedure_id": "Biabduction.f2():void.41c05a632eb912a458482c1e2e4dcbb4",
+        "procedure_start_line": 15,
+        "qualifier": "object returned by `get(this)` could be null and is dereferenced at line 17.",
+        "severity": "HIGH",
+        "visibility": "user"
+    }
+]
new file mode 100644
--- /dev/null
+++ b/tools/infer/test/autotest/src/main/java/Checkers.java
@@ -0,0 +1,34 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+
+public class Checkers {
+    public static void leak() {
+        try {
+            BufferedReader br = new BufferedReader(
+                new FileReader(new File("some.txt"))
+            );
+        } catch (Exception e) {
+
+        }
+    }
+
+    public static void error1() {
+        String str = null;
+        try {
+            int x = str.length(); // Error: even if exception is caught
+        } catch (NullPointerException e) {
+
+        }
+    }
+
+    public static void error2() {
+        String str = null;
+        int x = str.length(); // Error: not checking for null
+    }
+
+}
new file mode 100644
--- /dev/null
+++ b/tools/infer/test/autotest/src/main/java/Checkers.json
@@ -0,0 +1,121 @@
+[
+    {
+        "bug_class": "PROVER",
+        "bug_trace": [
+            {
+                "column_number": -1,
+                "description": "start of procedure leak()",
+                "filename": "autotest/src/main/java/Checkers.java",
+                "level": 0,
+                "line_number": 10
+            },
+            {
+                "column_number": -1,
+                "description": "",
+                "filename": "autotest/src/main/java/Checkers.java",
+                "level": 0,
+                "line_number": 12
+            }
+        ],
+        "bug_type": "RESOURCE_LEAK",
+        "bug_type_hum": "Resource Leak",
+        "censored_reason": "",
+        "column": -1,
+        "file": "autotest/src/main/java/Checkers.java",
+        "hash": "56806153823413731f2e2166ed8d30a0",
+        "key": "Checkers.java|leak|RESOURCE_LEAK",
+        "kind": "ERROR",
+        "line": 12,
+        "node_key": "3a2af627d5d1f10e1994f6259cf18e4c",
+        "procedure": "void Checkers.leak()",
+        "procedure_id": "Checkers.leak():void.e21648e10d3037f4559cdb7c08642c84",
+        "procedure_start_line": 10,
+        "qualifier": "resource of type `java.io.FileReader` acquired by call to `new()` at line 12 is not released after line 12.",
+        "severity": "HIGH",
+        "visibility": "user"
+    },
+    {
+        "bug_class": "PROVER",
+        "bug_trace": [
+            {
+                "column_number": -1,
+                "description": "start of procedure error1()",
+                "filename": "autotest/src/main/java/Checkers.java",
+                "level": 0,
+                "line_number": 20
+            },
+            {
+                "column_number": -1,
+                "description": "",
+                "filename": "autotest/src/main/java/Checkers.java",
+                "level": 0,
+                "line_number": 21
+            },
+            {
+                "column_number": -1,
+                "description": "",
+                "filename": "autotest/src/main/java/Checkers.java",
+                "level": 0,
+                "line_number": 23
+            }
+        ],
+        "bug_type": "NULL_DEREFERENCE",
+        "bug_type_hum": "Null Dereference",
+        "censored_reason": "",
+        "column": -1,
+        "file": "autotest/src/main/java/Checkers.java",
+        "hash": "6de26e7c66c71b1114ad233679d55640",
+        "key": "Checkers.java|error1|NULL_DEREFERENCE",
+        "kind": "ERROR",
+        "line": 23,
+        "node_key": "c281f77c6dae544ee5fb7d5e2bb35118",
+        "procedure": "void Checkers.error1()",
+        "procedure_id": "Checkers.error1():void.59417424d80960700a32012973e98db5",
+        "procedure_start_line": 20,
+        "qualifier": "object `str` last assigned on line 21 could be null and is dereferenced at line 23.",
+        "severity": "HIGH",
+        "visibility": "user"
+    },
+    {
+        "bug_class": "PROVER",
+        "bug_trace": [
+            {
+                "column_number": -1,
+                "description": "start of procedure error2()",
+                "filename": "autotest/src/main/java/Checkers.java",
+                "level": 0,
+                "line_number": 29
+            },
+            {
+                "column_number": -1,
+                "description": "",
+                "filename": "autotest/src/main/java/Checkers.java",
+                "level": 0,
+                "line_number": 30
+            },
+            {
+                "column_number": -1,
+                "description": "",
+                "filename": "autotest/src/main/java/Checkers.java",
+                "level": 0,
+                "line_number": 31
+            }
+        ],
+        "bug_type": "NULL_DEREFERENCE",
+        "bug_type_hum": "Null Dereference",
+        "censored_reason": "",
+        "column": -1,
+        "file": "autotest/src/main/java/Checkers.java",
+        "hash": "39e021b634ab428af7be2034688491a7",
+        "key": "Checkers.java|error2|NULL_DEREFERENCE",
+        "kind": "ERROR",
+        "line": 31,
+        "node_key": "c281f77c6dae544ee5fb7d5e2bb35118",
+        "procedure": "void Checkers.error2()",
+        "procedure_id": "Checkers.error2():void.e9146d80ba20c908c11d08947cd89d06",
+        "procedure_start_line": 29,
+        "qualifier": "object `str` last assigned on line 30 could be null and is dereferenced at line 31.",
+        "severity": "HIGH",
+        "visibility": "user"
+    }
+]
new file mode 100644
--- /dev/null
+++ b/tools/infer/test/autotest/src/main/java/Eradicate.java
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import javax.annotation.Nullable;
+
+// Examples taken from the infer website.
+public class Eradicate {
+
+    public String f; // Because it is not annoted with nullable -> can never be null!
+
+    public void field(@Nullable Eradicate x) {
+        x.f = "3"; // Error: Eradicate null field access
+    }
+
+    public void method(@Nullable Object x) {
+        String s = x.toString(); // Error: Eradicate null method call
+    }
+
+    public void filedNotNull(@Nullable String s) {
+        f = s; // Error: Eradicate field not nullable
+    }
+
+    public Eradicate() {} // Error: Eradicate field not initialized
+
+    public void str(Eradicate x) {
+        String s = x.toString();
+    }
+
+    public void callStr(@Nullable Eradicate x) {
+        str(x); // Error:  Eradicate parameter not nullable
+    }
+
+    public String shouldNotReturnNullBecauseNotAnnotated() {
+        return null; // Error: Eradicate return not nullable
+    }
+
+    public void redundant() {
+        String s = new String("abc");
+        if (s != null) { // Error: Eradicate condition redundant
+            int n = s.length();
+        }
+    }
+
+    @Nullable
+    public static String someMethod() {
+        return ""; // Error: Eradicate return overannotated
+    }
+}
+
+class B extends Eradicate {
+    @Nullable public String shouldNotReturnNullBecauseNotAnnotated() {
+        return null; // Error: Eradicate inconsistent subclass return annotation
+    }
+
+    public void field(Eradicate x) {} // Error: Inconsistent subclass parameter annotation
+}
new file mode 100644
--- /dev/null
+++ b/tools/infer/test/autotest/src/main/java/Racerd.java
@@ -0,0 +1,40 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import javax.annotation.concurrent.ThreadSafe;
+
+// Examples taken from the infer website.
+@ThreadSafe
+public class Racerd {
+    private int mTemperature;
+
+    public void makeDinner() {
+        boilWater();
+    }
+
+    private void boilWater() {
+        mTemperature = 100; //Error: unprotected write.
+    }
+}
+
+@ThreadSafe
+class Account {
+
+    int mBalance = 0;
+
+    public void deposit(int amount) {
+        if (amount > 0) {