Merge autoland to mozilla-central r=merge a=merge
authorMargareta Eliza Balazs <ebalazs@mozilla.com>
Fri, 10 Nov 2017 11:51:18 +0200
changeset 444382 e9814434b1a95cc4633c5a41e0f0e5b772bf9135
parent 444319 80474174ca663daffc37537bc811be5d99bd01a0 (current diff)
parent 444381 5ee7f274f84b5b7f98573d7b4097f39af47f2783 (diff)
child 444407 864174ac0707207f7fc698ed6c3c47c043e99395
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central r=merge a=merge
browser/themes/shared/notification-icons/plugin-blocked.svg
browser/themes/shared/notification-icons/plugin.svg
build/sccache.mk
config/stl-headers
config/system-headers
dom/interfaces/html/nsIDOMHTMLOptionElement.idl
js/src/ds/FixedSizeHash.h
services/sync/tests/tps/mozmill_sanity.js
services/sync/tests/tps/mozmill_sanity2.js
services/sync/tests/tps/test_mozmill_sanity.js
services/sync/tps/extensions/mozmill/chrome.manifest
services/sync/tps/extensions/mozmill/install.rdf
services/sync/tps/extensions/mozmill/resource/driver/controller.js
services/sync/tps/extensions/mozmill/resource/driver/elementslib.js
services/sync/tps/extensions/mozmill/resource/driver/mozelement.js
services/sync/tps/extensions/mozmill/resource/driver/mozmill.js
services/sync/tps/extensions/mozmill/resource/driver/msgbroker.js
services/sync/tps/extensions/mozmill/resource/modules/assertions.js
services/sync/tps/extensions/mozmill/resource/modules/driver.js
services/sync/tps/extensions/mozmill/resource/modules/errors.js
services/sync/tps/extensions/mozmill/resource/modules/frame.js
services/sync/tps/extensions/mozmill/resource/modules/l10n.js
services/sync/tps/extensions/mozmill/resource/modules/stack.js
services/sync/tps/extensions/mozmill/resource/modules/windows.js
services/sync/tps/extensions/mozmill/resource/stdlib/EventUtils.js
services/sync/tps/extensions/mozmill/resource/stdlib/arrays.js
services/sync/tps/extensions/mozmill/resource/stdlib/dom.js
services/sync/tps/extensions/mozmill/resource/stdlib/httpd.js
services/sync/tps/extensions/mozmill/resource/stdlib/json2.js
services/sync/tps/extensions/mozmill/resource/stdlib/objects.js
services/sync/tps/extensions/mozmill/resource/stdlib/os.js
services/sync/tps/extensions/mozmill/resource/stdlib/securable-module.js
services/sync/tps/extensions/mozmill/resource/stdlib/strings.js
services/sync/tps/extensions/mozmill/resource/stdlib/utils.js
services/sync/tps/extensions/mozmill/resource/stdlib/withs.js
toolkit/content/Makefile.in
--- a/.eslintignore
+++ b/.eslintignore
@@ -329,17 +329,16 @@ testing/talos/talos/tests/kraken/**
 
 testing/web-platform/**
 testing/xpcshell/moz-http2/**
 testing/xpcshell/node-http2/**
 
 # Third party services
 services/common/kinto-http-client.js
 services/common/kinto-offline-client.js
-services/sync/tps/extensions/mozmill
 
 # toolkit/ exclusions
 
 # Not part of the default build
 toolkit/components/help/**
 
 # Intentionally invalid JS
 toolkit/components/workerloader/tests/moduleF-syntax-error.js
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -776,20 +776,16 @@ html|input.urlbar-input[textoverflow]:no
 #identity-icon-country-label {
   direction: ltr;
 }
 
 #identity-box.verifiedIdentity > #identity-icon-labels > #identity-icon-label {
   margin-inline-end: 0.25em !important;
 }
 
-#main-window[customizing] :-moz-any(#urlbar, .searchbar-textbox) > .urlbar-textbox-container > .textbox-input-box {
-  visibility: hidden;
-}
-
 /* Flexible spacer sizing (matching url bar) */
 toolbarpaletteitem[place=toolbar][id^=wrapper-customizableui-special-spring],
 toolbarspring {
   -moz-box-flex: 1;
   min-width: 28px;
   max-width: 112px;
 }
 
--- a/browser/components/preferences/handlers.xml
+++ b/browser/components/preferences/handlers.xml
@@ -77,22 +77,22 @@
         <xul:hbox flex="1" align="center">
           <xul:hbox xbl:inherits="data-identity-icon=containerIcon,data-identity-color=containerColor" height="24" width="24" class="userContext-icon"/>
           <xul:label flex="1" crop="end" xbl:inherits="xbl:text=containerName,highlightable"/>
         </xul:hbox>
         <xul:hbox flex="1" align="right">
           <xul:button anonid="preferencesButton"
                       label="&preferencesButton.label;"
                       xbl:inherits="value=userContextId"
-                      onclick="gContainersPane.onPreferenceClick(event.originalTarget)">
+                      oncommand="gContainersPane.onPreferenceCommand(event.originalTarget)">
           </xul:button>
           <xul:button anonid="removeButton"
                       label="&removeButton.label;"
                       xbl:inherits="value=userContextId"
-                      onclick="gContainersPane.onRemoveClick(event.originalTarget)">
+                      oncommand="gContainersPane.onRemoveCommand(event.originalTarget)">
           </xul:button>
         </xul:hbox>
       </xul:hbox>
     </content>
   </binding>
 
   <binding id="offlineapp"
 	   extends="chrome://global/content/bindings/listbox.xml#listitem">
--- a/browser/components/preferences/in-content/containers.js
+++ b/browser/components/preferences/in-content/containers.js
@@ -35,17 +35,17 @@ let gContainersPane = {
       item.setAttribute("containerIcon", container.icon);
       item.setAttribute("containerColor", container.color);
       item.setAttribute("userContextId", container.userContextId);
 
       this._list.appendChild(item);
     }
   },
 
-  async onRemoveClick(button) {
+  async onRemoveCommand(button) {
     let userContextId = parseInt(button.getAttribute("value"), 10);
 
     let count = ContextualIdentityService.countContainerTabs(userContextId);
     if (count > 0) {
       let bundlePreferences = document.getElementById("bundlePreferences");
 
       let title = bundlePreferences.getString("removeContainerAlertTitle");
       let message = PluralForm.get(count, bundlePreferences.getString("removeContainerMsg"))
@@ -64,21 +64,21 @@ let gContainersPane = {
 
       await ContextualIdentityService.closeContainerTabs(userContextId);
     }
 
     ContextualIdentityService.remove(userContextId);
     this._rebuildView();
   },
 
-  onPreferenceClick(button) {
+  onPreferenceCommand(button) {
     this.openPreferenceDialog(button.getAttribute("value"));
   },
 
-  onAddButtonClick(button) {
+  onAddButtonCommand(button) {
     this.openPreferenceDialog(null);
   },
 
   openPreferenceDialog(userContextId) {
     let identity = {
       name: "",
       icon: defaultContainerIcon,
       color: defaultContainerColor
--- a/browser/components/preferences/in-content/containers.xul
+++ b/browser/components/preferences/in-content/containers.xul
@@ -34,12 +34,12 @@
   <vbox id="browserContainersbox">
 
     <richlistbox id="containersView" orient="vertical" persist="lastSelectedType"
                  flex="1">
     </richlistbox>
   </vbox>
   <vbox>
     <hbox flex="1">
-      <button id="containersAdd" onclick="gContainersPane.onAddButtonClick();" accesskey="&addButton.accesskey;" label="&addButton.label;"/>
+      <button id="containersAdd" oncommand="gContainersPane.onAddButtonCommand();" accesskey="&addButton.accesskey;" label="&addButton.label;"/>
     </hbox>
   </vbox>
 </groupbox>
--- a/browser/components/preferences/in-content/tests/browser_containers_name_input.js
+++ b/browser/components/preferences/in-content/tests/browser_containers_name_input.js
@@ -9,17 +9,17 @@ add_task(async function setup() {
 
 add_task(async function() {
   async function openDialog() {
     let doc = gBrowser.selectedBrowser.contentDocument;
 
     let dialogPromise = promiseLoadSubDialog(CONTAINERS_URL);
 
     let addButton = doc.getElementById("containersAdd");
-    addButton.click();
+    addButton.doCommand();
 
     let dialog = await dialogPromise;
 
     return dialog.document;
   }
 
   let doc = await openDialog();
 
--- a/browser/components/search/test/browser_426329.js
+++ b/browser/components/search/test/browser_426329.js
@@ -244,21 +244,34 @@ add_task(async function testClearHistory
   let popupShownPromise = BrowserTestUtils.waitForEvent(textbox, "popupshown");
   EventUtils.synthesizeMouseAtCenter(textbox, { type: "contextmenu", button: 2 });
   await popupShownPromise;
   // Close the context menu.
   EventUtils.synthesizeKey("VK_ESCAPE", {});
 
   let controller = searchBar.textbox.controllers.getControllerForCommand("cmd_clearhistory");
   ok(controller.isCommandEnabled("cmd_clearhistory"), "Clear history command enabled");
+
+  let historyCleared = promiseObserver("satchel-storage-changed");
   controller.doCommand("cmd_clearhistory");
+  await historyCleared;
   let count = await countEntries();
   ok(count == 0, "History cleared");
 });
 
 add_task(async function asyncCleanup() {
   searchBar.value = "";
   while (gBrowser.tabs.length != 1) {
     gBrowser.removeTab(gBrowser.tabs[0], {animate: false});
   }
   gBrowser.selectedBrowser.loadURI("about:blank");
   await promiseRemoveEngine();
 });
+
+function promiseObserver(topic) {
+  return new Promise(resolve => {
+    let obs = (aSubject, aTopic, aData) => {
+      Services.obs.removeObserver(obs, aTopic);
+      resolve(aSubject);
+    };
+    Services.obs.addObserver(obs, topic);
+  });
+}
--- a/browser/themes/shared/urlbar-searchbar.inc.css
+++ b/browser/themes/shared/urlbar-searchbar.inc.css
@@ -64,16 +64,20 @@
   min-height: 32px;
 }
 
 :root[chromehidden~="toolbar"] #urlbar {
   /* Remove excess space between the address bar and the menu button in popups. */
   margin-inline-end: 0;
 }
 
+:root[customizing] .urlbar-input-box {
+  visibility: hidden;
+}
+
 #urlbar-container {
   -moz-box-align: center;
 }
 
 #urlbar-search-splitter {
   /* The splitter width should equal the location and search bars' combined
      neighboring margin and border width. */
   min-width: 12px;
--- a/build/mozconfig.cache
+++ b/build/mozconfig.cache
@@ -114,18 +114,17 @@ else
     mk_add_options "export SCCACHE_BUCKET=$bucket"
     case "$master" in
     *us[ew][12].mozilla.com*|*euc1.mozilla.com*)
         mk_add_options "export SCCACHE_NAMESERVER=169.254.169.253"
         ;;
     esac
     export CCACHE="$topsrcdir/sccache2/sccache${suffix}"
     export SCCACHE_VERBOSE_STATS=1
-    mk_add_options MOZ_PREFLIGHT_ALL+=build/sccache.mk
-    mk_add_options MOZ_POSTFLIGHT_ALL+=build/sccache.mk
+    mk_add_options MOZBUILD_MANAGE_SCCACHE_DAEMON=${topsrcdir}/sccache2/sccache
     mk_add_options "UPLOAD_EXTRA_FILES+=sccache.log.gz"
     case "$platform" in
     win*)
         # sccache supports a special flag to create depfiles.
         #TODO: bug 1318370 - move this all into toolchain.configure
         export _DEPEND_CFLAGS='-deps$(MDDEPDIR)/$(@F).pp'
         # Windows builds have a default wrapper that needs to be overridden
         mk_add_options "export CC_WRAPPER="
deleted file mode 100644
--- a/build/sccache.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-preflight_all:
-	# Terminate any sccache server that might still be around
-	-$(TOPSRCDIR)/sccache2/sccache --stop-server > /dev/null 2>&1
-	# Start a new server, ensuring it gets the jobserver file descriptors
-	# from make (but don't use the + prefix when make -n is used, so that
-	# the command doesn't run in that case)
-	$(if $(findstring n,$(filter-out --%, $(MAKEFLAGS))),,+)env RUST_LOG=sccache::compiler=debug SCCACHE_ERROR_LOG=$(OBJDIR)/dist/sccache.log $(TOPSRCDIR)/sccache2/sccache --start-server
-
-postflight_all:
-	# Terminate sccache server. This prints sccache stats.
-	-$(TOPSRCDIR)/sccache2/sccache --stop-server
--- a/client.mk
+++ b/client.mk
@@ -7,24 +7,20 @@
 # Defines main targets for driving the Firefox build system.
 #
 # This make file should not be invoked directly. Instead, use
 # `mach` (likely `mach build`) for invoking the build system.
 #
 # Options:
 #   MOZ_OBJDIR           - Destination object directory
 #   MOZ_MAKE_FLAGS       - Flags to pass to $(MAKE)
-#   MOZ_PREFLIGHT_ALL    - Makefiles to run before building.
-#   MOZ_POSTFLIGHT_ALL   - Makefiles to run after building.
 #
 #######################################################################
 # Defines
 
-comma := ,
-
 ifdef MACH
 ifndef NO_BUILDSTATUS_MESSAGES
 define BUILDSTATUS
 @echo 'BUILDSTATUS $1'
 
 endef
 endif
 endif
@@ -39,18 +35,16 @@ endif
 ifndef TOPSRCDIR
 ifeq (,$(wildcard client.mk))
 TOPSRCDIR := $(patsubst %/,%,$(dir $(MAKEFILE_LIST)))
 else
 TOPSRCDIR := $(CWD)
 endif
 endif
 
-SH := /bin/sh
-PERL ?= perl
 PYTHON ?= $(shell which python2.7 > /dev/null 2>&1 && echo python2.7 || echo python)
 
 CONFIG_GUESS := $(shell $(TOPSRCDIR)/build/autoconf/config.guess)
 
 ####################################
 # Sanity checks
 
 # Windows checks.
@@ -148,25 +142,26 @@ all build clean distclean export libs in
 ifneq (,$(strip $(MOZCONFIG_OUT_FILTERED)))
 	$(info Adding client.mk options from $(FOUND_MOZCONFIG):)
 	$(foreach line,$(MOZCONFIG_OUT_FILTERED),$(info $(NULL) $(NULL) $(NULL) $(NULL) $(subst ||, ,$(line))))
 endif
 
 # helper target for mobile
 build_and_deploy: build package install
 
-#####################################################
-# Preflight, before building any project
-
-ifdef MOZ_PREFLIGHT_ALL
-build preflight_all::
-	set -e; \
-	for mkfile in $(MOZ_PREFLIGHT_ALL); do \
-	  $(MAKE) -f $(TOPSRCDIR)/$$mkfile preflight_all TOPSRCDIR=$(TOPSRCDIR) OBJDIR=$(OBJDIR) MOZ_OBJDIR=$(MOZ_OBJDIR); \
-	done
+# In automation, manage an sccache daemon. The starting of the server
+# needs to be in a make file so sccache inherits the jobserver.
+ifdef MOZBUILD_MANAGE_SCCACHE_DAEMON
+build::
+	# Terminate any sccache server that might still be around.
+	-$(MOZBUILD_MANAGE_SCCACHE_DAEMON) --stop-server > /dev/null 2>&1
+	# Start a new server, ensuring it gets the jobserver file descriptors
+	# from make (but don't use the + prefix when make -n is used, so that
+	# the command doesn't run in that case)
+	$(if $(findstring n,$(filter-out --%, $(MAKEFLAGS))),,+)env RUST_LOG=sccache::compiler=debug SCCACHE_ERROR_LOG=$(OBJDIR)/dist/sccache.log $(MOZBUILD_MANAGE_SCCACHE_DAEMON) --start-server
 endif
 
 ####################################
 # Configure
 
 MAKEFILE      = $(wildcard $(OBJDIR)/Makefile)
 CONFIG_STATUS = $(wildcard $(OBJDIR)/config.status)
 
@@ -275,38 +270,28 @@ build::  $(OBJDIR)/Makefile $(OBJDIR)/co
 
 ####################################
 # Other targets
 
 # Pass these target onto the real build system
 $(OBJDIR_TARGETS):: $(OBJDIR)/Makefile $(OBJDIR)/config.status
 	+$(MOZ_MAKE) $@
 
-####################################
-# Postflight, after building all projects
-
 ifdef MOZ_AUTOMATION
 build::
 	$(MAKE) -f $(TOPSRCDIR)/client.mk automation/build
 endif
 
-ifdef MOZ_POSTFLIGHT_ALL
-build postflight_all::
-	set -e; \
-	for mkfile in $(MOZ_POSTFLIGHT_ALL); do \
-	  $(MAKE) -f $(TOPSRCDIR)/$$mkfile postflight_all TOPSRCDIR=$(TOPSRCDIR) OBJDIR=$(OBJDIR) MOZ_OBJDIR=$(MOZ_OBJDIR); \
-	done
+ifdef MOZBUILD_MANAGE_SCCACHE_DAEMON
+build::
+	# Terminate sccache server. This prints sccache stats.
+	-$(MOZBUILD_MANAGE_SCCACHE_DAEMON) --stop-server
 endif
 
-echo-variable-%:
-	@echo $($*)
-
 # This makefile doesn't support parallel execution. It does pass
 # MOZ_MAKE_FLAGS to sub-make processes, so they will correctly execute
 # in parallel.
 .NOTPARALLEL:
 
 .PHONY: \
     build \
     configure \
-    preflight_all \
-    postflight_all \
     $(OBJDIR_TARGETS)
--- a/config/faster/rules.mk
+++ b/config/faster/rules.mk
@@ -54,18 +54,17 @@ ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 default:
 	$(MAKE) -C $(TOPOBJDIR)/$(MOZ_BUILD_APP)/app repackage
 endif
 endif
 
 .PHONY: FORCE
 
 # Extra define to trigger some workarounds. We should strive to limit the
-# use of those. As of writing the only ones are in
-# toolkit/content/buildconfig.html and browser/locales/jar.mn.
+# use of those. As of writing the only one is in browser/locales/jar.mn.
 ACDEFINES += -DBUILD_FASTER
 
 # Files under the faster/ sub-directory, however, are not meant to use the
 # fallback
 $(TOPOBJDIR)/faster/%: ;
 
 # Generic rule to fall back to the recursive make backend.
 # This needs to stay after other $(TOPOBJDIR)/* rules because GNU Make
--- a/devtools/client/jsonview/test/browser_jsonview_row_selection.js
+++ b/devtools/client/jsonview/test/browser_jsonview_row_selection.js
@@ -1,44 +1,68 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-add_task(function* () {
-  info("Test JSON row selection started");
+add_task(async function () {
+  info("Test 1 JSON row selection started");
 
   // Create a tall JSON so that there is a scrollbar.
   let numRows = 1e3;
   let json = JSON.stringify(Array(numRows).fill().map((_, i) => i));
-  let tab = yield addJsonViewTab("data:application/json," + json);
+  let tab = await addJsonViewTab("data:application/json," + json);
 
-  is(yield getElementCount(".treeRow"), numRows, "Got the expected number of rows.");
-  yield assertRowSelected(null);
-  yield evalInContent("var scroller = $('.jsonPanelBox .panelContent')");
-  ok(yield evalInContent("scroller.clientHeight < scroller.scrollHeight"),
+  is(await getElementCount(".treeRow"), numRows, "Got the expected number of rows.");
+  await assertRowSelected(null);
+  await evalInContent("var scroller = $('.jsonPanelBox .panelContent')");
+  ok(await evalInContent("scroller.clientHeight < scroller.scrollHeight"),
      "There is a scrollbar.");
-  is(yield evalInContent("scroller.scrollTop"), 0, "Initially scrolled to the top.");
+  is(await evalInContent("scroller.scrollTop"), 0, "Initially scrolled to the top.");
 
   // Click to select last row.
-  yield evalInContent("$('.treeRow:last-child').click()");
-  yield assertRowSelected(numRows);
-  is(yield evalInContent("scroller.scrollTop + scroller.clientHeight"),
-     yield evalInContent("scroller.scrollHeight"), "Scrolled to the bottom.");
+  await evalInContent("$('.treeRow:last-child').click()");
+  await assertRowSelected(numRows);
+  is(await evalInContent("scroller.scrollTop + scroller.clientHeight"),
+     await evalInContent("scroller.scrollHeight"), "Scrolled to the bottom.");
 
   // Click to select 2nd row.
-  yield evalInContent("$('.treeRow:nth-child(2)').click()");
-  yield assertRowSelected(2);
-  ok(yield evalInContent("scroller.scrollTop > 0"), "Not scrolled to the top.");
+  await evalInContent("$('.treeRow:nth-child(2)').click()");
+  await assertRowSelected(2);
+  ok(await evalInContent("scroller.scrollTop > 0"), "Not scrolled to the top.");
 
   // Synthetize up arrow key to select first row.
-  yield evalInContent("$('.treeTable').focus()");
-  yield BrowserTestUtils.synthesizeKey("VK_UP", {}, tab.linkedBrowser);
-  yield assertRowSelected(1);
-  is(yield evalInContent("scroller.scrollTop"), 0, "Scrolled to the top.");
+  await evalInContent("$('.treeTable').focus()");
+  await BrowserTestUtils.synthesizeKey("VK_UP", {}, tab.linkedBrowser);
+  await assertRowSelected(1);
+  is(await evalInContent("scroller.scrollTop"), 0, "Scrolled to the top.");
+});
+
+add_task(async function () {
+  info("Test 2 JSON row selection started");
+
+  let numRows = 4;
+  let tab = await addJsonViewTab("data:application/json,[0,1,2,3]");
+
+  is(await getElementCount(".treeRow"), numRows, "Got the expected number of rows.");
+  await assertRowSelected(null);
+
+  // Click to select first row.
+  await clickJsonNode(".treeRow:first-child");
+  await assertRowSelected(1);
+
+  // Synthetize multiple down arrow keydowns to select following rows.
+  for (let i = 2; i < numRows; ++i) {
+    await BrowserTestUtils.synthesizeKey("VK_DOWN", {type: "keydown"}, tab.linkedBrowser);
+    await assertRowSelected(i);
+  }
+
+  // Now synthetize the keyup, this shouldn't change selected row.
+  await BrowserTestUtils.synthesizeKey("VK_DOWN", {type: "keyup"}, tab.linkedBrowser);
+  await assertRowSelected(numRows - 1);
 });
 
 async function assertRowSelected(rowNum) {
   let idx = evalInContent("[].indexOf.call($$('.treeRow'), $('.treeRow.selected'))");
   is(await idx + 1, +rowNum, `${rowNum ? "The row #" + rowNum : "No row"} is selected.`);
 }
--- a/devtools/client/jsonview/test/head.js
+++ b/devtools/client/jsonview/test/head.js
@@ -27,64 +27,57 @@ registerCleanupFunction(() => {
  * @param {String} url
  *   The url to be loaded in the new tab.
  * @param {Number} timeout [optional]
  *   The maximum number of milliseconds allowed before the initialization of the
  *   JSON Viewer once the tab has been loaded. If exceeded, the initialization
  *   will be considered to have failed, and the returned promise will be rejected.
  *   If this parameter is not passed or is negative, it will be ignored.
  */
-function addJsonViewTab(url, timeout = -1) {
+async function addJsonViewTab(url, timeout = -1) {
   info("Adding a new JSON tab with URL: '" + url + "'");
 
-  let deferred = defer();
-  addTab(url).then(tab => {
-    let browser = tab.linkedBrowser;
+  let tab = await addTab(url);
+  let browser = tab.linkedBrowser;
 
-    // Load devtools/shared/frame-script-utils.js
-    getFrameScript();
+  // Load devtools/shared/frame-script-utils.js
+  getFrameScript();
 
-    // Load frame script with helpers for JSON View tests.
-    let rootDir = getRootDirectory(gTestPath);
-    let frameScriptUrl = rootDir + "doc_frame_script.js";
-    browser.messageManager.loadFrameScript(frameScriptUrl, false);
+  // Load frame script with helpers for JSON View tests.
+  let rootDir = getRootDirectory(gTestPath);
+  let frameScriptUrl = rootDir + "doc_frame_script.js";
+  browser.messageManager.loadFrameScript(frameScriptUrl, false);
 
-    // Check if there is a JSONView object.
-    if (!content.window.wrappedJSObject.JSONView) {
-      deferred.reject("JSON Viewer did not load.");
-      return;
-    }
+  // Check if there is a JSONView object.
+  if (!content.window.wrappedJSObject.JSONView) {
+    throw new Error("JSON Viewer did not load.");
+  }
 
-    // Resolve if the JSONView is fully loaded or wait
-    // for an initialization event.
-    if (content.window.wrappedJSObject.JSONView.initialized) {
-      deferred.resolve(tab);
-    } else {
-      waitForContentMessage("Test:JsonView:JSONViewInitialized").then(() => {
-        deferred.resolve(tab);
-      });
-    }
+  // Resolve if the JSONView is fully loaded.
+  if (content.window.wrappedJSObject.JSONView.initialized) {
+    return tab;
+  }
+
+  // Otherwise wait for an initialization event, possibly with a time limit.
+  const onJSONViewInitialized =
+    waitForContentMessage("Test:JsonView:JSONViewInitialized")
+    .then(() => tab);
 
-    // Add a timeout.
-    if (timeout >= 0) {
-      new Promise(resolve => {
-        if (content.window.document.readyState === "complete") {
-          resolve();
-        } else {
-          waitForContentMessage("Test:JsonView:load").then(resolve);
-        }
-      }).then(() => {
-        setTimeout(() => {
-          deferred.reject("JSON Viewer did not load.");
-        }, timeout);
-      });
-    }
-  });
+  if (!(timeout >= 0)) {
+    return onJSONViewInitialized;
+  }
 
-  return deferred.promise;
+  if (content.window.document.readyState !== "complete") {
+    await waitForContentMessage("Test:JsonView:load");
+  }
+
+  let onTimeout = new Promise((_, reject) =>
+    setTimeout(() => reject(new Error("JSON Viewer did not load.")), timeout));
+
+  return Promise.race([onJSONViewInitialized, onTimeout]);
 }
 
 /**
  * Expanding a node in the JSON tree
  */
 function clickJsonNode(selector) {
   info("Expanding node: '" + selector + "'");
 
@@ -160,19 +153,17 @@ function sendString(str, selector) {
     selector: selector,
     str: str
   };
 
   return executeInContent("Test:JsonView:SendString", data);
 }
 
 function waitForTime(delay) {
-  let deferred = defer();
-  setTimeout(deferred.resolve, delay);
-  return deferred.promise;
+  return new Promise(resolve => setTimeout(resolve, delay));
 }
 
 function waitForFilter() {
   return executeInContent("Test:JsonView:WaitForFilter");
 }
 
 function normalizeNewLines(value) {
   return value.replace("(\r\n|\n)", "\n");
--- a/devtools/client/shared/components/tree/TreeView.js
+++ b/devtools/client/shared/components/tree/TreeView.js
@@ -125,17 +125,16 @@ define(function (require, exports, modul
         expandedNodes: props.expandedNodes,
         columns: ensureDefaultColumn(props.columns),
         selected: null
       };
 
       this.toggle = this.toggle.bind(this);
       this.isExpanded = this.isExpanded.bind(this);
       this.onKeyDown = this.onKeyDown.bind(this);
-      this.onKeyUp = this.onKeyUp.bind(this);
       this.onClickRow = this.onClickRow.bind(this);
       this.getSelectedRow = this.getSelectedRow.bind(this);
       this.selectRow = this.selectRow.bind(this);
       this.isSelected = this.isSelected.bind(this);
       this.onFilter = this.onFilter.bind(this);
       this.onSort = this.onSort.bind(this);
       this.getMembers = this.getMembers.bind(this);
       this.renderRows = this.renderRows.bind(this);
@@ -223,23 +222,20 @@ define(function (require, exports, modul
 
     isExpanded(nodePath) {
       return this.state.expandedNodes.has(nodePath);
     }
 
     // Event Handlers
 
     onKeyDown(event) {
-      if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(
-        event.key)) {
-        event.preventDefault();
+      if (!["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(event.key)) {
+        return;
       }
-    }
 
-    onKeyUp(event) {
       let row = this.getSelectedRow(this.rows);
       if (!row) {
         return;
       }
 
       let index = this.rows.indexOf(row);
       switch (event.key) {
         case "ArrowRight":
@@ -260,18 +256,16 @@ define(function (require, exports, modul
           }
           break;
         case "ArrowUp":
           let previousRow = this.rows[index - 1];
           if (previousRow) {
             this.selectRow(previousRow);
           }
           break;
-        default:
-          return;
       }
 
       event.preventDefault();
     }
 
     onClickRow(nodePath, event) {
       event.stopPropagation();
       let cell = event.target.closest("td");
@@ -466,17 +460,16 @@ define(function (require, exports, modul
       });
 
       return (
         dom.table({
           className: classNames.join(" "),
           role: "tree",
           tabIndex: 0,
           onKeyDown: this.onKeyDown,
-          onKeyUp: this.onKeyUp,
           "aria-label": this.props.label || "",
           "aria-activedescendant": this.state.selected,
           cellPadding: 0,
           cellSpacing: 0},
           TreeHeader(props),
           dom.tbody({
             role: "presentation"
           }, rows)
--- a/devtools/shim/devtools-startup.js
+++ b/devtools/shim/devtools-startup.js
@@ -424,30 +424,42 @@ DevToolsStartup.prototype = {
       let window = enumerator.getNext();
       if (window.gBrowserInit && window.gBrowserInit.delayedStartupFinished) {
         this.updateDevToolsMenuItems(window);
       }
     }
   },
 
   /**
+   * Check if the user is a DevTools user by looking at our selfxss pref.
+   * This preference is incremented everytime the console is used (up to 5).
+   *
+   * @return {Boolean} true if the user can be considered as a devtools user.
+   */
+  isDevToolsUser() {
+    let selfXssCount = Services.prefs.getIntPref("devtools.selfxss.count", 0);
+    return selfXssCount > 0;
+  },
+
+  /**
    * Depending on some runtime parameters (command line arguments as well as existing
    * preferences), the DEVTOOLS_ENABLED_PREF might be forced to true.
    *
    * @param {Boolean} hasDevToolsFlag
    *        true if any DevTools command line argument was passed when starting Firefox.
    */
   setupEnabledPref(hasDevToolsFlag) {
     if (Services.prefs.getBoolPref(DEVTOOLS_ENABLED_PREF)) {
       // Nothing to do if DevTools are already enabled.
       return;
     }
 
     let hasToolbarPref = Services.prefs.getBoolPref(TOOLBAR_VISIBLE_PREF, false);
-    if (hasDevToolsFlag || hasToolbarPref) {
+
+    if (hasDevToolsFlag || hasToolbarPref || this.isDevToolsUser()) {
       Services.prefs.setBoolPref(DEVTOOLS_ENABLED_PREF, true);
     }
   },
 
   hookKeyShortcuts(window) {
     let doc = window.document;
     let keyset = doc.createElement("keyset");
     keyset.setAttribute("id", "devtoolsKeyset");
--- a/dom/base/RangeBoundary.h
+++ b/dom/base/RangeBoundary.h
@@ -82,20 +82,21 @@ protected:
   RangeBoundaryBase(nsINode* aContainer, nsIContent* aRef, int32_t aOffset)
     : mParent(aContainer)
     , mRef(aRef)
     , mOffset(mozilla::Some(aOffset))
   {
     MOZ_RELEASE_ASSERT(aContainer,
       "This constructor shouldn't be used when pointing nowhere");
     if (!mRef) {
-      MOZ_ASSERT(mOffset.value() == 0);
+      MOZ_ASSERT(!mParent->IsContainerNode() || mOffset.value() == 0);
       return;
     }
     MOZ_ASSERT(mOffset.value() > 0);
+    MOZ_ASSERT(mParent == mRef->GetParentNode());
     MOZ_ASSERT(mParent->GetChildAt(mOffset.value() - 1) == mRef);
   }
 
 public:
   RangeBoundaryBase()
     : mParent(nullptr)
     , mRef(nullptr)
   {
--- a/dom/html/HTMLOptionElement.cpp
+++ b/dom/html/HTMLOptionElement.cpp
@@ -44,29 +44,20 @@ HTMLOptionElement::HTMLOptionElement(alr
   // We start off enabled
   AddStatesSilently(NS_EVENT_STATE_ENABLED);
 }
 
 HTMLOptionElement::~HTMLOptionElement()
 {
 }
 
-NS_IMPL_ISUPPORTS_INHERITED(HTMLOptionElement, nsGenericHTMLElement,
-                            nsIDOMHTMLOptionElement)
+NS_IMPL_ISUPPORTS_INHERITED0(HTMLOptionElement, nsGenericHTMLElement)
 
 NS_IMPL_ELEMENT_CLONE(HTMLOptionElement)
 
-
-NS_IMETHODIMP
-HTMLOptionElement::GetForm(nsIDOMHTMLFormElement** aForm)
-{
-  NS_IF_ADDREF(*aForm = GetForm());
-  return NS_OK;
-}
-
 mozilla::dom::HTMLFormElement*
 HTMLOptionElement::GetForm()
 {
   HTMLSelectElement* selectControl = GetSelect();
   return selectControl ? selectControl->GetForm() : nullptr;
 }
 
 void
@@ -110,25 +101,17 @@ HTMLOptionElement::UpdateDisabledState(b
   EventStates oldDisabledStates = State() & DISABLED_STATES;
   EventStates changedStates = disabledStates ^ oldDisabledStates;
 
   if (!changedStates.IsEmpty()) {
     ToggleStates(changedStates, aNotify);
   }
 }
 
-NS_IMETHODIMP
-HTMLOptionElement::GetSelected(bool* aValue)
-{
-  NS_ENSURE_ARG_POINTER(aValue);
-  *aValue = Selected();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
+void
 HTMLOptionElement::SetSelected(bool aValue)
 {
   // Note: The select content obj maintains all the PresState
   // so defer to it to get the answer
   HTMLSelectElement* selectInt = GetSelect();
   if (selectInt) {
     int32_t index = Index();
     uint32_t mask = HTMLSelectElement::SET_DISABLED | HTMLSelectElement::NOTIFY;
@@ -136,31 +119,16 @@ HTMLOptionElement::SetSelected(bool aVal
       mask |= HTMLSelectElement::IS_SELECTED;
     }
 
     // This should end up calling SetSelectedInternal
     selectInt->SetOptionsSelectedByIndex(index, index, mask);
   } else {
     SetSelectedInternal(aValue, true);
   }
-
-  return NS_OK;
-}
-
-NS_IMPL_BOOL_ATTR(HTMLOptionElement, DefaultSelected, selected)
-// GetText returns a whitespace compressed .textContent value.
-NS_IMPL_STRING_ATTR_WITH_FALLBACK(HTMLOptionElement, Label, label, GetText)
-NS_IMPL_STRING_ATTR_WITH_FALLBACK(HTMLOptionElement, Value, value, GetText)
-NS_IMPL_BOOL_ATTR(HTMLOptionElement, Disabled, disabled)
-
-NS_IMETHODIMP
-HTMLOptionElement::GetIndex(int32_t* aIndex)
-{
-  *aIndex = Index();
-  return NS_OK;
 }
 
 int32_t
 HTMLOptionElement::Index()
 {
   static int32_t defaultIndex = 0;
 
   // Only select elements can contain a list of options.
@@ -174,28 +142,16 @@ HTMLOptionElement::Index()
     return defaultIndex;
   }
 
   int32_t index = defaultIndex;
   MOZ_ALWAYS_SUCCEEDS(options->GetOptionIndex(this, 0, true, &index));
   return index;
 }
 
-bool
-HTMLOptionElement::Selected() const
-{
-  return mIsSelected;
-}
-
-bool
-HTMLOptionElement::DefaultSelected() const
-{
-  return HasAttr(kNameSpaceID_None, nsGkAtoms::selected);
-}
-
 nsChangeHint
 HTMLOptionElement::GetAttributeChangeHint(const nsAtom* aAttribute,
                                           int32_t aModType) const
 {
   nsChangeHint retval =
       nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType);
 
   if (aAttribute == nsGkAtoms::label ||
@@ -283,17 +239,17 @@ HTMLOptionElement::AfterSetAttr(int32_t 
       }
     }
   }
 
   return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName,
                                             aValue, aOldValue, aSubjectPrincipal, aNotify);
 }
 
-NS_IMETHODIMP
+void
 HTMLOptionElement::GetText(nsAString& aText)
 {
   nsAutoString text;
 
   nsIContent* child = nsINode::GetFirstChild();
   while (child) {
     if (child->NodeType() == nsIDOMNode::TEXT_NODE ||
         child->NodeType() == nsIDOMNode::CDATA_SECTION_NODE) {
@@ -305,24 +261,22 @@ HTMLOptionElement::GetText(nsAString& aT
     } else {
       child = child->GetNextNode(this);
     }
   }
 
   // XXX No CompressWhitespace for nsAString.  Sad.
   text.CompressWhitespace(true, true);
   aText = text;
-
-  return NS_OK;
 }
 
-NS_IMETHODIMP
-HTMLOptionElement::SetText(const nsAString& aText)
+void
+HTMLOptionElement::SetText(const nsAString& aText, ErrorResult& aRv)
 {
-  return nsContentUtils::SetNodeTextContent(this, aText, true);
+  aRv = nsContentUtils::SetNodeTextContent(this, aText, true);
 }
 
 nsresult
 HTMLOptionElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers)
 {
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
@@ -430,21 +384,17 @@ HTMLOptionElement::Option(const GlobalOb
     // aNotify == false.
     aError = option->SetAttr(kNameSpaceID_None, nsGkAtoms::selected,
                              EmptyString(), false);
     if (aError.Failed()) {
       return nullptr;
     }
   }
 
-  option->SetSelected(aSelected, aError);
-  if (aError.Failed()) {
-    return nullptr;
-  }
-
+  option->SetSelected(aSelected);
   option->SetSelectedChanged(false);
 
   return option.forget();
 }
 
 nsresult
 HTMLOptionElement::CopyInnerTo(Element* aDest, bool aPreallocateChildren)
 {
--- a/dom/html/HTMLOptionElement.h
+++ b/dom/html/HTMLOptionElement.h
@@ -4,26 +4,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/. */
 
 #ifndef mozilla_dom_HTMLOptionElement_h__
 #define mozilla_dom_HTMLOptionElement_h__
 
 #include "mozilla/Attributes.h"
 #include "nsGenericHTMLElement.h"
-#include "nsIDOMHTMLOptionElement.h"
 #include "mozilla/dom/HTMLFormElement.h"
 
 namespace mozilla {
 namespace dom {
 
 class HTMLSelectElement;
 
-class HTMLOptionElement final : public nsGenericHTMLElement,
-                                public nsIDOMHTMLOptionElement
+class HTMLOptionElement final : public nsGenericHTMLElement
 {
 public:
   explicit HTMLOptionElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
   static already_AddRefed<HTMLOptionElement>
     Option(const GlobalObject& aGlobal,
            const nsAString& aText,
            const Optional<nsAString>& aValue,
@@ -31,23 +29,25 @@ public:
            bool aSelected,
            ErrorResult& aError);
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLOptionElement, option)
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
-  // nsIDOMHTMLOptionElement
   using mozilla::dom::Element::SetText;
   using mozilla::dom::Element::GetText;
-  NS_DECL_NSIDOMHTMLOPTIONELEMENT
 
-  bool Selected() const;
-  bool DefaultSelected() const;
+  bool Selected() const
+  {
+    return mIsSelected;
+  }
+  void SetSelected(bool aValue);
+
 
   void SetSelectedChanged(bool aValue)
   {
     mSelectedChanged = aValue;
   }
 
   virtual nsChangeHint GetAttributeChangeHint(const nsAtom* aAttribute,
                                               int32_t aModType) const override;
@@ -101,45 +101,49 @@ public:
 
   void SetDisabled(bool aValue, ErrorResult& aRv)
   {
     SetHTMLBoolAttr(nsGkAtoms::disabled, aValue, aRv);
   }
 
   HTMLFormElement* GetForm();
 
-  // The XPCOM GetLabel is OK for us
+  void GetLabel(DOMString& aLabel)
+  {
+    if (!GetAttr(kNameSpaceID_None, nsGkAtoms::label, aLabel)) {
+      GetText(aLabel);
+    }
+  }
   void SetLabel(const nsAString& aLabel, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::label, aLabel, aError);
   }
 
-  // The XPCOM DefaultSelected is OK for us
+  bool DefaultSelected() const
+  {
+    return HasAttr(kNameSpaceID_None, nsGkAtoms::selected);
+  }
   void SetDefaultSelected(bool aValue, ErrorResult& aRv)
   {
     SetHTMLBoolAttr(nsGkAtoms::selected, aValue, aRv);
   }
 
-  // The XPCOM Selected is OK for us
-  void SetSelected(bool aValue, ErrorResult& aRv)
+  void GetValue(nsAString& aValue)
   {
-    aRv = SetSelected(aValue);
+    if (!GetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue)) {
+      GetText(aValue);
+    }
   }
-
-  // The XPCOM GetValue is OK for us
   void SetValue(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::value, aValue, aRv);
   }
 
-  // The XPCOM GetText is OK for us
-  void SetText(const nsAString& aValue, ErrorResult& aRv)
-  {
-    aRv = SetText(aValue);
-  }
+  void GetText(nsAString& aText);
+  void SetText(const nsAString& aText, ErrorResult& aRv);
 
   int32_t Index();
 
 protected:
   virtual ~HTMLOptionElement();
 
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
--- a/dom/html/HTMLOptionsCollection.h
+++ b/dom/html/HTMLOptionsCollection.h
@@ -12,18 +12,16 @@
 
 #include "mozilla/dom/HTMLOptionElement.h"
 #include "mozilla/ErrorResult.h"
 #include "nsCOMPtr.h"
 #include "nsError.h"
 #include "nsGenericHTMLElement.h"
 #include "nsTArray.h"
 
-class nsIDOMHTMLOptionElement;
-
 namespace mozilla {
 namespace dom {
 
 class HTMLElementOrLong;
 class HTMLOptionElementOrHTMLOptGroupElement;
 class HTMLSelectElement;
 
 /**
--- a/dom/html/HTMLSelectElement.cpp
+++ b/dom/html/HTMLSelectElement.cpp
@@ -508,17 +508,17 @@ HTMLSelectElement::GetOptionIndexAfter(n
 }
 
 int32_t
 HTMLSelectElement::GetFirstOptionIndex(nsIContent* aOptions)
 {
   int32_t listIndex = -1;
   HTMLOptionElement* optElement = HTMLOptionElement::FromContent(aOptions);
   if (optElement) {
-    GetOptionIndex(optElement, 0, true, &listIndex);
+    mOptions->GetOptionIndex(optElement->AsElement(), 0, true, &listIndex);
     return listIndex;
   }
 
   listIndex = GetFirstChildOptionIndex(aOptions, 0, aOptions->GetChildCount());
 
   return listIndex;
 }
 
@@ -706,25 +706,16 @@ HTMLSelectElement::SetSelectedIndexInter
     rv = selectFrame->OnSetSelectedIndex(oldSelectedIndex, mSelectedIndex);
   }
 
   SetSelectionChanged(true, aNotify);
 
   return rv;
 }
 
-NS_IMETHODIMP
-HTMLSelectElement::GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
-                                  int32_t aStartIndex, bool aForward,
-                                  int32_t* aIndex)
-{
-  nsCOMPtr<nsINode> option = do_QueryInterface(aOption);
-  return mOptions->GetOptionIndex(option->AsElement(), aStartIndex, aForward, aIndex);
-}
-
 bool
 HTMLSelectElement::IsOptionSelectedByIndex(int32_t aIndex)
 {
   HTMLOptionElement* option = Item(static_cast<uint32_t>(aIndex));
   return option && option->Selected();
 }
 
 void
@@ -1027,18 +1018,17 @@ HTMLSelectElement::GetValue(DOMString& a
 
   RefPtr<HTMLOptionElement> option =
     Item(static_cast<uint32_t>(selectedIndex));
 
   if (!option) {
     return;
   }
 
-  DebugOnly<nsresult> rv = option->GetValue(aValue);
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  option->GetValue(aValue);
 }
 
 void
 HTMLSelectElement::SetValue(const nsAString& aValue)
 {
   uint32_t length = Length();
 
   for (uint32_t i = 0; i < length; i++) {
@@ -1474,18 +1464,18 @@ HTMLSelectElement::RestoreStateTo(Select
   // First clear all
   SetOptionsSelectedByIndex(-1, -1, mask);
 
   // Next set the proper ones
   for (uint32_t i = 0; i < len; i++) {
     HTMLOptionElement* option = Item(i);
     if (option) {
       nsAutoString value;
-      nsresult rv = option->GetValue(value);
-      if (NS_SUCCEEDED(rv) && aNewSelected->ContainsOption(i, value)) {
+      option->GetValue(value);
+      if (aNewSelected->ContainsOption(i, value)) {
         SetOptionsSelectedByIndex(i, i, IS_SELECTED | SET_DISABLED | NOTIFY);
       }
     }
   }
 }
 
 NS_IMETHODIMP
 HTMLSelectElement::Reset()
@@ -1574,17 +1564,17 @@ HTMLSelectElement::SubmitNamesValues(HTM
       continue;
     }
 
     if (!option->Selected()) {
       continue;
     }
 
     nsString value;
-    MOZ_ALWAYS_SUCCEEDS(option->GetValue(value));
+    option->GetValue(value);
 
     if (keyGenProcessor) {
       nsString tmp(value);
       if (NS_SUCCEEDED(keyGenProcessor->ProcessValue(this, name, tmp))) {
         value = tmp;
       }
     }
 
@@ -1654,17 +1644,17 @@ HTMLSelectElement::IsValueMissing() cons
 
   uint32_t length = Length();
 
   for (uint32_t i = 0; i < length; ++i) {
     RefPtr<HTMLOptionElement> option = Item(i);
     // Check for a placeholder label option, don't count it as a valid value.
     if (i == 0 && !Multiple() && Size() <= 1 && option->GetParent() == this) {
       nsAutoString value;
-      MOZ_ALWAYS_SUCCEEDS(option->GetValue(value));
+      option->GetValue(value);
       if (value.IsEmpty()) {
         continue;
       }
     }
 
     if (!option->Selected()) {
       continue;
     }
--- a/dom/html/HTMLSelectElement.h
+++ b/dom/html/HTMLSelectElement.h
@@ -356,29 +356,16 @@ public:
    *        options and whether frames are to be notified of such.
    * @return whether any options were actually changed
    */
   bool SetOptionsSelectedByIndex(int32_t aStartIndex,
                                  int32_t aEndIndex,
                                  uint32_t aOptionsMask);
 
   /**
-   * Finds the index of a given option element
-   *
-   * @param aOption the option to get the index of
-   * @param aStartIndex the index to start looking at
-   * @param aForward TRUE to look forward, FALSE to look backward
-   * @return the option index
-   */
-  NS_IMETHOD GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
-                            int32_t aStartIndex,
-                            bool aForward,
-                            int32_t* aIndex);
-
-  /**
    * Called when an attribute is about to be changed
    */
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                                nsIContent* aBindingParent,
                                bool aCompileEventHandlers) override;
   virtual void UnbindFromTree(bool aDeep, bool aNullParent) override;
   virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsAtom* aName,
                                  const nsAttrValueOrString* aValue,
--- a/dom/interfaces/html/moz.build
+++ b/dom/interfaces/html/moz.build
@@ -11,17 +11,16 @@ XPIDL_SOURCES += [
     'nsIDOMHTMLBaseElement.idl',
     'nsIDOMHTMLCollection.idl',
     'nsIDOMHTMLDocument.idl',
     'nsIDOMHTMLElement.idl',
     'nsIDOMHTMLFormElement.idl',
     'nsIDOMHTMLHtmlElement.idl',
     'nsIDOMHTMLInputElement.idl',
     'nsIDOMHTMLMediaElement.idl',
-    'nsIDOMHTMLOptionElement.idl',
     'nsIDOMHTMLScriptElement.idl',
     'nsIDOMMozBrowserFrame.idl',
     'nsIDOMTimeRanges.idl',
     'nsIDOMValidityState.idl',
     'nsIMozBrowserFrame.idl',
 ]
 
 XPIDL_MODULE = 'dom_html'
deleted file mode 100644
--- a/dom/interfaces/html/nsIDOMHTMLOptionElement.idl
+++ /dev/null
@@ -1,31 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* 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 "nsIDOMHTMLElement.idl"
-
-/**
- * The nsIDOMHTMLOptionElement interface is the interface to a [X]HTML
- * option element.
- *
- * This interface is trying to follow the DOM Level 2 HTML specification:
- * http://www.w3.org/TR/DOM-Level-2-HTML/
- *
- * with changes from the work-in-progress WHATWG HTML specification:
- * http://www.whatwg.org/specs/web-apps/current-work/
- */
-
-[uuid(c2b3e9ff-6b36-4158-ace3-05a9c5b8e1c1)]
-interface nsIDOMHTMLOptionElement : nsISupports
-{
-           attribute boolean               disabled;
-  readonly attribute nsIDOMHTMLFormElement form;
-           attribute DOMString             label;
-           attribute boolean               defaultSelected;
-           attribute boolean               selected;
-           attribute DOMString             value;
-
-           attribute DOMString             text;
-  readonly attribute long                  index;
-};
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -228,23 +228,23 @@ class MediaRecorder::Session: public Pri
                        nsresult aRv) override
     {
       RefPtr<MediaRecorder> recorder = mSession->mRecorder;
       if (!recorder) {
         return;
       }
 
       if (NS_FAILED(aRv)) {
-        recorder->NotifyError(aRv);
+        mSession->DoSessionEndTask(aRv);
         return;
       }
 
       nsresult rv = recorder->CreateAndDispatchBlobEvent(aBlob);
       if (NS_FAILED(rv)) {
-        recorder->NotifyError(aRv);
+        mSession->DoSessionEndTask(aRv);
       }
 
       if (mDestroyRunnable &&
           NS_FAILED(NS_DispatchToMainThread(mDestroyRunnable.forget()))) {
         MOZ_ASSERT(false, "NS_DispatchToMainThread failed");
       }
     }
 
@@ -270,21 +270,28 @@ class MediaRecorder::Session: public Pri
       : Runnable("StoreEncodedBufferRunnable")
       , mSession(aSession)
       , mBuffer(Move(aBuffer))
     {}
 
     NS_IMETHOD
     Run() override
     {
+      MOZ_ASSERT(NS_IsMainThread());
       mSession->MaybeCreateMutableBlobStorage();
       for (uint32_t i = 0; i < mBuffer.Length(); i++) {
-        if (!mBuffer[i].IsEmpty()) {
-          mSession->mMutableBlobStorage->Append(mBuffer[i].Elements(),
-                                                mBuffer[i].Length());
+        if (mBuffer[i].IsEmpty()) {
+          continue;
+        }
+
+        nsresult rv = mSession->mMutableBlobStorage->Append(mBuffer[i].Elements(),
+                                                            mBuffer[i].Length());
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          mSession->DoSessionEndTask(rv);
+          break;
         }
       }
 
       return NS_OK;
     }
   };
 
   // Notify encoder error, run in main thread task. (Bug 1095381)
@@ -313,39 +320,36 @@ class MediaRecorder::Session: public Pri
   private:
     RefPtr<Session> mSession;
   };
 
   // Fire start event and set mimeType, run in main thread task.
   class DispatchStartEventRunnable : public Runnable
   {
   public:
-    DispatchStartEventRunnable(Session* aSession, const nsAString& aEventName)
+    explicit DispatchStartEventRunnable(Session* aSession)
       : Runnable("dom::MediaRecorder::Session::DispatchStartEventRunnable")
       , mSession(aSession)
-      , mEventName(aEventName)
     { }
 
     NS_IMETHOD Run() override
     {
       LOG(LogLevel::Debug, ("Session.DispatchStartEventRunnable s=(%p)", mSession.get()));
       MOZ_ASSERT(NS_IsMainThread());
 
       NS_ENSURE_TRUE(mSession->mRecorder, NS_OK);
       RefPtr<MediaRecorder> recorder = mSession->mRecorder;
 
-      recorder->SetMimeType(mSession->mMimeType);
-      recorder->DispatchSimpleEvent(mEventName);
+      recorder->DispatchSimpleEvent(NS_LITERAL_STRING("start"));
 
       return NS_OK;
     }
 
   private:
     RefPtr<Session> mSession;
-    nsString mEventName;
   };
 
   // To ensure that MediaRecorder has tracks to record.
   class TracksAvailableCallback : public OnTracksAvailableCallback
   {
   public:
     explicit TracksAvailableCallback(Session *aSession)
      : mSession(aSession) {}
@@ -371,37 +375,43 @@ class MediaRecorder::Session: public Pri
     explicit DestroyRunnable(already_AddRefed<Session> aSession)
       : Runnable("dom::MediaRecorder::Session::DestroyRunnable")
       , mSession(aSession)
     {
     }
 
     NS_IMETHOD Run() override
     {
-      LOG(LogLevel::Debug, ("Session.DestroyRunnable session refcnt = (%d) stopIssued %d s=(%p)",
-                         (int)mSession->mRefCnt, mSession->mStopIssued, mSession.get()));
+      LOG(LogLevel::Debug, ("Session.DestroyRunnable session refcnt = (%d) s=(%p)",
+                            static_cast<int>(mSession->mRefCnt), mSession.get()));
       MOZ_ASSERT(NS_IsMainThread() && mSession);
       RefPtr<MediaRecorder> recorder = mSession->mRecorder;
       if (!recorder) {
         return NS_OK;
       }
       // SourceMediaStream is ended, and send out TRACK_EVENT_END notification.
       // Read Thread will be terminate soon.
       // We need to switch MediaRecorder to "Stop" state first to make sure
       // MediaRecorder is not associated with this Session anymore, then, it's
       // safe to delete this Session.
       // Also avoid to run if this session already call stop before
-      if (!mSession->mStopIssued) {
+      if (mSession->mRunningState.isOk() &&
+          mSession->mRunningState.unwrap() != RunningState::Stopping &&
+          mSession->mRunningState.unwrap() != RunningState::Stopped) {
         recorder->StopForSessionDestruction();
         if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(mSession.forget())))) {
           MOZ_ASSERT(false, "NS_DispatchToMainThread failed");
         }
         return NS_OK;
       }
 
+      if (mSession->mRunningState.isOk()) {
+        mSession->mRunningState = RunningState::Stopped;
+      }
+
       // Dispatch stop event and clear MIME type.
       mSession->mMimeType = NS_LITERAL_STRING("");
       recorder->SetMimeType(mSession->mMimeType);
       recorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
 
       RefPtr<Session> session = mSession.forget();
       session->Shutdown()->Then(
         GetCurrentThreadSerialEventTarget(), __func__,
@@ -479,19 +489,17 @@ class MediaRecorder::Session: public Pri
   friend class PushBlobRunnable;
   friend class DestroyRunnable;
   friend class TracksAvailableCallback;
 
 public:
   Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
     : mRecorder(aRecorder)
     , mTimeSlice(aTimeSlice)
-    , mStopIssued(false)
-    , mIsStartEventFired(false)
-    , mNeedSessionEndTask(true)
+    , mRunningState(RunningState::Idling)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     mMaxMemory = Preferences::GetUint("media.recorder.max_memory",
                                       MAX_ALLOW_MEMORY_BUFFER);
     mLastBlobTimeStamp = TimeStamp::Now();
   }
 
@@ -558,26 +566,30 @@ public:
 
     MOZ_ASSERT(false, "Unknown source");
   }
 
   void Stop()
   {
     LOG(LogLevel::Debug, ("Session.Stop %p", this));
     MOZ_ASSERT(NS_IsMainThread());
-    mStopIssued = true;
 
     if (mEncoder) {
       mEncoder->Stop();
     }
 
-    if (mNeedSessionEndTask) {
-      LOG(LogLevel::Debug, ("Session.Stop mNeedSessionEndTask %p", this));
+    if (mRunningState.isOk() &&
+        mRunningState.unwrap() == RunningState::Idling) {
+      LOG(LogLevel::Debug, ("Session.Stop Explicit end task %p", this));
       // End the Session directly if there is no ExtractRunnable.
       DoSessionEndTask(NS_OK);
+    } else if (mRunningState.isOk() &&
+               (mRunningState.unwrap() == RunningState::Starting ||
+                mRunningState.unwrap() == RunningState::Running)) {
+      mRunningState = RunningState::Stopping;
     }
   }
 
   nsresult Pause()
   {
     LOG(LogLevel::Debug, ("Session.Pause"));
     MOZ_ASSERT(NS_IsMainThread());
 
@@ -709,17 +721,17 @@ private:
         MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
       }
     }
   }
 
   void MediaStreamReady(DOMMediaStream* aStream) {
     MOZ_RELEASE_ASSERT(aStream);
 
-    if (mStopIssued) {
+    if (!mRunningState.isOk() || mRunningState.unwrap() != RunningState::Idling) {
       return;
     }
 
     mMediaStream = aStream;
     aStream->RegisterTrackListener(this);
 
     uint8_t trackTypes = 0;
     nsTArray<RefPtr<mozilla::dom::AudioStreamTrack>> audioTracks;
@@ -827,16 +839,21 @@ private:
     return PrincipalSubsumes(principal);
   }
 
   void InitEncoder(uint8_t aTrackTypes, TrackRate aTrackRate)
   {
     LOG(LogLevel::Debug, ("Session.InitEncoder %p", this));
     MOZ_ASSERT(NS_IsMainThread());
 
+    if (!mRunningState.isOk() || mRunningState.unwrap() != RunningState::Idling) {
+      MOZ_ASSERT_UNREACHABLE("Double-init");
+      return;
+    }
+
     // Create a TaskQueue to read encode media data from MediaEncoder.
     MOZ_RELEASE_ASSERT(!mEncoderThread);
     RefPtr<SharedThreadPool> pool =
       SharedThreadPool::Get(NS_LITERAL_CSTRING("MediaRecorderReadThread"));
     if (!pool) {
       LOG(LogLevel::Debug, ("Session.InitEncoder %p Failed to create "
                             "MediaRecorderReadThread thread pool", this));
       DoSessionEndTask(NS_ERROR_FAILURE);
@@ -939,29 +956,46 @@ private:
       mEncoder->ConnectAudioNode(mRecorder->mAudioNode,
                                  mRecorder->mAudioNodeOutput);
     }
 
     for (auto& track : mMediaStreamTracks) {
       mEncoder->ConnectMediaStreamTrack(track);
     }
 
-    // Set mNeedSessionEndTask to false because the
-    // ExtractRunnable/DestroyRunnable will take the response to
-    // end the session.
-    mNeedSessionEndTask = false;
+    // Set mRunningState to Running so that ExtractRunnable/DestroyRunnable will
+    // take the responsibility to end the session.
+    mRunningState = RunningState::Starting;
   }
 
   // application should get blob and onstop event
   void DoSessionEndTask(nsresult rv)
   {
     MOZ_ASSERT(NS_IsMainThread());
-    if (!mIsStartEventFired) {
-      NS_DispatchToMainThread(
-        new DispatchStartEventRunnable(this, NS_LITERAL_STRING("start")));
+    if (mRunningState.isErr()) {
+      // We have already ended with an error.
+      return;
+    }
+
+    if (mRunningState.isOk() &&
+        mRunningState.unwrap() == RunningState::Stopped) {
+      // We have already ended gracefully.
+      return;
+    }
+
+    if (mRunningState.isOk() &&
+        (mRunningState.unwrap() == RunningState::Idling ||
+         mRunningState.unwrap() == RunningState::Starting)) {
+      NS_DispatchToMainThread(new DispatchStartEventRunnable(this));
+    }
+
+    if (rv == NS_OK) {
+      mRunningState = RunningState::Stopped;
+    } else {
+      mRunningState = Err(rv);
     }
 
     if (NS_FAILED(rv)) {
       mRecorder->ForceInactive();
       NS_DispatchToMainThread(
         NewRunnableMethod<nsresult>("dom::MediaRecorder::NotifyError",
                                     mRecorder,
                                     &MediaRecorder::NotifyError,
@@ -975,47 +1009,66 @@ private:
       if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this, destroyRunnable)))) {
         MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
       }
     } else {
       if (NS_FAILED(NS_DispatchToMainThread(destroyRunnable))) {
         MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
       }
     }
-
-    mNeedSessionEndTask = false;
   }
 
   void MediaEncoderInitialized()
   {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
 
     // Pull encoded metadata from MediaEncoder
     nsTArray<nsTArray<uint8_t> > encodedBuf;
-    nsresult rv = mEncoder->GetEncodedMetadata(&encodedBuf, mMimeType);
+    nsString mime;
+    nsresult rv = mEncoder->GetEncodedMetadata(&encodedBuf, mime);
+
     if (NS_FAILED(rv)) {
       MOZ_ASSERT(false);
       return;
     }
 
     // Append pulled data into cache buffer.
     NS_DispatchToMainThread(new StoreEncodedBufferRunnable(this,
                                                            Move(encodedBuf)));
+
+    RefPtr<Session> self = this;
+    NS_DispatchToMainThread(NewRunnableFrom([self, mime]() {
+      if (!self->mRecorder) {
+        MOZ_ASSERT_UNREACHABLE("Recorder should be live");
+        return NS_OK;
+      }
+      if (self->mRunningState.isOk()) {
+        auto state = self->mRunningState.unwrap();
+        if (state == RunningState::Starting || state == RunningState::Stopping) {
+          if (state == RunningState::Starting) {
+            // We set it to Running in the runnable since we can only assign
+            // mRunningState on main thread. We set it before running the start
+            // event runnable since that dispatches synchronously (and may cause
+            // js calls to methods depending on mRunningState).
+            self->mRunningState = RunningState::Running;
+          }
+          self->mMimeType = mime;
+          self->mRecorder->SetMimeType(self->mMimeType);
+          auto startEvent = MakeRefPtr<DispatchStartEventRunnable>(self);
+          startEvent->Run();
+        }
+      }
+      return NS_OK;
+    }));
   }
 
   void MediaEncoderDataAvailable()
   {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
 
-    if (!mIsStartEventFired) {
-      NS_DispatchToMainThread(
-        new DispatchStartEventRunnable(this, NS_LITERAL_STRING("start")));
-      mIsStartEventFired = true;
-    }
-
     Extract(false, nullptr);
   }
 
   void MediaEncoderError()
   {
     MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
     NS_DispatchToMainThread(
       NewRunnableMethod<nsresult>(
@@ -1112,16 +1165,24 @@ private:
           return ShutdownPromise::CreateAndReject(false, __func__);
         });
     }
 
     return mShutdownPromise;
   }
 
 private:
+  enum class RunningState {
+    Idling, // Session has been created
+    Starting, // MediaEncoder started, waiting for data
+    Running, // MediaEncoder has produced data
+    Stopping, // Stop() has been called
+    Stopped, // Session has stopped without any error
+  };
+
   // Hold reference to MediaRecorder that ensure MediaRecorder is alive
   // if there is an active session. Access ONLY on main thread.
   RefPtr<MediaRecorder> mRecorder;
 
   // Stream currently recorded.
   RefPtr<DOMMediaStream> mMediaStream;
 
   // Tracks currently recorded. This should be a subset of mMediaStream's track
@@ -1144,24 +1205,20 @@ private:
   nsString mMimeType;
   // Timestamp of the last fired dataavailable event.
   TimeStamp mLastBlobTimeStamp;
   // The interval of passing encoded data from MutableBlobStorage to
   // onDataAvailable handler. "mTimeSlice < 0" means Session object does not
   // push encoded data to onDataAvailable, instead, it passive wait the client
   // side pull encoded data by calling requestData API.
   const int32_t mTimeSlice;
-  // Indicate this session's stop has been called.
-  bool mStopIssued;
-  // Indicate the session had fire start event. Encoding thread only.
-  bool mIsStartEventFired;
-  // False if the InitEncoder called successfully, ensure the
-  // ExtractRunnable/DestroyRunnable will end the session.
+  // The session's current main thread state. The error type gets setwhen ending
+  // a recording with an error. An NS_OK error is invalid.
   // Main thread only.
-  bool mNeedSessionEndTask;
+  Result<RunningState, nsresult> mRunningState;
 };
 
 NS_IMPL_ISUPPORTS_INHERITED0(MediaRecorder::Session::PushBlobRunnable, Runnable)
 
 MediaRecorder::~MediaRecorder()
 {
   LOG(LogLevel::Debug, ("~MediaRecorder (%p)", this));
   UnRegisterActivityObserver();
--- a/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
+++ b/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
@@ -6,16 +6,17 @@
 #include "WebBrowserPersistLocalDocument.h"
 #include "WebBrowserPersistDocumentParent.h"
 
 #include "mozilla/dom/HTMLAnchorElement.h"
 #include "mozilla/dom/HTMLAreaElement.h"
 #include "mozilla/dom/HTMLInputElement.h"
 #include "mozilla/dom/HTMLLinkElement.h"
 #include "mozilla/dom/HTMLObjectElement.h"
+#include "mozilla/dom/HTMLOptionElement.h"
 #include "mozilla/dom/HTMLSharedElement.h"
 #include "mozilla/dom/HTMLTextAreaElement.h"
 #include "mozilla/dom/TabParent.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsContentCID.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsFrameLoader.h"
@@ -24,17 +25,16 @@
 #include "nsIDOMAttr.h"
 #include "nsIDOMComment.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMHTMLBaseElement.h"
 #include "nsIDOMHTMLCollection.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMHTMLMediaElement.h"
-#include "nsIDOMHTMLOptionElement.h"
 #include "nsIDOMHTMLScriptElement.h"
 #include "nsIDOMMozNamedAttrMap.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeFilter.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMProcessingInstruction.h"
 #include "nsIDOMTreeWalker.h"
 #include "nsIDOMWindowUtils.h"
@@ -1161,24 +1161,25 @@ PersistNodeFixup::FixupNode(nsIDOMNode *
             nsAutoString valueStr;
             nodeAsTextArea->GetValue(valueStr);
 
             (*aNodeOut)->SetTextContent(valueStr);
         }
         return rv;
     }
 
-    nsCOMPtr<nsIDOMHTMLOptionElement> nodeAsOption = do_QueryInterface(aNodeIn);
+    dom::HTMLOptionElement* nodeAsOption = dom::HTMLOptionElement::FromContent(content);
     if (nodeAsOption) {
         rv = GetNodeToFixup(aNodeIn, aNodeOut);
         if (NS_SUCCEEDED(rv) && *aNodeOut) {
-            nsCOMPtr<nsIDOMHTMLOptionElement> outElt = do_QueryInterface(*aNodeOut);
-            bool selected;
-            nodeAsOption->GetSelected(&selected);
-            outElt->SetDefaultSelected(selected);
+            nsCOMPtr<nsIContent> outContent = do_QueryInterface(*aNodeOut);
+            dom::HTMLOptionElement* outElt = dom::HTMLOptionElement::FromContent(outContent);
+            bool selected = nodeAsOption->Selected();
+            IgnoredErrorResult ignored;
+            outElt->SetDefaultSelected(selected, ignored);
         }
         return rv;
     }
 
     return NS_OK;
 }
 
 } // unnamed namespace
--- a/dom/webidl/HTMLOptionElement.webidl
+++ b/dom/webidl/HTMLOptionElement.webidl
@@ -8,24 +8,23 @@
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 [HTMLConstructor, NamedConstructor=Option(optional DOMString text = "", optional DOMString value, optional boolean defaultSelected = false, optional boolean selected = false)]
 interface HTMLOptionElement : HTMLElement {
-           [CEReactions, SetterThrows]
-           attribute boolean disabled;
+  [CEReactions, SetterThrows]
+  attribute boolean disabled;
   readonly attribute HTMLFormElement? form;
-           [CEReactions, SetterThrows]
-           attribute DOMString label;
-           [CEReactions, SetterThrows]
-           attribute boolean defaultSelected;
-           [SetterThrows]
-           attribute boolean selected;
-           [CEReactions, SetterThrows]
-           attribute DOMString value;
+  [CEReactions, SetterThrows]
+  attribute DOMString label;
+  [CEReactions, SetterThrows]
+  attribute boolean defaultSelected;
+  attribute boolean selected;
+  [CEReactions, SetterThrows]
+  attribute DOMString value;
 
-           [CEReactions, SetterThrows]
-           attribute DOMString text;
+  [CEReactions, SetterThrows]
+  attribute DOMString text;
   readonly attribute long index;
 };
--- a/editor/libeditor/CreateElementTransaction.cpp
+++ b/editor/libeditor/CreateElementTransaction.cpp
@@ -27,130 +27,151 @@
 #include "nsReadableUtils.h"
 #include "nsStringFwd.h"
 #include "nsString.h"
 
 namespace mozilla {
 
 using namespace dom;
 
-CreateElementTransaction::CreateElementTransaction(EditorBase& aEditorBase,
-                                                   nsAtom& aTag,
-                                                   nsINode& aParent,
-                                                   int32_t aOffsetInParent,
-                                                   nsIContent* aChildAtOffset)
+CreateElementTransaction::CreateElementTransaction(
+                            EditorBase& aEditorBase,
+                            nsAtom& aTag,
+                            const EditorRawDOMPoint& aPointToInsert)
   : EditTransactionBase()
   , mEditorBase(&aEditorBase)
   , mTag(&aTag)
-  , mParent(&aParent)
-  , mOffsetInParent(aOffsetInParent)
-  , mRefNode(aChildAtOffset)
+  , mPointToInsert(aPointToInsert)
 {
 }
 
 CreateElementTransaction::~CreateElementTransaction()
 {
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(CreateElementTransaction,
                                    EditTransactionBase,
                                    mEditorBase,
-                                   mParent,
-                                   mNewNode,
-                                   mRefNode)
+                                   mPointToInsert,
+                                   mNewNode)
 
 NS_IMPL_ADDREF_INHERITED(CreateElementTransaction, EditTransactionBase)
 NS_IMPL_RELEASE_INHERITED(CreateElementTransaction, EditTransactionBase)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CreateElementTransaction)
 NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
 
 
 NS_IMETHODIMP
 CreateElementTransaction::DoTransaction()
 {
-  if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mTag) || NS_WARN_IF(!mParent)) {
+  if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mTag) ||
+      NS_WARN_IF(!mPointToInsert.IsSet())) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   mNewNode = mEditorBase->CreateHTMLContent(mTag);
   NS_ENSURE_STATE(mNewNode);
 
   // Try to insert formatting whitespace for the new node:
   mEditorBase->MarkNodeDirty(GetAsDOMNode(mNewNode));
 
   // Insert the new node
-  ErrorResult rv;
-  if (mOffsetInParent == -1) {
-    mParent->AppendChild(*mNewNode, rv);
-    return rv.StealNSResult();
+  ErrorResult error;
+  InsertNewNode(error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.StealNSResult();
   }
 
-  mOffsetInParent = std::min(mOffsetInParent,
-                             static_cast<int32_t>(mParent->GetChildCount()));
-
-  if (!mRefNode) {
-    // Note, it's ok for mRefNode to be null. That means append
-    mRefNode = mParent->GetChildAt(mOffsetInParent);
-  }
-
-  nsCOMPtr<nsIContent> refNode = mRefNode;
-  mParent->InsertBefore(*mNewNode, refNode, rv);
-  NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
-
   // Only set selection to insertion point if editor gives permission
   if (!mEditorBase->GetShouldTxnSetSelection()) {
     // Do nothing - DOM range gravity will adjust selection
     return NS_OK;
   }
 
   RefPtr<Selection> selection = mEditorBase->GetSelection();
   NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
 
   EditorRawDOMPoint afterNewNode(mNewNode);
   if (NS_WARN_IF(!afterNewNode.AdvanceOffset())) {
     // If mutation observer or mutation event listener moved or removed the
     // new node, we hit this case.  Should we use script blocker while we're
     // in this method?
     return NS_ERROR_FAILURE;
   }
-  rv = selection->Collapse(afterNewNode);
-  NS_ASSERTION(!rv.Failed(),
-               "selection could not be collapsed after insert");
+  selection->Collapse(afterNewNode, error);
+  if (error.Failed()) {
+    NS_WARNING("selection could not be collapsed after insert");
+    error.SuppressException();
+  }
   return NS_OK;
 }
 
+void
+CreateElementTransaction::InsertNewNode(ErrorResult& aError)
+{
+  if (mPointToInsert.IsSetAndValid()) {
+    if (mPointToInsert.IsEndOfContainer()) {
+      mPointToInsert.Container()->AppendChild(*mNewNode, aError);
+      NS_WARNING_ASSERTION(!aError.Failed(), "Failed to append the new node");
+      return;
+    }
+    mPointToInsert.Container()->InsertBefore(*mNewNode,
+                                             mPointToInsert.GetChildAtOffset(),
+                                             aError);
+    NS_WARNING_ASSERTION(!aError.Failed(), "Failed to insert the new node");
+    return;
+  }
+
+  if (NS_WARN_IF(mPointToInsert.GetChildAtOffset() &&
+                 mPointToInsert.Container() !=
+                   mPointToInsert.GetChildAtOffset()->GetParentNode())) {
+    aError.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
+  // If mPointToInsert has only offset and it's not valid, we need to treat
+  // it as pointing end of the container.
+  mPointToInsert.Container()->AppendChild(*mNewNode, aError);
+  NS_WARNING_ASSERTION(!aError.Failed(), "Failed to append the new node");
+}
+
 NS_IMETHODIMP
 CreateElementTransaction::UndoTransaction()
 {
-  if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mParent)) {
+  if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mPointToInsert.IsSet())) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  ErrorResult rv;
-  mParent->RemoveChild(*mNewNode, rv);
-
-  return rv.StealNSResult();
+  ErrorResult error;
+  mPointToInsert.Container()->RemoveChild(*mNewNode, error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.StealNSResult();
+  }
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 CreateElementTransaction::RedoTransaction()
 {
-  if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mParent)) {
+  if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mPointToInsert.IsSet())) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // First, reset mNewNode so it has no attributes or content
   // XXX We never actually did this, we only cleared mNewNode's contents if it
   // was a CharacterData node (which it's not, it's an Element)
+  // XXX Don't we need to set selection like DoTransaction()?
 
   // Now, reinsert mNewNode
-  ErrorResult rv;
-  nsCOMPtr<nsIContent> refNode = mRefNode;
-  mParent->InsertBefore(*mNewNode, refNode, rv);
-  return rv.StealNSResult();
+  ErrorResult error;
+  InsertNewNode(error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.StealNSResult();
+  }
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 CreateElementTransaction::GetTxnDescription(nsAString& aString)
 {
   aString.AssignLiteral("CreateElementTransaction: ");
   aString += nsDependentAtomString(mTag);
   return NS_OK;
--- a/editor/libeditor/CreateElementTransaction.h
+++ b/editor/libeditor/CreateElementTransaction.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 CreateElementTransaction_h
 #define CreateElementTransaction_h
 
+#include "mozilla/EditorDOMPoint.h"
 #include "mozilla/EditTransactionBase.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsISupportsImpl.h"
 
 class nsAtom;
 class nsIContent;
 class nsINode;
@@ -27,56 +28,51 @@ class Element;
 
 class CreateElementTransaction final : public EditTransactionBase
 {
 public:
   /**
    * Initialize the transaction.
    * @param aEditorBase     The provider of basic editing functionality.
    * @param aTag            The tag (P, HR, TABLE, etc.) for the new element.
-   * @param aParent         The node into which the new element will be
-   *                        inserted.
-   * @param aOffsetInParent The location in aParent to insert the new element.
-   *                        If eAppend, the new element is appended as the last
-   *                        child.
+   * @param aPointToInsert  The new node will be inserted before the child at
+   *                        aPointToInsert.  If this refers end of the container
+   *                        or after, the new node will be appended to the
+   *                        container.
    */
   CreateElementTransaction(EditorBase& aEditorBase,
                            nsAtom& aTag,
-                           nsINode& aParent,
-                           int32_t aOffsetInParent,
-                           nsIContent* aChildAtOffset);
+                           const EditorRawDOMPoint& aPointToInsert);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CreateElementTransaction,
                                            EditTransactionBase)
 
   NS_DECL_EDITTRANSACTIONBASE
 
   NS_IMETHOD RedoTransaction() override;
 
   already_AddRefed<dom::Element> GetNewNode();
 
 protected:
   virtual ~CreateElementTransaction();
 
+  /**
+   * InsertNewNode() inserts mNewNode before the child node at mPointToInsert.
+   */
+  void InsertNewNode(ErrorResult& aError);
+
   // The document into which the new node will be inserted.
   RefPtr<EditorBase> mEditorBase;
 
   // The tag (mapping to object type) for the new element.
   RefPtr<nsAtom> mTag;
 
-  // The node into which the new node will be inserted.
-  nsCOMPtr<nsINode> mParent;
-
-  // The index in mParent for the new node.
-  int32_t mOffsetInParent;
+  // The DOM point we will insert mNewNode.
+  RangeBoundary mPointToInsert;
 
   // The new node to insert.
   nsCOMPtr<dom::Element> mNewNode;
-
-  // The node we will insert mNewNode before.  We compute this ourselves if it
-  // is not set by the constructor.
-  nsCOMPtr<nsIContent> mRefNode;
 };
 
 } // namespace mozilla
 
 #endif // #ifndef CreateElementTransaction_h
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -1414,50 +1414,57 @@ EditorBase::SetSpellcheckUserOverride(bo
 {
   mSpellcheckCheckboxState = enable ? eTriTrue : eTriFalse;
 
   return SyncRealTimeSpell();
 }
 
 already_AddRefed<Element>
 EditorBase::CreateNode(nsAtom* aTag,
-                       nsINode* aParent,
-                       int32_t aPosition,
-                       nsIContent* aChildAtPosition)
-{
-  MOZ_ASSERT(aTag && aParent);
+                       EditorRawDOMPoint& aPointToInsert)
+{
+  MOZ_ASSERT(aTag);
+  MOZ_ASSERT(aPointToInsert.IsSetAndValid());
+
+  // XXX We need to offset at new node to mRangeUpdater.  Therefore, we need
+  //     to compute the offset now but this is expensive.  So, if it's possible,
+  //     we need to redesign mRangeUpdater as avoiding using indices.
+  int32_t offset = static_cast<int32_t>(aPointToInsert.Offset());
 
   AutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::eNext);
 
   {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
       listener->WillCreateNode(nsDependentAtomString(aTag),
-                               GetAsDOMNode(aParent), aPosition);
+                               GetAsDOMNode(aPointToInsert.GetChildAtOffset()));
     }
   }
 
   nsCOMPtr<Element> ret;
 
   RefPtr<CreateElementTransaction> transaction =
-    CreateTxnForCreateElement(*aTag, *aParent, aPosition,
-                              aChildAtPosition);
+    CreateTxnForCreateElement(*aTag, aPointToInsert);
   nsresult rv = DoTransaction(transaction);
   if (NS_SUCCEEDED(rv)) {
     ret = transaction->GetNewNode();
     MOZ_ASSERT(ret);
-  }
-
-  mRangeUpdater.SelAdjCreateNode(aParent, aPosition);
+    // Now, aPointToInsert may be invalid.  I.e., ChildAtOffset() keeps
+    // referring the next sibling of new node but Offset() refers the
+    // new node.  Let's make refer the new node.
+    aPointToInsert.Set(ret);
+  }
+
+  mRangeUpdater.SelAdjCreateNode(aPointToInsert.Container(), offset);
 
   {
     AutoActionListenerArray listeners(mActionListeners);
     for (auto& listener : listeners) {
-      listener->DidCreateNode(nsDependentAtomString(aTag), GetAsDOMNode(ret),
-                              GetAsDOMNode(aParent), aPosition, rv);
+      listener->DidCreateNode(nsDependentAtomString(aTag),
+                              GetAsDOMNode(ret), rv);
     }
   }
 
   return ret.forget();
 }
 
 NS_IMETHODIMP
 EditorBase::InsertNode(nsIDOMNode* aNode,
@@ -4209,26 +4216,39 @@ already_AddRefed<Element>
 EditorBase::DeleteSelectionAndCreateElement(nsAtom& aTag)
 {
   nsresult rv = DeleteSelectionAndPrepareToCreateNode();
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   RefPtr<Selection> selection = GetSelection();
   NS_ENSURE_TRUE(selection, nullptr);
 
-  nsCOMPtr<nsINode> node = selection->GetAnchorNode();
-  uint32_t offset = selection->AnchorOffset();
-  nsIContent* child = selection->GetChildAtAnchorOffset();
-
-  nsCOMPtr<Element> newElement = CreateNode(&aTag, node, offset, child);
+  EditorRawDOMPoint pointToInsert(selection->GetChildAtAnchorOffset());
+  if (!pointToInsert.IsSet()) {
+    // Perhaps, the anchor point is in a text node.
+    pointToInsert.Set(selection->GetAnchorNode(), selection->AnchorOffset());
+    if (NS_WARN_IF(!pointToInsert.IsSet())) {
+      return nullptr;
+    }
+  }
+  RefPtr<Element> newElement = CreateNode(&aTag, pointToInsert);
 
   // We want the selection to be just after the new node
-  rv = selection->Collapse(node, offset + 1);
-  NS_ENSURE_SUCCESS(rv, nullptr);
-
+  DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
+  NS_WARNING_ASSERTION(advanced,
+                       "Failed to move offset next to the new element");
+  ErrorResult error;
+  selection->Collapse(pointToInsert, error);
+  if (NS_WARN_IF(error.Failed())) {
+    // XXX Even if it succeeded to create new element, this returns error
+    //     when Selection.Collapse() fails something.  This could occur with
+    //     mutation observer or mutation event listener.
+    error.SuppressException();
+    return nullptr;
+  }
   return newElement.forget();
 }
 
 TextComposition*
 EditorBase::GetComposition() const
 {
   return mComposition;
 }
@@ -4363,23 +4383,20 @@ EditorBase::CreateTxnForRemoveAttribute(
   RefPtr<ChangeAttributeTransaction> transaction =
     new ChangeAttributeTransaction(aElement, aAttribute, nullptr);
 
   return transaction.forget();
 }
 
 already_AddRefed<CreateElementTransaction>
 EditorBase::CreateTxnForCreateElement(nsAtom& aTag,
-                                      nsINode& aParent,
-                                      int32_t aPosition,
-                                      nsIContent* aChildAtPosition)
+                                      const EditorRawDOMPoint& aPointToInsert)
 {
   RefPtr<CreateElementTransaction> transaction =
-    new CreateElementTransaction(*this, aTag, aParent, aPosition,
-                                 aChildAtPosition);
+    new CreateElementTransaction(*this, aTag, aPointToInsert);
 
   return transaction.forget();
 }
 
 
 already_AddRefed<InsertNodeTransaction>
 EditorBase::CreateTxnForInsertNode(nsIContent& aNode,
                                    nsINode& aParent,
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -389,27 +389,49 @@ protected:
   /**
    * Create a transaction for removing aAttribute on aElement.  Never returns
    * null.
    */
   already_AddRefed<ChangeAttributeTransaction>
     CreateTxnForRemoveAttribute(Element& aElement, nsAtom& aAttribute);
 
   /**
-   * Create a transaction for creating a new child node of aParent of type aTag.
+   * Create a transaction for creating a new child node of the container of
+   * aPointToInsert of type aTag.
+   *
+   * @param aTag            The element name to create.
+   * @param aPointToInsert  The insertion point of new element.  If this refers
+   *                        end of the container or after, the transaction
+   *                        will append the element to the container.
+   *                        Otherwise, will insert the element before the
+   *                        child node referred by this.
+   * @return                A CreateElementTransaction which are initialized
+   *                        with the arguments.
    */
   already_AddRefed<CreateElementTransaction>
     CreateTxnForCreateElement(nsAtom& aTag,
-                              nsINode& aParent,
-                              int32_t aPosition,
-                              nsIContent* aChildAtPosition);
+                              const EditorRawDOMPoint& aPointToInsert);
 
-  already_AddRefed<Element> CreateNode(nsAtom* aTag, nsINode* aParent,
-                                       int32_t aPosition,
-                                       nsIContent* aChildAtPosition = nullptr);
+  /**
+   * Create an element node whose name is aTag at before aPointToInsert.  When
+   * this succeed to create an element node, this sets aPointToInsert to the
+   * new element because the relation of child and offset may be broken.
+   * If the caller needs to collapse the selection to next to the new element
+   * node, it should call |aPointToInsert.AdvanceOffset()| after calling this.
+   *
+   * @param aTag            The element name to create.
+   * @param aPointToInsert  The insertion point of new element.  If this refers
+   *                        end of the container or after, the transaction
+   *                        will append the element to the container.
+   *                        Otherwise, will insert the element before the
+   *                        child node referred by this.
+   * @return                The created new element node.
+   */
+  already_AddRefed<Element> CreateNode(nsAtom* aTag,
+                                       EditorRawDOMPoint& aPointToInsert);
 
   /**
    * Create a transaction for inserting aNode as a child of aParent.
    */
   already_AddRefed<InsertNodeTransaction>
     CreateTxnForInsertNode(nsIContent& aNode, nsINode& aParent,
                            int32_t aOffset);
 
--- a/editor/libeditor/EditorDOMPoint.h
+++ b/editor/libeditor/EditorDOMPoint.h
@@ -54,43 +54,52 @@ public:
    * 0 - Length() - 1.  Otherwise, set nullptr, i.e., if offset is same as
    * Length().
    */
   explicit EditorDOMPointBase(nsINode* aPointedNode)
     : RangeBoundaryBase<ParentType, RefType>(
         aPointedNode && aPointedNode->IsContent() ?
           aPointedNode->GetParentNode() : nullptr,
         aPointedNode && aPointedNode->IsContent() ?
-          GetRef(aPointedNode->AsContent()) : nullptr)
+          GetRef(aPointedNode->GetParentNode(),
+                 aPointedNode->AsContent()) : nullptr)
   {
   }
 
-  EditorDOMPointBase(nsINode* aConatiner,
+  EditorDOMPointBase(nsINode* aContainer,
                      nsIContent* aPointedNode,
                      int32_t aOffset)
-    : RangeBoundaryBase<ParentType, RefType>(aConatiner,
-                                             GetRef(aPointedNode),
+    : RangeBoundaryBase<ParentType, RefType>(aContainer,
+                                             GetRef(aContainer, aPointedNode),
                                              aOffset)
   {
   }
 
   template<typename PT, typename RT>
   explicit EditorDOMPointBase(const RangeBoundaryBase<PT, RT>& aOther)
     : RangeBoundaryBase<ParentType, RefType>(aOther)
   {
   }
 
   explicit EditorDOMPointBase(const RawRangeBoundary& aRawRangeBoundary)
     : RangeBoundaryBase<ParentType, RefType>(aRawRangeBoundary)
   {
   }
 
 private:
-  static nsIContent* GetRef(nsIContent* aPointedNode)
+  static nsIContent* GetRef(nsINode* aContainerNode, nsIContent* aPointedNode)
   {
-    MOZ_ASSERT(aPointedNode);
-    return aPointedNode ? aPointedNode->GetPreviousSibling() : nullptr;
+    // If referring one of a child of the container, the 'ref' should be the
+    // previous sibling of the referring child.
+    if (aPointedNode) {
+      return aPointedNode->GetPreviousSibling();
+    }
+    // If referring after the last child, the 'ref' should be the last child.
+    if (aContainerNode && aContainerNode->IsContainerNode()) {
+      return aContainerNode->GetLastChild();
+    }
+    return nullptr;
   }
 };
 
 } // namespace mozilla
 
 #endif // #ifndef mozilla_EditorDOMPoint_h
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -3391,33 +3391,39 @@ HTMLEditRules::WillMakeList(Selection* a
     if (!mHTMLEditor->CanContainTag(*container, listType)) {
       *aCancel = true;
       return NS_OK;
     }
     rv = SplitAsNeeded(listType, container, offset,
                        address_of(child));
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ENSURE_STATE(mHTMLEditor);
-    nsCOMPtr<Element> theList =
-      mHTMLEditor->CreateNode(listType, container, offset, child);
+    EditorRawDOMPoint atListItemToInsertBefore(container, child, offset);
+    RefPtr<Element> theList =
+      mHTMLEditor->CreateNode(listType, atListItemToInsertBefore);
     NS_ENSURE_STATE(theList);
 
     NS_ENSURE_STATE(mHTMLEditor);
-    nsCOMPtr<Element> theListItem =
-      mHTMLEditor->CreateNode(itemType, theList, 0, theList->GetFirstChild());
+    EditorRawDOMPoint atFirstListItemToInsertBefore(theList, 0);
+    RefPtr<Element> theListItem =
+      mHTMLEditor->CreateNode(itemType, atFirstListItemToInsertBefore);
     NS_ENSURE_STATE(theListItem);
 
     // remember our new block for postprocessing
     mNewBlock = theListItem;
     // put selection in new list item
     *aHandled = true;
-    rv = aSelection->Collapse(theListItem, 0);
+    ErrorResult error;
+    aSelection->Collapse(EditorRawDOMPoint(theListItem, 0), error);
     // Don't restore the selection
     selectionRestorer.Abort();
-    return rv;
+    if (NS_WARN_IF(!error.Failed())) {
+      return error.StealNSResult();
+    }
+    return NS_OK;
   }
 
   // if there is only one node in the array, and it is a list, div, or
   // blockquote, then look inside of it until we find inner list or content.
 
   LookInsideDivBQandList(arrayOfNodes);
 
   // Ok, now go through all the nodes and put then in the list,
@@ -3493,23 +3499,19 @@ HTMLEditRules::WillMakeList(Selection* a
         if (!curList || EditorUtils::IsDescendantOf(*curNode, *curList)) {
           NS_ENSURE_STATE(mHTMLEditor);
           NS_ENSURE_STATE(curParent->IsContent());
           ErrorResult rv;
           nsCOMPtr<nsIContent> splitNode =
             mHTMLEditor->SplitNode(*curParent->AsContent(), offset, rv);
           NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
           newBlock = splitNode ? splitNode->AsElement() : nullptr;
-          int32_t offset;
-          nsCOMPtr<nsINode> parent = EditorBase::GetNodeLocation(curParent,
-                                                                 &offset);
           NS_ENSURE_STATE(mHTMLEditor);
-          curList = mHTMLEditor->CreateNode(listType, parent, offset,
-                                            curParent ? curParent->AsContent()
-                                                      : nullptr);
+          EditorRawDOMPoint atCurParent(curParent);
+          curList = mHTMLEditor->CreateNode(listType, atCurParent);
           NS_ENSURE_STATE(curList);
         }
         // move list item to new list
         NS_ENSURE_STATE(mHTMLEditor);
         rv = mHTMLEditor->MoveNode(curNode, curList, -1);
         NS_ENSURE_SUCCESS(rv, rv);
         // convert list item type if needed
         NS_ENSURE_STATE(mHTMLEditor);
@@ -3570,18 +3572,18 @@ HTMLEditRules::WillMakeList(Selection* a
 
     // need to make a list to put things in if we haven't already,
     if (!curList) {
       nsCOMPtr<nsIContent> curChild(curNode);
       rv = SplitAsNeeded(listType, curParent, offset,
                          address_of(curChild));
       NS_ENSURE_SUCCESS(rv, rv);
       NS_ENSURE_STATE(mHTMLEditor);
-      curList = mHTMLEditor->CreateNode(listType, curParent, offset,
-                                        curChild);
+      EditorRawDOMPoint atCurChild(curParent, curChild, offset);
+      curList = mHTMLEditor->CreateNode(listType, atCurChild);
       // remember our new block for postprocessing
       mNewBlock = curList;
       // curList is now the correct thing to put curNode in
       prevListItem = nullptr;
     }
 
     // if curNode isn't a list item, we must wrap it in one
     nsCOMPtr<Element> listItem;
@@ -3776,23 +3778,25 @@ HTMLEditRules::MakeBasicBlock(Selection&
       // We are making a block.  Consume a br, if needed.
       nsCOMPtr<nsIContent> brNode =
         htmlEditor->GetNextHTMLNode(container, offset, child, true);
       if (brNode && brNode->IsHTMLElement(nsGkAtoms::br)) {
         rv = htmlEditor->DeleteNode(brNode);
         NS_ENSURE_SUCCESS(rv, rv);
         // We don't need to act on this node any more
         arrayOfNodes.RemoveElement(brNode);
-        child = nullptr;
+        // XXX We need to recompute child here because SplitAsNeeded() and
+        //     EditorBase::SplitNodeDeep() don't compute child in some cases.
+        child = container->GetChildAt(offset);
       }
       // Make sure we can put a block here
       rv = SplitAsNeeded(blockType, container, offset, address_of(child));
       NS_ENSURE_SUCCESS(rv, rv);
-      nsCOMPtr<Element> block =
-        htmlEditor->CreateNode(&blockType, container, offset, child);
+      EditorRawDOMPoint atChild(container, child, offset);
+      RefPtr<Element> block = htmlEditor->CreateNode(&blockType, atChild);
       NS_ENSURE_STATE(block);
       // Remember our new block for postprocessing
       mNewBlock = block;
       // Delete anything that was in the list of nodes
       while (!arrayOfNodes.IsEmpty()) {
         OwningNonNull<nsINode> curNode = arrayOfNodes[0];
         rv = htmlEditor->DeleteNode(curNode);
         NS_ENSURE_SUCCESS(rv, rv);
@@ -3923,37 +3927,42 @@ HTMLEditRules::WillCSSIndent(Selection* 
     NS_ENSURE_STATE(container);
     nsCOMPtr<nsIContent> child =
       aSelection->GetRangeAt(0)->GetChildAtStartOffset();
 
     // make sure we can put a block here
     rv = SplitAsNeeded(*nsGkAtoms::div, container, offset, address_of(child));
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ENSURE_STATE(mHTMLEditor);
-    nsCOMPtr<Element> theBlock = mHTMLEditor->CreateNode(nsGkAtoms::div,
-                                                         container, offset,
-                                                         child);
+    EditorRawDOMPoint atChild(container, child, offset);
+    RefPtr<Element> theBlock =
+      mHTMLEditor->CreateNode(nsGkAtoms::div, atChild);
     NS_ENSURE_STATE(theBlock);
     // remember our new block for postprocessing
     mNewBlock = theBlock;
     ChangeIndentation(*theBlock, Change::plus);
     // delete anything that was in the list of nodes
     while (!arrayOfNodes.IsEmpty()) {
       OwningNonNull<nsINode> curNode = arrayOfNodes[0];
       NS_ENSURE_STATE(mHTMLEditor);
       rv = mHTMLEditor->DeleteNode(curNode);
       NS_ENSURE_SUCCESS(rv, rv);
       arrayOfNodes.RemoveElementAt(0);
     }
     // put selection in new block
     *aHandled = true;
-    rv = aSelection->Collapse(theBlock, 0);
+    EditorRawDOMPoint atStartOfTheBlock(theBlock, 0);
+    ErrorResult error;
+    aSelection->Collapse(atStartOfTheBlock, error);
     // Don't restore the selection
     selectionRestorer.Abort();
-    return rv;
+    if (NS_WARN_IF(!error.Failed())) {
+      return error.StealNSResult();
+    }
+    return NS_OK;
   }
 
   // Ok, now go through all the nodes and put them in a blockquote,
   // or whatever is appropriate.  Wohoo!
   nsCOMPtr<Element> curList, curQuote;
   nsCOMPtr<nsIContent> sibling;
   int32_t listCount = arrayOfNodes.Length();
   for (int32_t i = 0; i < listCount; i++) {
@@ -4020,18 +4029,19 @@ HTMLEditRules::WillCSSIndent(Selection* 
       if (!curList || (sibling && sibling != curList)) {
         // create a new nested list of correct type
         nsCOMPtr<nsIContent> curChild(curNode);
         rv =
           SplitAsNeeded(*curParent->NodeInfo()->NameAtom(), curParent, offset,
                         address_of(curChild));
         NS_ENSURE_SUCCESS(rv, rv);
         NS_ENSURE_STATE(mHTMLEditor);
+        EditorRawDOMPoint atCurChild(curParent, curChild, offset);
         curList = mHTMLEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
-                                          curParent, offset, curChild);
+                                          atCurChild);
         NS_ENSURE_STATE(curList);
         // curList is now the correct thing to put curNode in
         // remember our new block for postprocessing
         mNewBlock = curList;
       }
       // tuck the node into the end of the active list
       uint32_t listLen = curList->Length();
       NS_ENSURE_STATE(mHTMLEditor);
@@ -4053,18 +4063,18 @@ HTMLEditRules::WillCSSIndent(Selection* 
             return NS_OK; // cancelled
           }
 
           nsCOMPtr<nsIContent> curChild(curNode);
           rv = SplitAsNeeded(*nsGkAtoms::div, curParent, offset,
                              address_of(curChild));
           NS_ENSURE_SUCCESS(rv, rv);
           NS_ENSURE_STATE(mHTMLEditor);
-          curQuote = mHTMLEditor->CreateNode(nsGkAtoms::div, curParent,
-                                             offset, curChild);
+          EditorRawDOMPoint atCurChild(curParent, curChild, offset);
+          curQuote = mHTMLEditor->CreateNode(nsGkAtoms::div, atCurChild);
           NS_ENSURE_STATE(curQuote);
           ChangeIndentation(*curQuote, Change::plus);
           // remember our new block for postprocessing
           mNewBlock = curQuote;
           // curQuote is now the correct thing to put curNode in
         }
 
         // tuck the node into the end of the active blockquote
@@ -4122,36 +4132,41 @@ HTMLEditRules::WillHTMLIndent(Selection*
     nsCOMPtr<nsIContent> child =
       aSelection->GetRangeAt(0)->GetChildAtStartOffset();
 
     // make sure we can put a block here
     rv = SplitAsNeeded(*nsGkAtoms::blockquote, container, offset,
                        address_of(child));
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ENSURE_STATE(mHTMLEditor);
-    nsCOMPtr<Element> theBlock = mHTMLEditor->CreateNode(nsGkAtoms::blockquote,
-                                                         container, offset,
-                                                         child);
+    EditorRawDOMPoint atChild(container, child, offset);
+    RefPtr<Element> theBlock =
+      mHTMLEditor->CreateNode(nsGkAtoms::blockquote, atChild);
     NS_ENSURE_STATE(theBlock);
     // remember our new block for postprocessing
     mNewBlock = theBlock;
     // delete anything that was in the list of nodes
     while (!arrayOfNodes.IsEmpty()) {
       OwningNonNull<nsINode> curNode = arrayOfNodes[0];
       NS_ENSURE_STATE(mHTMLEditor);
       rv = mHTMLEditor->DeleteNode(curNode);
       NS_ENSURE_SUCCESS(rv, rv);
       arrayOfNodes.RemoveElementAt(0);
     }
     // put selection in new block
     *aHandled = true;
-    rv = aSelection->Collapse(theBlock, 0);
+    EditorRawDOMPoint atStartOfTheBlock(theBlock, 0);
+    ErrorResult error;
+    aSelection->Collapse(atStartOfTheBlock, error);
     // Don't restore the selection
     selectionRestorer.Abort();
-    return rv;
+    if (NS_WARN_IF(!error.Failed())) {
+      return error.StealNSResult();
+    }
+    return NS_OK;
   }
 
   // Ok, now go through all the nodes and put them in a blockquote,
   // or whatever is appropriate.  Wohoo!
   nsCOMPtr<nsIContent> sibling;
   nsCOMPtr<Element> curList, curQuote, indentedLI;
   int32_t listCount = arrayOfNodes.Length();
   for (int32_t i = 0; i < listCount; i++) {
@@ -4219,18 +4234,19 @@ HTMLEditRules::WillHTMLIndent(Selection*
       if (!curList || (sibling && sibling != curList)) {
         // create a new nested list of correct type
         nsCOMPtr<nsIContent> curChild(curNode);
         rv =
           SplitAsNeeded(*curParent->NodeInfo()->NameAtom(), curParent, offset,
                         address_of(curChild));
         NS_ENSURE_SUCCESS(rv, rv);
         NS_ENSURE_STATE(mHTMLEditor);
+        EditorRawDOMPoint atCurChild(curParent, curChild, offset);
         curList = mHTMLEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
-                                          curParent, offset, curChild);
+                                          atCurChild);
         NS_ENSURE_STATE(curList);
         // curList is now the correct thing to put curNode in
         // remember our new block for postprocessing
         mNewBlock = curList;
       }
       // tuck the node into the end of the active list
       NS_ENSURE_STATE(mHTMLEditor);
       rv = mHTMLEditor->MoveNode(curNode, curList, -1);
@@ -4264,18 +4280,19 @@ HTMLEditRules::WillHTMLIndent(Selection*
 
         if (!curList || (sibling && sibling != curList)) {
           // create a new nested list of correct type
           nsCOMPtr<nsIContent> curChild(listItem);
           rv = SplitAsNeeded(*curParent->NodeInfo()->NameAtom(), curParent,
                              offset, address_of(curChild));
           NS_ENSURE_SUCCESS(rv, rv);
           NS_ENSURE_STATE(mHTMLEditor);
+          EditorRawDOMPoint atCurChild(curParent, curChild, offset);
           curList = mHTMLEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
-                                            curParent, offset, curChild);
+                                            atCurChild);
           NS_ENSURE_STATE(curList);
         }
         NS_ENSURE_STATE(mHTMLEditor);
         rv = mHTMLEditor->MoveNode(listItem, curList, -1);
         NS_ENSURE_SUCCESS(rv, rv);
         // remember we indented this li
         indentedLI = listItem;
       } else {
@@ -4296,18 +4313,18 @@ HTMLEditRules::WillHTMLIndent(Selection*
             return NS_OK; // cancelled
           }
 
           nsCOMPtr<nsIContent> curChild(curNode);
           rv = SplitAsNeeded(*nsGkAtoms::blockquote, curParent, offset,
                              address_of(curChild));
           NS_ENSURE_SUCCESS(rv, rv);
           NS_ENSURE_STATE(mHTMLEditor);
-          curQuote = mHTMLEditor->CreateNode(nsGkAtoms::blockquote, curParent,
-                                             offset, curChild);
+          EditorRawDOMPoint atCurChild(curParent, curChild, offset);
+          curQuote = mHTMLEditor->CreateNode(nsGkAtoms::blockquote, atCurChild);
           NS_ENSURE_STATE(curQuote);
           // remember our new block for postprocessing
           mNewBlock = curQuote;
           // curQuote is now the correct thing to put curNode in
         }
 
         // tuck the node into the end of the active blockquote
         NS_ENSURE_STATE(mHTMLEditor);
@@ -4911,32 +4928,36 @@ HTMLEditRules::WillAlign(Selection& aSel
       if (child) {
         sibling = htmlEditor->GetNextHTMLSibling(child);
       }
       if (sibling && !IsBlockNode(*sibling)) {
         rv = htmlEditor->DeleteNode(brContent);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
-    nsCOMPtr<Element> div = htmlEditor->CreateNode(nsGkAtoms::div, parent,
-                                                   offset, child);
+    EditorRawDOMPoint atChild(parent, child, offset);
+    RefPtr<Element> div = htmlEditor->CreateNode(nsGkAtoms::div, atChild);
     NS_ENSURE_STATE(div);
     // Remember our new block for postprocessing
     mNewBlock = div;
     // Set up the alignment on the div, using HTML or CSS
     rv = AlignBlock(*div, aAlignType, ContentsOnly::yes);
     NS_ENSURE_SUCCESS(rv, rv);
     *aHandled = true;
     // Put in a moz-br so that it won't get deleted
     rv = CreateMozBR(div->AsDOMNode(), 0);
     NS_ENSURE_SUCCESS(rv, rv);
-    rv = aSelection.Collapse(div, 0);
+    EditorRawDOMPoint atStartOfDiv(div, 0);
+    ErrorResult error;
+    aSelection.Collapse(atStartOfDiv, error);
     // Don't restore the selection
     selectionRestorer.Abort();
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.StealNSResult();
+    }
     return NS_OK;
   }
 
   // Next we detect all the transitions in the array, where a transition
   // means that adjacent nodes in the array don't have the same parent.
 
   nsTArray<bool> transitionList;
   MakeTransitionList(nodeArray, transitionList);
@@ -5017,18 +5038,18 @@ HTMLEditRules::WillAlign(Selection& aSel
         // Cancelled
         return NS_OK;
       }
 
       nsCOMPtr<nsIContent> curChild(curNode->AsContent());
       rv = SplitAsNeeded(*nsGkAtoms::div, curParent, offset,
                          address_of(curChild));
       NS_ENSURE_SUCCESS(rv, rv);
-      curDiv = htmlEditor->CreateNode(nsGkAtoms::div, curParent, offset,
-                                      curChild);
+      EditorRawDOMPoint atCurChild(curParent, curChild, offset);
+      curDiv = htmlEditor->CreateNode(nsGkAtoms::div, atCurChild);
       NS_ENSURE_STATE(curDiv);
       // Remember our new block for postprocessing
       mNewBlock = curDiv;
       // Set up the alignment on the div
       rv = AlignBlock(*curDiv, aAlignType, ContentsOnly::yes);
     }
 
     NS_ENSURE_STATE(curNode->IsContent());
@@ -5094,18 +5115,19 @@ HTMLEditRules::AlignBlockContents(nsIDOM
                                                         nsGkAtoms::align,
                                                         *alignType, false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   } else {
     // else we need to put in a div, set the alignment, and toss in all the children
     NS_ENSURE_STATE(mHTMLEditor);
-    RefPtr<Element> divElem = mHTMLEditor->CreateNode(nsGkAtoms::div, node, 0,
-                                                      node->GetFirstChild());
+    EditorRawDOMPoint atStartOfNode(node, 0);
+    RefPtr<Element> divElem =
+      mHTMLEditor->CreateNode(nsGkAtoms::div, atStartOfNode);
     NS_ENSURE_STATE(divElem);
     // set up the alignment on the div
     NS_ENSURE_STATE(mHTMLEditor);
     nsresult rv =
       mHTMLEditor->SetAttributeOrEquivalent(divElem, nsGkAtoms::align,
                                             *alignType, false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
@@ -6613,20 +6635,21 @@ HTMLEditRules::ReturnInHeader(Selection&
     }
     if (!sibling || !sibling->IsHTMLElement(nsGkAtoms::br)) {
       ClearCachedStyles();
       htmlEditor->mTypeInState->ClearAllProps();
 
       // Create a paragraph
       nsAtom& paraAtom = DefaultParagraphSeparator();
       // We want a wrapper element even if we separate with <br>
-      nsCOMPtr<Element> pNode =
-        htmlEditor->CreateNode(&paraAtom == nsGkAtoms::br ? nsGkAtoms::p
-                                                           : &paraAtom,
-                                headerParent, offset + 1);
+      EditorRawDOMPoint nextToHeader(headerParent, offset + 1);
+      RefPtr<Element> pNode =
+        htmlEditor->CreateNode(&paraAtom == nsGkAtoms::br ?
+                                 nsGkAtoms::p : &paraAtom,
+                               nextToHeader);
       NS_ENSURE_STATE(pNode);
 
       // Append a <br> to it
       nsCOMPtr<Element> brNode = htmlEditor->CreateBR(pNode, 0);
       NS_ENSURE_STATE(brNode);
 
       // Set selection to before the break
       rv = aSelection.Collapse(pNode, 0);
@@ -6894,20 +6917,21 @@ HTMLEditRules::ReturnInListItem(Selectio
     } else {
       // Otherwise kill this item
       rv = htmlEditor->DeleteNode(&aListItem);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // Time to insert a paragraph
       nsAtom& paraAtom = DefaultParagraphSeparator();
       // We want a wrapper even if we separate with <br>
-      nsCOMPtr<Element> pNode =
-        htmlEditor->CreateNode(&paraAtom == nsGkAtoms::br ? nsGkAtoms::p
-                                                           : &paraAtom,
-                                listParent, offset + 1);
+      EditorRawDOMPoint atNextListItem(listParent, offset + 1);
+      RefPtr<Element> pNode =
+        htmlEditor->CreateNode(&paraAtom == nsGkAtoms::br ?
+                                 nsGkAtoms::p : &paraAtom,
+                               atNextListItem);
       NS_ENSURE_STATE(pNode);
 
       // Append a <br> to it
       nsCOMPtr<Element> brNode = htmlEditor->CreateBR(pNode, 0);
       NS_ENSURE_STATE(brNode);
 
       // Set selection to before the break
       rv = aSelection.Collapse(pNode, 0);
@@ -6943,19 +6967,21 @@ HTMLEditRules::ReturnInListItem(Selectio
       if (isEmptyNode) {
         RefPtr<nsAtom> nodeAtom = aListItem.NodeInfo()->NameAtom();
         if (nodeAtom == nsGkAtoms::dd || nodeAtom == nsGkAtoms::dt) {
           nsCOMPtr<nsINode> list = aListItem.GetParentNode();
           int32_t itemOffset = list ? list->IndexOf(&aListItem) : -1;
 
           nsAtom* listAtom = nodeAtom == nsGkAtoms::dt ? nsGkAtoms::dd
                                                         : nsGkAtoms::dt;
-          nsCOMPtr<Element> newListItem =
-            htmlEditor->CreateNode(listAtom, list, itemOffset + 1,
-                                   aListItem.GetNextSibling());
+          MOZ_DIAGNOSTIC_ASSERT(itemOffset != -1);
+          EditorRawDOMPoint atNextListItem(list, aListItem.GetNextSibling(),
+                                           itemOffset + 1);
+          RefPtr<Element> newListItem =
+            htmlEditor->CreateNode(listAtom, atNextListItem);
           NS_ENSURE_STATE(newListItem);
           rv = htmlEditor->DeleteNode(&aListItem);
           NS_ENSURE_SUCCESS(rv, rv);
           rv = aSelection.Collapse(newListItem, 0);
           NS_ENSURE_SUCCESS(rv, rv);
 
           return NS_OK;
         }
@@ -7048,18 +7074,18 @@ HTMLEditRules::MakeBlockquote(nsTArray<O
     if (!curBlock) {
       nsCOMPtr<nsINode> curParent = curNode->GetParentNode();
       int32_t offset = curParent ? curParent->IndexOf(curNode) : -1;
       nsCOMPtr<nsIContent> curChild(curNode->AsContent());
       nsresult rv = SplitAsNeeded(*nsGkAtoms::blockquote, curParent, offset,
                                   address_of(curChild));
       NS_ENSURE_SUCCESS(rv, rv);
       NS_ENSURE_STATE(mHTMLEditor);
-      curBlock = mHTMLEditor->CreateNode(nsGkAtoms::blockquote, curParent,
-                                         offset, curChild);
+      EditorRawDOMPoint atCurChild(curParent, curChild, offset);
+      curBlock = mHTMLEditor->CreateNode(nsGkAtoms::blockquote, atCurChild);
       NS_ENSURE_STATE(curBlock);
       // remember our new block for postprocessing
       mNewBlock = curBlock;
       // note: doesn't matter if we set mNewBlock multiple times.
     }
 
     NS_ENSURE_STATE(mHTMLEditor);
     nsresult rv = mHTMLEditor->MoveNode(curNode->AsContent(), curBlock, -1);
@@ -7218,19 +7244,19 @@ HTMLEditRules::ApplyBlockStyle(nsTArray<
         nsresult rv = ApplyBlockStyle(childArray, aBlockTag);
         NS_ENSURE_SUCCESS(rv, rv);
       } else {
         // Make sure we can put a block here
         nsCOMPtr<nsIContent> curChild(curNode->AsContent());
         nsresult rv = SplitAsNeeded(aBlockTag, curParent, offset,
                                     address_of(curChild));
         NS_ENSURE_SUCCESS(rv, rv);
-        nsCOMPtr<Element> theBlock =
-          htmlEditor->CreateNode(&aBlockTag, curParent, offset,
-                                 curChild);
+        EditorRawDOMPoint atCurChild(curParent, curChild, offset);
+        RefPtr<Element> theBlock =
+          htmlEditor->CreateNode(&aBlockTag, atCurChild);
         NS_ENSURE_STATE(theBlock);
         // Remember our new block for postprocessing
         mNewBlock = theBlock;
       }
     } else if (curNode->IsHTMLElement(nsGkAtoms::br)) {
       // If the node is a break, we honor it by putting further nodes in a new
       // parent
       if (curBlock) {
@@ -7240,18 +7266,18 @@ HTMLEditRules::ApplyBlockStyle(nsTArray<
         NS_ENSURE_SUCCESS(rv, rv);
       } else {
         // The break is the first (or even only) node we encountered.  Create a
         // block for it.
         nsCOMPtr<nsIContent> curChild(curNode->AsContent());
         nsresult rv = SplitAsNeeded(aBlockTag, curParent, offset,
                                     address_of(curChild));
         NS_ENSURE_SUCCESS(rv, rv);
-        curBlock = htmlEditor->CreateNode(&aBlockTag, curParent, offset,
-                                          curChild);
+        EditorRawDOMPoint atCurChild(curParent, curChild, offset);
+        curBlock = htmlEditor->CreateNode(&aBlockTag, atCurChild);
         NS_ENSURE_STATE(curBlock);
         // Remember our new block for postprocessing
         mNewBlock = curBlock;
         // Note: doesn't matter if we set mNewBlock multiple times.
         rv = htmlEditor->MoveNode(curNode->AsContent(), curBlock, -1);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     } else if (IsInlineNode(curNode)) {
@@ -7268,18 +7294,18 @@ HTMLEditRules::ApplyBlockStyle(nsTArray<
       }
 
       // If no curBlock, make one
       if (!curBlock) {
         nsCOMPtr<nsIContent> curChild(curNode->AsContent());
         nsresult rv = SplitAsNeeded(aBlockTag, curParent, offset,
                                     address_of(curChild));
         NS_ENSURE_SUCCESS(rv, rv);
-        curBlock = htmlEditor->CreateNode(&aBlockTag, curParent, offset,
-                                          curChild);
+        EditorRawDOMPoint atCurChild(curParent, curChild, offset);
+        curBlock = htmlEditor->CreateNode(&aBlockTag, atCurChild);
         NS_ENSURE_STATE(curBlock);
         // Remember our new block for postprocessing
         mNewBlock = curBlock;
         // Note: doesn't matter if we set mNewBlock multiple times.
       }
 
       if (NS_WARN_IF(!curNode->GetParentNode())) {
         // This is possible due to mutation events, let's not assert
@@ -8513,34 +8539,31 @@ HTMLEditRules::InsertBRIfNeededInternal(
   }
 
   return aInsertMozBR ? CreateMozBR(aNode.AsDOMNode(), 0) :
                         CreateBR(aNode.AsDOMNode(), 0);
 }
 
 NS_IMETHODIMP
 HTMLEditRules::WillCreateNode(const nsAString& aTag,
-                              nsIDOMNode* aParent,
-                              int32_t aPosition)
+                              nsIDOMNode* aNextSiblingOfNewNode)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLEditRules::DidCreateNode(const nsAString& aTag,
-                             nsIDOMNode* aNode,
-                             nsIDOMNode* aParent,
-                             int32_t aPosition,
+                             nsIDOMNode* aNewNode,
                              nsresult aResult)
 {
   if (!mListenerEnabled) {
     return NS_OK;
   }
   // assumption that Join keeps the righthand node
-  nsresult rv = mUtilRange->SelectNode(aNode);
+  nsresult rv = mUtilRange->SelectNode(aNewNode);
   NS_ENSURE_SUCCESS(rv, rv);
   return UpdateDocChangeRange(mUtilRange);
 }
 
 NS_IMETHODIMP
 HTMLEditRules::WillInsertNode(nsIDOMNode* aNode,
                               nsIDOMNode* aParent,
                               int32_t aPosition)
@@ -9023,18 +9046,19 @@ HTMLEditRules::WillAbsolutePosition(Sele
     int32_t offset = aSelection.GetRangeAt(0)->StartOffset();
     nsCOMPtr<nsIContent> child =
       aSelection.GetRangeAt(0)->GetChildAtStartOffset();
 
     // Make sure we can put a block here
     rv = SplitAsNeeded(*nsGkAtoms::div, parent, offset,
                        address_of(child));
     NS_ENSURE_SUCCESS(rv, rv);
-    nsCOMPtr<Element> positionedDiv =
-      htmlEditor->CreateNode(nsGkAtoms::div, parent, offset, child);
+    EditorRawDOMPoint atChild(parent, child, offset);
+    RefPtr<Element> positionedDiv =
+      htmlEditor->CreateNode(nsGkAtoms::div, atChild);
     NS_ENSURE_STATE(positionedDiv);
     // Remember our new block for postprocessing
     mNewBlock = positionedDiv;
     // Delete anything that was in the list of nodes
     while (!arrayOfNodes.IsEmpty()) {
       OwningNonNull<nsINode> curNode = arrayOfNodes[0];
       rv = htmlEditor->DeleteNode(curNode);
       NS_ENSURE_SUCCESS(rv, rv);
@@ -9079,26 +9103,25 @@ HTMLEditRules::WillAbsolutePosition(Sele
       }
 
       if (!curList || (sibling && sibling != curList)) {
         // Create a new nested list of correct type
         rv =
           SplitAsNeeded(*curParent->NodeInfo()->NameAtom(), curParent, offset);
         NS_ENSURE_SUCCESS(rv, rv);
         if (!curPositionedDiv) {
-          nsCOMPtr<nsINode> curParentParent = curParent->GetParentNode();
-          int32_t parentOffset = curParentParent
-            ? curParentParent->IndexOf(curParent) : -1;
-          curPositionedDiv = htmlEditor->CreateNode(nsGkAtoms::div, curParentParent,
-                                                    parentOffset);
+          EditorRawDOMPoint atCurParent(curParent);
+          curPositionedDiv =
+            htmlEditor->CreateNode(nsGkAtoms::div, atCurParent);
           mNewBlock = curPositionedDiv;
         }
+        EditorRawDOMPoint atEndOfCurPositionedDiv(curPositionedDiv,
+                                                  curPositionedDiv->Length());
         curList = htmlEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
-                                         curPositionedDiv, -1,
-                                         curPositionedDiv->GetLastChild());
+                                         atEndOfCurPositionedDiv);
         NS_ENSURE_STATE(curList);
         // curList is now the correct thing to put curNode in.  Remember our
         // new block for postprocessing.
       }
       // Tuck the node into the end of the active list
       rv = htmlEditor->MoveNode(curNode, curList, -1);
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
@@ -9123,28 +9146,25 @@ HTMLEditRules::WillAbsolutePosition(Sele
         }
 
         if (!curList || (sibling && sibling != curList)) {
           // Create a new nested list of correct type
           rv = SplitAsNeeded(*curParent->NodeInfo()->NameAtom(), curParent,
                              offset);
           NS_ENSURE_SUCCESS(rv, rv);
           if (!curPositionedDiv) {
-            nsCOMPtr<nsINode> curParentParent = curParent->GetParentNode();
-            int32_t parentOffset = curParentParent ?
-              curParentParent->IndexOf(curParent) : -1;
-            curPositionedDiv = htmlEditor->CreateNode(nsGkAtoms::div,
-                                                      curParentParent,
-                                                      parentOffset,
-                                                      curParent->AsContent());
+            EditorRawDOMPoint atCurParent(curParent);
+            curPositionedDiv =
+              htmlEditor->CreateNode(nsGkAtoms::div, atCurParent);
             mNewBlock = curPositionedDiv;
           }
+          EditorRawDOMPoint atEndOfCurPositionedDiv(curPositionedDiv,
+                                                    curPositionedDiv->Length());
           curList = htmlEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
-                                           curPositionedDiv, -1,
-                                           curPositionedDiv->GetLastChild());
+                                           atEndOfCurPositionedDiv);
           NS_ENSURE_STATE(curList);
         }
         rv = htmlEditor->MoveNode(listItem, curList, -1);
         NS_ENSURE_SUCCESS(rv, rv);
         // Remember we indented this li
         indentedLI = listItem;
       } else {
         // Need to make a div to put things in if we haven't already
@@ -9155,18 +9175,18 @@ HTMLEditRules::WillAbsolutePosition(Sele
             mNewBlock = curPositionedDiv;
             curList = nullptr;
             continue;
           }
           nsCOMPtr<nsIContent> curChild(curNode);
           rv = SplitAsNeeded(*nsGkAtoms::div, curParent, offset,
                              address_of(curChild));
           NS_ENSURE_SUCCESS(rv, rv);
-          curPositionedDiv = htmlEditor->CreateNode(nsGkAtoms::div, curParent,
-                                                    offset, curChild);
+          EditorRawDOMPoint atCurChild(curParent, curChild, offset);
+          curPositionedDiv = htmlEditor->CreateNode(nsGkAtoms::div, atCurChild);
           NS_ENSURE_STATE(curPositionedDiv);
           // Remember our new block for postprocessing
           mNewBlock = curPositionedDiv;
           // curPositionedDiv is now the correct thing to put curNode in
         }
 
         // Tuck the node into the end of the active blockquote
         rv = htmlEditor->MoveNode(curNode, curPositionedDiv, -1);
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -101,20 +101,19 @@ public:
   nsresult GetListItemState(bool* aMixed, bool* aLI, bool* aDT, bool* aDD);
   nsresult GetIndentState(bool* aCanIndent, bool* aCanOutdent);
   nsresult GetAlignment(bool* aMixed, nsIHTMLEditor::EAlignment* aAlign);
   nsresult GetParagraphState(bool* aMixed, nsAString& outFormat);
   nsresult MakeSureElemStartsOrEndsOnCR(nsINode& aNode);
 
   // nsIEditActionListener methods
 
-  NS_IMETHOD WillCreateNode(const nsAString& aTag, nsIDOMNode* aParent,
-                            int32_t aPosition) override;
-  NS_IMETHOD DidCreateNode(const nsAString& aTag, nsIDOMNode* aNode,
-                           nsIDOMNode* aParent, int32_t aPosition,
+  NS_IMETHOD WillCreateNode(const nsAString& aTag,
+                            nsIDOMNode* aNextSiblingOfNewNode) override;
+  NS_IMETHOD DidCreateNode(const nsAString& aTag, nsIDOMNode* aNewNode,
                            nsresult aResult) override;
   NS_IMETHOD WillInsertNode(nsIDOMNode* aNode, nsIDOMNode* aParent,
                             int32_t aPosition) override;
   NS_IMETHOD DidInsertNode(nsIDOMNode* aNode, nsIDOMNode* aParent,
                            int32_t aPosition, nsresult aResult) override;
   NS_IMETHOD WillDeleteNode(nsIDOMNode* aChild) override;
   NS_IMETHOD DidDeleteNode(nsIDOMNode* aChild, nsresult aResult) override;
   NS_IMETHOD WillSplitNode(nsIDOMNode* aExistingRightNode,
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "mozilla/HTMLEditor.h"
 
 #include "mozilla/DebugOnly.h"
+#include "mozilla/EditorDOMPoint.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/TextEvents.h"
 
 #include "nsCRT.h"
 
 #include "nsUnicharUtils.h"
 
 #include "HTMLEditorEventListener.h"
@@ -2016,21 +2017,23 @@ HTMLEditor::MakeOrChangeList(const nsASt
         // we need to split up to the child of parent
         offset = SplitNodeDeep(*topChild, *node, offset,
                                EmptyContainers::yes, nullptr, nullptr,
                                address_of(child));
         NS_ENSURE_STATE(offset != -1);
       }
 
       // make a list
-      nsCOMPtr<Element> newList = CreateNode(listAtom, parent, offset, child);
+      MOZ_DIAGNOSTIC_ASSERT(child);
+      EditorRawDOMPoint atChild(parent, child, offset);
+      RefPtr<Element> newList = CreateNode(listAtom, atChild);
       NS_ENSURE_STATE(newList);
       // make a list item
-      nsCOMPtr<Element> newItem = CreateNode(nsGkAtoms::li, newList, 0,
-                                             newList->GetFirstChild());
+      EditorRawDOMPoint atStartOfNewList(newList, 0);
+      RefPtr<Element> newItem = CreateNode(nsGkAtoms::li, atStartOfNewList);
       NS_ENSURE_STATE(newItem);
       rv = selection->Collapse(newItem, 0);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   return rules->DidDoAction(selection, &ruleInfo, rv);
 }
@@ -2159,17 +2162,19 @@ HTMLEditor::InsertBasicBlock(const nsASt
         // we need to split up to the child of parent
         offset = SplitNodeDeep(*topChild, *node, offset,
                                EmptyContainers::yes, nullptr, nullptr,
                                address_of(child));
         NS_ENSURE_STATE(offset != -1);
       }
 
       // make a block
-      nsCOMPtr<Element> newBlock = CreateNode(blockAtom, parent, offset, child);
+      MOZ_DIAGNOSTIC_ASSERT(child);
+      EditorRawDOMPoint atChild(parent, child, offset);
+      RefPtr<Element> newBlock = CreateNode(blockAtom, atChild);
       NS_ENSURE_STATE(newBlock);
 
       // reposition selection to inside the block
       rv = selection->Collapse(newBlock, 0);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
@@ -2233,18 +2238,19 @@ HTMLEditor::Indent(const nsAString& aInd
           // we need to split up to the child of parent
           offset = SplitNodeDeep(*topChild, *node, offset,
                                  EmptyContainers::yes, nullptr, nullptr,
                                  address_of(child));
           NS_ENSURE_STATE(offset != -1);
         }
 
         // make a blockquote
-        nsCOMPtr<Element> newBQ =
-          CreateNode(nsGkAtoms::blockquote, parent, offset, child);
+        MOZ_DIAGNOSTIC_ASSERT(child);
+        EditorRawDOMPoint atChild(parent, child, offset);
+        RefPtr<Element> newBQ = CreateNode(nsGkAtoms::blockquote, atChild);
         NS_ENSURE_STATE(newBQ);
         // put a space in it so layout will draw the list item
         rv = selection->Collapse(newBQ, 0);
         NS_ENSURE_SUCCESS(rv, rv);
         rv = InsertText(NS_LITERAL_STRING(" "));
         NS_ENSURE_SUCCESS(rv, rv);
         // reposition selection to before the space character
         NS_ENSURE_STATE(selection->GetRangeAt(0));
@@ -4527,19 +4533,19 @@ HTMLEditor::CopyLastEditableChildStyles(
   while (childElement && (childElement != aPreviousBlock)) {
     if (HTMLEditUtils::IsInlineStyle(childElement) ||
         childElement->IsHTMLElement(nsGkAtoms::span)) {
       if (newStyles) {
         newStyles = InsertContainerAbove(newStyles,
                                          childElement->NodeInfo()->NameAtom());
         NS_ENSURE_STATE(newStyles);
       } else {
+        EditorRawDOMPoint atStartOfNewBlock(newBlock, 0);
         deepestStyle = newStyles =
-          CreateNode(childElement->NodeInfo()->NameAtom(), newBlock, 0,
-                     newBlock->GetFirstChild());
+          CreateNode(childElement->NodeInfo()->NameAtom(), atStartOfNewBlock);
         NS_ENSURE_STATE(newStyles);
       }
       CloneAttributes(newStyles, childElement);
     }
     childElement = childElement->GetParentElement();
   }
   if (deepestStyle) {
     RefPtr<Element> retVal = CreateBR(deepestStyle, 0);
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -237,17 +237,18 @@ TextEditor::SetDocumentCharacterSet(cons
   }
 
   nsCOMPtr<nsIContent> headNode = headList->Item(0);
   if (NS_WARN_IF(!headNode)) {
     return NS_OK;
   }
 
   // Create a new meta charset tag
-  RefPtr<Element> metaElement = CreateNode(nsGkAtoms::meta, headNode, 0);
+  EditorRawDOMPoint atStartOfHeadNode(headNode, 0);
+  RefPtr<Element> metaElement = CreateNode(nsGkAtoms::meta, atStartOfHeadNode);
   if (NS_WARN_IF(!metaElement)) {
     return NS_OK;
   }
 
   // Set attributes to the created element
   if (characterSet.IsEmpty()) {
     return NS_OK;
   }
@@ -438,42 +439,45 @@ TextEditor::CreateBRImpl(nsCOMPtr<nsIDOM
   NS_ENSURE_TRUE(aInOutParent && *aInOutParent && aInOutOffset && outBRNode, NS_ERROR_NULL_POINTER);
   *outBRNode = nullptr;
 
   // we need to insert a br.  unfortunately, we may have to split a text node to do it.
   nsCOMPtr<nsINode> node = do_QueryInterface(*aInOutParent);
   int32_t theOffset = *aInOutOffset;
   RefPtr<Element> brNode;
   if (IsTextNode(node)) {
-    int32_t offset;
-    nsCOMPtr<nsINode> tmp = GetNodeLocation(node, &offset);
-    NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
+    EditorRawDOMPoint atNode(node);
+    if (NS_WARN_IF(!atNode.IsSetAndValid())) {
+      return NS_ERROR_FAILURE;
+    }
     if (!theOffset) {
       // we are already set to go
     } else if (theOffset == static_cast<int32_t>(node->Length())) {
       // update offset to point AFTER the text node
-      offset++;
+      atNode.AdvanceOffset();
     } else {
       // split the text node
       ErrorResult rv;
       SplitNode(*node->AsContent(), theOffset, rv);
       if (NS_WARN_IF(rv.Failed())) {
         return rv.StealNSResult();
       }
-      tmp = GetNodeLocation(node, &offset);
+      atNode.Clear();
+      atNode.Set(node);
     }
     // create br
-    brNode = CreateNode(nsGkAtoms::br, tmp, offset);
+    brNode = CreateNode(nsGkAtoms::br, atNode);
     if (NS_WARN_IF(!brNode)) {
       return NS_ERROR_FAILURE;
     }
-    *aInOutParent = GetAsDOMNode(tmp);
-    *aInOutOffset = offset+1;
+    *aInOutParent = GetAsDOMNode(atNode.Container());
+    *aInOutOffset = atNode.Offset() + 1;
   } else {
-    brNode = CreateNode(nsGkAtoms::br, node, theOffset);
+    EditorRawDOMPoint atTheOffset(node, theOffset);
+    brNode = CreateNode(nsGkAtoms::br, atTheOffset);
     if (NS_WARN_IF(!brNode)) {
       return NS_ERROR_FAILURE;
     }
     (*aInOutOffset)++;
   }
 
   *outBRNode = GetAsDOMNode(brNode);
   if (*outBRNode && (aSelect != eNone)) {
--- a/editor/nsIEditActionListener.idl
+++ b/editor/nsIEditActionListener.idl
@@ -25,41 +25,33 @@ Editor Action Listener interface to outs
  * nsIDocumentObserver.
  */
 [scriptable, uuid(b22907b1-ee93-11d2-8d50-000064657374)]
 
 interface nsIEditActionListener : nsISupports{
 
   /**
    * Called before the editor creates a node.
-   * @param aTag      The tag name of the DOM Node to create.
-   * @param aParent   The node to insert the new object into
-   * @param aPosition The place in aParent to insert the new node
-   *                  0=first child, 1=second child, etc.
-   *                  any number > number of current children = last child
+   * @param aTag                    The tag name of the DOM Node to create.
+   * @param aNextSiblingOfNewNode   The node which will be next sibling of
+   *                                new node.  If the new node will be appended,
+   *                                this is null.
    */
   void WillCreateNode(in DOMString aTag,
-                            in nsIDOMNode   aParent,
-                            in long aPosition);
+                      in nsIDOMNode aNextSiblingOfNewNode);
 
   /**
    * Called after the editor creates a node.
    * @param aTag      The tag name of the DOM Node to create.
-   * @param aNode     The DOM Node that was created.
-   * @param aParent   The node to insert the new object into
-   * @param aPosition The place in aParent to insert the new node
-   *                  0=first child, 1=second child, etc.
-   *                  any number > number of current children = last child
+   * @param aNewNode  The DOM Node that was created.
    * @param aResult   The result of the create node operation.
    */
   void DidCreateNode(in DOMString aTag,
-                           in nsIDOMNode aNode,
-                           in nsIDOMNode aParent,
-                           in long          aPosition,
-                           in nsresult      aResult);
+                     in nsIDOMNode aNewNode,
+                     in nsresult aResult);
 
   /**
    * Called before the editor inserts a node.
    * @param aNode     The DOM Node to insert.
    * @param aParent   The node to insert the new object into
    * @param aPosition The place in aParent to insert the new node
    *                  0=first child, 1=second child, etc.
    *                  any number > number of current children = last child
--- a/editor/txtsvc/nsTextServicesDocument.cpp
+++ b/editor/txtsvc/nsTextServicesDocument.cpp
@@ -3368,23 +3368,26 @@ nsTextServicesDocument::WillJoinNodes(ns
   return NS_OK;
 }
 
 // -------------------------------
 // stubs for unused listen methods
 // -------------------------------
 
 NS_IMETHODIMP
-nsTextServicesDocument::WillCreateNode(const nsAString& aTag, nsIDOMNode *aParent, int32_t aPosition)
+nsTextServicesDocument::WillCreateNode(const nsAString& aTag,
+                                       nsIDOMNode* aNextSiblingOfNewNode)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsTextServicesDocument::DidCreateNode(const nsAString& aTag, nsIDOMNode *aNode, nsIDOMNode *aParent, int32_t aPosition, nsresult aResult)
+nsTextServicesDocument::DidCreateNode(const nsAString& aTag,
+                                      nsIDOMNode* aNewNode,
+                                      nsresult aResult)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsTextServicesDocument::WillInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString)
 {
   return NS_OK;
--- a/editor/txtsvc/nsTextServicesDocument.h
+++ b/editor/txtsvc/nsTextServicesDocument.h
@@ -113,18 +113,21 @@ public:
   NS_IMETHOD WillJoinNodes(nsIDOMNode  *aLeftNode,
                            nsIDOMNode  *aRightNode,
                            nsIDOMNode  *aParent) override;
   NS_IMETHOD DidJoinNodes(nsIDOMNode  *aLeftNode,
                           nsIDOMNode  *aRightNode,
                           nsIDOMNode  *aParent,
                           nsresult     aResult) override;
   // these listen methods are unused:
-  NS_IMETHOD WillCreateNode(const nsAString& aTag, nsIDOMNode *aParent, int32_t aPosition) override;
-  NS_IMETHOD DidCreateNode(const nsAString& aTag, nsIDOMNode *aNode, nsIDOMNode *aParent, int32_t aPosition, nsresult aResult) override;
+  NS_IMETHOD WillCreateNode(const nsAString& aTag,
+                            nsIDOMNode* aNextSiblingOfNewNode) override;
+  NS_IMETHOD DidCreateNode(const nsAString& aTag,
+                           nsIDOMNode* aNewNode,
+                           nsresult aResult) override;
   NS_IMETHOD WillInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString) override;
   NS_IMETHOD DidInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString, nsresult aResult) override;
   NS_IMETHOD WillDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength) override;
   NS_IMETHOD DidDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength, nsresult aResult) override;
   NS_IMETHOD WillDeleteSelection(nsISelection *aSelection) override;
   NS_IMETHOD DidDeleteSelection(nsISelection *aSelection) override;
 
   /* Helper functions */
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -1049,23 +1049,27 @@ mozInlineSpellChecker::IgnoreWords(const
     mSpellCheck->IgnoreWordAllOccurrences(aWordsToIgnore[index]);
 
   auto status = MakeUnique<mozInlineSpellStatus>(this);
   nsresult rv = status->InitForSelection();
   NS_ENSURE_SUCCESS(rv, rv);
   return ScheduleSpellCheck(Move(status));
 }
 
-NS_IMETHODIMP mozInlineSpellChecker::WillCreateNode(const nsAString & aTag, nsIDOMNode *aParent, int32_t aPosition)
+NS_IMETHODIMP
+mozInlineSpellChecker::WillCreateNode(const nsAString& aTag,
+                                      nsIDOMNode* aNextSiblingOfNewNode)
 {
   return NS_OK;
 }
 
-NS_IMETHODIMP mozInlineSpellChecker::DidCreateNode(const nsAString & aTag, nsIDOMNode *aNode, nsIDOMNode *aParent,
-                                                   int32_t aPosition, nsresult aResult)
+NS_IMETHODIMP
+mozInlineSpellChecker::DidCreateNode(const nsAString& aTag,
+                                     nsIDOMNode* aNewNode,
+                                     nsresult aResult)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP mozInlineSpellChecker::WillInsertNode(nsIDOMNode *aNode, nsIDOMNode *aParent,
                                                     int32_t aPosition)
 {
   return NS_OK;
deleted file mode 100644
--- a/js/src/ds/FixedSizeHash.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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 jsfixedsizehash_h_
-#define jsfixedsizehash_h_
-
-#include "ds/LifoAlloc.h"
-
-namespace js {
-
-/*
- * Class representing a hash set with fixed capacity, with newer entries
- * evicting older entries. Each entry has several hashes and can be stored in
- * different buckets, with the choice of which to evict on insertion being
- * managed via LRU. For tables with a relatively small size, using different
- * hashes increases utilization and makes it less likely that entries will keep
- * evicting each other due to wanting to use the same bucket.
- *
- * T indicates the type of hash elements, HashPolicy must have the following
- * contents:
- *
- * Lookup - As for HashMap / HashSet.
- *
- * bool match(T, Lookup) - As for HashMap / HashSet.
- *
- * NumHashes - Number of different hashes generated for each entry.
- *
- * void hash(Lookup, HashNumber[NumHashes]) - Compute all hashes for an entry.
- *
- * void clear(T*) - Clear an entry, such that isCleared() holds afterwards.
- *
- * bool isCleared(T) - Test whether an entry has been cleared.
- */
-template <class T, class HashPolicy, size_t Capacity>
-class FixedSizeHashSet
-{
-    T entries[Capacity];
-    uint32_t lastOperations[Capacity];
-    uint32_t numOperations;
-
-    static const size_t NumHashes = HashPolicy::NumHashes;
-
-    static_assert(Capacity > 0, "an empty fixed-size hash set is meaningless");
-
-  public:
-    typedef typename HashPolicy::Lookup Lookup;
-
-    FixedSizeHashSet()
-      : entries(), lastOperations(), numOperations(0)
-    {
-        MOZ_ASSERT(HashPolicy::isCleared(entries[0]));
-    }
-
-    bool lookup(const Lookup& lookup, T* pentry)
-    {
-        size_t bucket;
-        if (lookupReference(lookup, &bucket)) {
-            *pentry = entries[bucket];
-            lastOperations[bucket] = numOperations++;
-            return true;
-        }
-        return false;
-    }
-
-    void insert(const Lookup& lookup, const T& entry)
-    {
-        size_t buckets[NumHashes];
-        getBuckets(lookup, buckets);
-
-        size_t min = buckets[0];
-        for (size_t i = 0; i < NumHashes; i++) {
-            const T& entry = entries[buckets[i]];
-            if (HashPolicy::isCleared(entry)) {
-                entries[buckets[i]] = entry;
-                lastOperations[buckets[i]] = numOperations++;
-                return;
-            }
-            if (i && lastOperations[min] > lastOperations[buckets[i]])
-                min = buckets[i];
-        }
-
-        entries[min] = entry;
-        lastOperations[min] = numOperations++;
-    }
-
-    template <typename S>
-    void remove(const S& s)
-    {
-        size_t bucket;
-        if (lookupReference(s, &bucket))
-            HashPolicy::clear(&entries[bucket]);
-    }
-
-  private:
-    template <typename S>
-    bool lookupReference(const S& s, size_t* pbucket)
-    {
-        size_t buckets[NumHashes];
-        getBuckets(s, buckets);
-
-        for (size_t i = 0; i < NumHashes; i++) {
-            const T& entry = entries[buckets[i]];
-            if (!HashPolicy::isCleared(entry) && HashPolicy::match(entry, s)) {
-                *pbucket = buckets[i];
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    template <typename S>
-    void getBuckets(const S& s, size_t buckets[NumHashes])
-    {
-        HashNumber hashes[NumHashes];
-        HashPolicy::hash(s, hashes);
-
-        for (size_t i = 0; i < NumHashes; i++)
-            buckets[i] = hashes[i] % Capacity;
-    }
-};
-
-}  /* namespace js */
-
-#endif /* jsfixedsizehash_h_ */
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/regress/nop-fill-jit-exit.js
@@ -0,0 +1,28 @@
+// |jit-test| --arm-asm-nop-fill=1
+//
+try {
+    enableSingleStepProfiling();
+    disableSingleStepProfiling();
+} catch (e) {
+    // Early quit on plateforms not supporting single step profiling.
+    quit();
+}
+
+load(libdir + "asm.js");
+
+var ffi = function(enable) {
+    enableGeckoProfiling();
+    enableSingleStepProfiling();
+}
+var f = asmLink(asmCompile('global', 'ffis',
+  USE_ASM + `
+    var ffi=ffis.ffi;
+    function f(i) {
+      i=i|0;
+      ffi(i|0);
+    } return f
+  `), null, {
+    ffi
+});
+f(0);
+f(+1);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1587,44 +1587,16 @@ JSFunction::createScriptForLazilyInterpr
             script = lazy->functionNonDelazifying()->nonLazyScript();
             if (!script)
                 return false;
 
             fun->setUnlazifiedScript(script);
             return true;
         }
 
-        // Lazy script caching is only supported for leaf functions. If a
-        // script with inner functions was returned by the cache, those inner
-        // functions would be delazified when deep cloning the script, even if
-        // they have never executed.
-        //
-        // Additionally, the lazy script cache is not used during incremental
-        // GCs, to avoid resurrecting dead scripts after incremental sweeping
-        // has started.
-        if (canRelazify && !JS::IsIncrementalGCInProgress(cx)) {
-            LazyScriptCache::Lookup lookup(cx, lazy);
-            cx->caches().lazyScriptCache.lookup(lookup, script.address());
-        }
-
-        if (script) {
-            RootedScope enclosingScope(cx, lazy->enclosingScope());
-            RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, enclosingScope, fun, script));
-            if (!clonedScript)
-                return false;
-
-            clonedScript->setSourceObject(lazy->sourceObject());
-
-            fun->initAtom(script->functionNonDelazifying()->displayAtom());
-
-            if (!lazy->maybeScript())
-                lazy->initScript(clonedScript);
-            return true;
-        }
-
         MOZ_ASSERT(lazy->scriptSource()->hasSourceData());
 
         // Parse and compile the script from source.
         size_t lazyLength = lazy->end() - lazy->begin();
         UncompressedSourceCache::AutoHoldEntry holder;
         ScriptSource::PinnedChars chars(cx, lazy->scriptSource(), holder,
                                         lazy->begin(), lazyLength);
         if (!chars.get())
@@ -1649,19 +1621,16 @@ JSFunction::createScriptForLazilyInterpr
 
         // Try to insert the newly compiled script into the lazy script cache.
         if (canRelazify) {
             // A script's starting column isn't set by the bytecode emitter, so
             // specify this from the lazy script so that if an identical lazy
             // script is encountered later a match can be determined.
             script->setColumn(lazy->column());
 
-            LazyScriptCache::Lookup lookup(cx, lazy);
-            cx->caches().lazyScriptCache.insert(lookup, script);
-
             // Remember the lazy script on the compiled script, so it can be
             // stored on the function again in case of re-lazification.
             // Only functions without inner functions are re-lazified.
             script->setLazyScript(lazy);
         }
 
         // XDR the newly delazified function.
         if (script->scriptSource()->hasEncoder()) {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3200,18 +3200,16 @@ JSScript::finalize(FreeOp* fop)
     if (data) {
         JS_POISON(data, 0xdb, computedSizeOfData());
         fop->free_(data);
     }
 
     if (scriptData_)
         scriptData_->decRefCount();
 
-    fop->runtime()->caches().lazyScriptCache.remove(this);
-
     // In most cases, our LazyScript's script pointer will reference this
     // script, and thus be nulled out by normal weakref processing. However, if
     // we unlazified the LazyScript during incremental sweeping, it will have a
     // completely different JSScript.
     MOZ_ASSERT_IF(lazyScript && !IsAboutToBeFinalizedUnbarriered(&lazyScript),
                   !lazyScript->hasScript() || lazyScript->maybeScriptUnbarriered() != this);
 }
 
@@ -4524,86 +4522,16 @@ JSScript::hasLoops()
 }
 
 bool
 JSScript::mayReadFrameArgsDirectly()
 {
     return argumentsHasVarBinding() || hasRest();
 }
 
-static inline void
-LazyScriptHash(uint32_t lineno, uint32_t column, uint32_t begin, uint32_t end,
-               HashNumber hashes[3])
-{
-    HashNumber hash = lineno;
-    hash = RotateLeft(hash, 4) ^ column;
-    hash = RotateLeft(hash, 4) ^ begin;
-    hash = RotateLeft(hash, 4) ^ end;
-
-    hashes[0] = hash;
-    hashes[1] = RotateLeft(hashes[0], 4) ^ begin;
-    hashes[2] = RotateLeft(hashes[1], 4) ^ end;
-}
-
-void
-LazyScriptHashPolicy::hash(const Lookup& lookup, HashNumber hashes[3])
-{
-    LazyScript* lazy = lookup.lazy;
-    LazyScriptHash(lazy->lineno(), lazy->column(), lazy->begin(), lazy->end(), hashes);
-}
-
-void
-LazyScriptHashPolicy::hash(JSScript* script, HashNumber hashes[3])
-{
-    LazyScriptHash(script->lineno(), script->column(), script->sourceStart(), script->sourceEnd(), hashes);
-}
-
-bool
-LazyScriptHashPolicy::match(JSScript* script, const Lookup& lookup)
-{
-    JSContext* cx = lookup.cx;
-    LazyScript* lazy = lookup.lazy;
-
-    // To be a match, the script and lazy script need to have the same line
-    // and column and to be at the same position within their respective
-    // source blobs, and to have the same source contents and version.
-    //
-    // While the surrounding code in the source may differ, this is
-    // sufficient to ensure that compiling the lazy script will yield an
-    // identical result to compiling the original script.
-    //
-    // Note that the filenames and origin principals of the lazy script and
-    // original script can differ. If there is a match, these will be fixed
-    // up in the resulting clone by the caller.
-
-    if (script->lineno() != lazy->lineno() ||
-        script->column() != lazy->column() ||
-        script->getVersion() != lazy->version() ||
-        script->sourceStart() != lazy->begin() ||
-        script->sourceEnd() != lazy->end())
-    {
-        return false;
-    }
-
-    UncompressedSourceCache::AutoHoldEntry holder;
-
-    size_t scriptBegin = script->sourceStart();
-    size_t length = script->sourceEnd() - scriptBegin;
-    ScriptSource::PinnedChars scriptChars(cx, script->scriptSource(), holder, scriptBegin, length);
-    if (!scriptChars.get())
-        return false;
-
-    MOZ_ASSERT(scriptBegin == lazy->begin());
-    ScriptSource::PinnedChars lazyChars(cx, lazy->scriptSource(), holder, scriptBegin, length);
-    if (!lazyChars.get())
-        return false;
-
-    return !memcmp(scriptChars.get(), lazyChars.get(), length);
-}
-
 void
 JSScript::AutoDelazify::holdScript(JS::HandleFunction fun)
 {
     if (fun) {
         if (fun->compartment()->isSelfHosting) {
             // The self-hosting compartment is shared across runtimes, so we
             // can't use JSAutoCompartment: it could cause races. Functions in
             // the self-hosting compartment will never be lazy, so we can safely
--- a/js/src/vm/Caches.h
+++ b/js/src/vm/Caches.h
@@ -8,17 +8,16 @@
 #define vm_Caches_h
 
 #include "jsatom.h"
 #include "jsbytecode.h"
 #include "jsmath.h"
 #include "jsobj.h"
 #include "jsscript.h"
 
-#include "ds/FixedSizeHash.h"
 #include "frontend/SourceNotes.h"
 #include "gc/Tracer.h"
 #include "js/RootingAPI.h"
 #include "js/UniquePtr.h"
 #include "vm/NativeObject.h"
 
 namespace js {
 
@@ -81,43 +80,16 @@ struct EvalCacheHashPolicy
     typedef EvalCacheLookup Lookup;
 
     static HashNumber hash(const Lookup& l);
     static bool match(const EvalCacheEntry& entry, const EvalCacheLookup& l);
 };
 
 typedef HashSet<EvalCacheEntry, EvalCacheHashPolicy, SystemAllocPolicy> EvalCache;
 
-struct LazyScriptHashPolicy
-{
-    struct Lookup {
-        JSContext* cx;
-        LazyScript* lazy;
-
-        Lookup(JSContext* cx, LazyScript* lazy)
-          : cx(cx), lazy(lazy)
-        {}
-    };
-
-    static const size_t NumHashes = 3;
-
-    static void hash(const Lookup& lookup, HashNumber hashes[NumHashes]);
-    static bool match(JSScript* script, const Lookup& lookup);
-
-    // Alternate methods for use when removing scripts from the hash without an
-    // explicit LazyScript lookup.
-    static void hash(JSScript* script, HashNumber hashes[NumHashes]);
-    static bool match(JSScript* script, JSScript* lookup) { return script == lookup; }
-
-    static void clear(JSScript** pscript) { *pscript = nullptr; }
-    static bool isCleared(JSScript* script) { return !script; }
-};
-
-typedef FixedSizeHashSet<JSScript*, LazyScriptHashPolicy, 769> LazyScriptCache;
-
 /*
  * Cache for speeding up repetitive creation of objects in the VM.
  * When an object is created which matches the criteria in the 'key' section
  * below, an entry is filled with the resulting object.
  */
 class NewObjectCache
 {
     /* Statically asserted to be equal to sizeof(JSObject_Slots16) */
@@ -251,17 +223,16 @@ class RuntimeCaches
     js::MathCache* createMathCache(JSContext* cx);
 
   public:
     js::GSNCache gsnCache;
     js::EnvironmentCoordinateNameCache envCoordinateNameCache;
     js::NewObjectCache newObjectCache;
     js::UncompressedSourceCache uncompressedSourceCache;
     js::EvalCache evalCache;
-    LazyScriptCache lazyScriptCache;
 
     bool init();
 
     js::MathCache* getMathCache(JSContext* cx) {
         return mathCache_ ? mathCache_.get() : createMathCache(cx);
     }
     js::MathCache* maybeGetMathCache() {
         return mathCache_.get();
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -23,17 +23,16 @@
 #include "jsscript.h"
 
 #ifdef XP_DARWIN
 # include "wasm/WasmSignalHandlers.h"
 #endif
 #include "builtin/AtomicsObject.h"
 #include "builtin/Intl.h"
 #include "builtin/Promise.h"
-#include "ds/FixedSizeHash.h"
 #include "frontend/NameCollections.h"
 #include "gc/GCRuntime.h"
 #include "gc/Tracer.h"
 #include "gc/ZoneGroup.h"
 #include "irregexp/RegExpStack.h"
 #include "js/Debug.h"
 #include "js/GCVector.h"
 #include "js/HashTable.h"
--- a/js/src/wasm/WasmTypes.cpp
+++ b/js/src/wasm/WasmTypes.cpp
@@ -754,16 +754,18 @@ CodeRange::CodeRange(uint32_t funcIndex,
     kind_(ImportJitExit)
 {
     MOZ_ASSERT(isImportJitExit());
     MOZ_ASSERT(begin_ < ret_);
     MOZ_ASSERT(ret_ < end_);
     u.funcIndex_ = funcIndex;
     u.jitExit.beginToUntrustedFPStart_ = offsets.untrustedFPStart - begin_;
     u.jitExit.beginToUntrustedFPEnd_ = offsets.untrustedFPEnd - begin_;
+    MOZ_ASSERT(jitExitUntrustedFPStart() == offsets.untrustedFPStart);
+    MOZ_ASSERT(jitExitUntrustedFPEnd() == offsets.untrustedFPEnd);
 }
 
 CodeRange::CodeRange(Trap trap, CallableOffsets offsets)
   : begin_(offsets.begin),
     ret_(offsets.ret),
     end_(offsets.end),
     kind_(TrapExit)
 {
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -1016,18 +1016,18 @@ class CodeRange
             uint32_t funcIndex_;
             union {
                 struct {
                     uint32_t lineOrBytecode_;
                     uint8_t beginToNormalEntry_;
                     uint8_t beginToTierEntry_;
                 } func;
                 struct {
-                    uint8_t beginToUntrustedFPStart_;
-                    uint8_t beginToUntrustedFPEnd_;
+                    uint16_t beginToUntrustedFPStart_;
+                    uint16_t beginToUntrustedFPEnd_;
                 } jitExit;
             };
         };
         Trap trap_;
     } u;
     Kind kind_ : 8;
 
   public:
--- a/layout/forms/nsListControlFrame.cpp
+++ b/layout/forms/nsListControlFrame.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nscore.h"
 #include "nsCOMPtr.h"
 #include "nsUnicharUtils.h"
 #include "nsListControlFrame.h"
 #include "nsCheckboxRadioFrame.h" // for COMPARE macro
 #include "nsGkAtoms.h"
-#include "nsIDOMHTMLOptionElement.h"
 #include "nsComboboxControlFrame.h"
 #include "nsIPresShell.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsIXULRuntime.h"
 #include "nsFontMetrics.h"
 #include "nsIScrollableFrame.h"
 #include "nsCSSRendering.h"
 #include "nsIDOMEventListener.h"
@@ -226,22 +225,19 @@ void nsListControlFrame::PaintFocus(Draw
       fRect.width = CalcFallbackRowBSize(inflation);
       fRect.height = GetScrollPortRect().height;
     }
     fRect.MoveBy(containerFrame->GetOffsetTo(this));
   }
   fRect += aPt;
 
   bool lastItemIsSelected = false;
-  if (focusedContent) {
-    nsCOMPtr<nsIDOMHTMLOptionElement> domOpt =
-      do_QueryInterface(focusedContent);
-    if (domOpt) {
-      domOpt->GetSelected(&lastItemIsSelected);
-    }
+  HTMLOptionElement* domOpt = HTMLOptionElement::FromContentOrNull(focusedContent);
+  if (domOpt) {
+    lastItemIsSelected = domOpt->Selected();
   }
 
   // set up back stop colors and then ask L&F service for the real colors
   nscolor color =
     LookAndFeel::GetColor(lastItemIsSelected ?
                             LookAndFeel::eColorID_WidgetSelectForeground :
                             LookAndFeel::eColorID_WidgetSelectBackground);
 
@@ -1852,17 +1848,18 @@ nsListControlFrame::MouseDown(nsIDOMEven
   } else {
     // NOTE: the combo box is responsible for dropping it down
     if (mComboboxFrame) {
       // Ignore the click that occurs on the option element when one is
       // selected from the parent process popup.
       if (mComboboxFrame->IsOpenInParentProcess()) {
         nsCOMPtr<nsIDOMEventTarget> etarget;
         aMouseEvent->GetTarget(getter_AddRefs(etarget));
-        nsCOMPtr<nsIDOMHTMLOptionElement> option = do_QueryInterface(etarget);
+        nsCOMPtr<nsIContent> econtent = do_QueryInterface(etarget);
+        HTMLOptionElement* option = HTMLOptionElement::FromContentOrNull(econtent);
         if (option) {
           return NS_OK;
         }
       }
 
       uint16_t inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
       if (NS_FAILED(mouseEvent->GetMozInputSource(&inputSource))) {
         return NS_ERROR_FAILURE;
@@ -2448,21 +2445,21 @@ nsListControlFrame::KeyPress(nsIDOMEvent
     uint32_t index = (i + startIndex) % numOptions;
     RefPtr<dom::HTMLOptionElement> optionElement =
       options->ItemAsOption(index);
     if (!optionElement || !optionElement->GetPrimaryFrame()) {
       continue;
     }
 
     nsAutoString text;
-    if (NS_FAILED(optionElement->GetText(text)) ||
-        !StringBeginsWith(
-           nsContentUtils::TrimWhitespace<
-             nsContentUtils::IsHTMLWhitespaceOrNBSP>(text, false),
-           incrementalString, nsCaseInsensitiveStringComparator())) {
+    optionElement->GetText(text);
+    if (!StringBeginsWith(
+          nsContentUtils::TrimWhitespace<
+          nsContentUtils::IsHTMLWhitespaceOrNBSP>(text, false),
+          incrementalString, nsCaseInsensitiveStringComparator())) {
       continue;
     }
 
     bool wasChanged = PerformSelection(index, keyEvent->IsShift(), isControlOrMeta);
     if (!weakFrame.IsAlive()) {
       return NS_OK;
     }
     if (!wasChanged) {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/publicsuffix/PublicSuffixPatterns.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/publicsuffix/PublicSuffixPatterns.java
@@ -34,17 +34,17 @@ class PublicSuffixPatterns {
                     new BufferedInputStream(context.getAssets().open("publicsuffixlist"))));
 
             String line;
             while ((line = reader.readLine()) != null) {
                 EXACT.add(line);
             }
 
         } catch (IOException e) {
-            Log.e("Patterns", "IOException during loading public suffix list");
+            throw new IllegalStateException("resource publicsuffixlist could not be opened but is bundled with app", e);
         } finally {
             IOUtils.safeStreamClose(reader);
         }
 
         return EXACT;
     }
 
 
--- a/python/mach/mach/logging.py
+++ b/python/mach/mach/logging.py
@@ -156,22 +156,22 @@ class LoggingManager(object):
         self.root_logger = logging.getLogger()
         self.root_logger.setLevel(logging.DEBUG)
 
         # Installing NullHandler on the root logger ensures that *all* log
         # messages have at least one handler. This prevents Python from
         # complaining about "no handlers could be found for logger XXX."
         self.root_logger.addHandler(logging.NullHandler())
 
-        self.mach_logger = logging.getLogger('mach')
-        self.mach_logger.setLevel(logging.DEBUG)
+        mach_logger = logging.getLogger('mach')
+        mach_logger.setLevel(logging.DEBUG)
 
         self.structured_filter = ConvertToStructuredFilter()
 
-        self.structured_loggers = [self.mach_logger]
+        self.structured_loggers = [mach_logger]
 
         self._terminal = None
 
     @property
     def terminal(self):
         if not self._terminal and blessings:
             # Sometimes blessings fails to set up the terminal. In that case,
             # silently fail.
@@ -251,15 +251,48 @@ class LoggingManager(object):
             self.root_logger.addHandler(self.terminal_handler)
 
     def disable_unstructured(self):
         """Disable logging of unstructured messages."""
         if self.terminal_handler:
             self.terminal_handler.removeFilter(self.structured_filter)
             self.root_logger.removeHandler(self.terminal_handler)
 
-    def register_structured_logger(self, logger):
+    def register_structured_logger(self, logger, terminal=True, json=True):
         """Register a structured logger.
 
         This needs to be called for all structured loggers that don't chain up
         to the mach logger in order for their output to be captured.
         """
         self.structured_loggers.append(logger)
+
+        if terminal and self.terminal_handler:
+            logger.addHandler(self.terminal_handler)
+
+        if json:
+            for handler in self.json_handlers:
+                logger.addHandler(handler)
+
+    def enable_all_structured_loggers(self, terminal=True, json=True):
+        """Enable logging of all structured messages from all loggers.
+
+        ``terminal`` and ``json`` determine which log handlers to operate
+        on. By default, all known handlers are operated on.
+        """
+        # Remove current handlers from all loggers so we don't double
+        # register handlers.
+        for logger in self.root_logger.manager.loggerDict.values():
+            # Some entries might be logging.PlaceHolder.
+            if not isinstance(logger, logging.Logger):
+                continue
+
+            if terminal:
+                logger.removeHandler(self.terminal_handler)
+
+            if json:
+                for handler in self.json_handlers:
+                    logger.removeHandler(handler)
+
+        # Wipe out existing registered structured loggers since they
+        # all propagate to root logger.
+        self.structured_loggers = []
+        self.register_structured_logger(self.root_logger, terminal=terminal,
+                                        json=json)
--- a/python/mozbuild/mozbuild/controller/building.py
+++ b/python/mozbuild/mozbuild/controller/building.py
@@ -3,46 +3,77 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, unicode_literals
 
 import getpass
 import json
 import logging
 import os
-import platform
 import subprocess
 import sys
 import time
 import which
 
 from collections import (
+    Counter,
     namedtuple,
     OrderedDict,
 )
+from textwrap import (
+    TextWrapper,
+)
 
 try:
     import psutil
 except Exception:
     psutil = None
 
+from mach.mixin.logging import LoggingMixin
 from mozsystemmonitor.resourcemonitor import SystemResourceMonitor
 
 import mozpack.path as mozpath
 
-from ..base import MozbuildObject
-
-from ..testing import install_test_files
-
+from ..base import (
+    BuildEnvironmentNotFoundException,
+    MozbuildObject,
+)
+from ..backend import (
+    get_backend_class,
+)
+from ..testing import (
+    install_test_files,
+)
 from ..compilation.warnings import (
     WarningsCollector,
     WarningsDatabase,
 )
+from ..shellutil import (
+    quote as shell_quote,
+)
+from ..util import (
+    mkdir,
+    resolve_target_to_make,
+)
 
-from textwrap import TextWrapper
+
+FINDER_SLOW_MESSAGE = '''
+===================
+PERFORMANCE WARNING
+
+The OS X Finder application (file indexing used by Spotlight) used a lot of CPU
+during the build - an average of %f%% (100%% is 1 core). This made your build
+slower.
+
+Consider adding ".noindex" to the end of your object directory name to have
+Finder ignore it. Or, add an indexing exclusion through the Spotlight System
+Preferences.
+===================
+'''.strip()
+
 
 INSTALL_TESTS_CLOBBER = ''.join([TextWrapper().fill(line) + '\n' for line in
 '''
 The build system was unable to install tests because the CLOBBER file has \
 been updated. This means if you edited any test files, your changes may not \
 be picked up until a full/clobber build is performed.
 
 The easiest and fastest way to perform a clobber build is to run:
@@ -480,16 +511,285 @@ class BuildMonitor(MozbuildObject):
         except which.WhichError:
             pass
         except ValueError as e:
             self.log(logging.WARNING, 'ccache', {'msg': str(e)}, '{msg}')
 
         return ccache_stats
 
 
+class TerminalLoggingHandler(logging.Handler):
+    """Custom logging handler that works with terminal window dressing.
+
+    This class should probably live elsewhere, like the mach core. Consider
+    this a proving ground for its usefulness.
+    """
+    def __init__(self):
+        logging.Handler.__init__(self)
+
+        self.fh = sys.stdout
+        self.footer = None
+
+    def flush(self):
+        self.acquire()
+
+        try:
+            self.fh.flush()
+        finally:
+            self.release()
+
+    def emit(self, record):
+        msg = self.format(record)
+
+        self.acquire()
+
+        try:
+            if self.footer:
+                self.footer.clear()
+
+            self.fh.write(msg)
+            self.fh.write('\n')
+
+            if self.footer:
+                self.footer.draw()
+
+            # If we don't flush, the footer may not get drawn.
+            self.fh.flush()
+        finally:
+            self.release()
+
+
+class Footer(object):
+    """Handles display of a footer in a terminal.
+
+    This class implements the functionality common to all mach commands
+    that render a footer.
+    """
+
+    def __init__(self, terminal):
+        # terminal is a blessings.Terminal.
+        self._t = terminal
+        self._fh = sys.stdout
+
+    def clear(self):
+        """Removes the footer from the current terminal."""
+        self._fh.write(self._t.move_x(0))
+        self._fh.write(self._t.clear_eol())
+
+    def write(self, parts):
+        """Write some output in the footer, accounting for terminal width.
+
+        parts is a list of 2-tuples of (encoding_function, input).
+        None means no encoding."""
+
+        # We don't want to write more characters than the current width of the
+        # terminal otherwise wrapping may result in weird behavior. We can't
+        # simply truncate the line at terminal width characters because a)
+        # non-viewable escape characters count towards the limit and b) we
+        # don't want to truncate in the middle of an escape sequence because
+        # subsequent output would inherit the escape sequence.
+        max_width = self._t.width
+        written = 0
+        write_pieces = []
+        for part in parts:
+            try:
+                func, part = part
+                encoded = getattr(self._t, func)(part)
+            except ValueError:
+                encoded = part
+
+            len_part = len(part)
+            len_spaces = len(write_pieces)
+            if written + len_part + len_spaces > max_width:
+                write_pieces.append(part[0:max_width - written - len_spaces])
+                written += len_part
+                break
+
+            write_pieces.append(encoded)
+            written += len_part
+
+        with self._t.location():
+            self._t.move(self._t.height-1,0)
+            self._fh.write(' '.join(write_pieces))
+
+
+class BuildProgressFooter(Footer):
+    """Handles display of a build progress indicator in a terminal.
+
+    When mach builds inside a blessings-supported terminal, it will render
+    progress information collected from a BuildMonitor. This class converts the
+    state of BuildMonitor into terminal output.
+    """
+
+    def __init__(self, terminal, monitor):
+        Footer.__init__(self, terminal)
+        self.tiers = monitor.tiers.tier_status.viewitems()
+
+    def draw(self):
+        """Draws this footer in the terminal."""
+
+        if not self.tiers:
+            return
+
+        # The drawn terminal looks something like:
+        # TIER: static export libs tools
+
+        parts = [('bold', 'TIER:')]
+        append = parts.append
+        for tier, status in self.tiers:
+            if status is None:
+                append(tier)
+            elif status == 'finished':
+                append(('green', tier))
+            else:
+                append(('underline_yellow', tier))
+
+        self.write(parts)
+
+
+
+class OutputManager(LoggingMixin):
+    """Handles writing job output to a terminal or log."""
+
+    def __init__(self, log_manager, footer):
+        self.populate_logger()
+
+        self.footer = None
+        terminal = log_manager.terminal
+
+        # TODO convert terminal footer to config file setting.
+        if not terminal or os.environ.get('MACH_NO_TERMINAL_FOOTER', None):
+            return
+        if os.environ.get('INSIDE_EMACS', None):
+            return
+
+        self.t = terminal
+        self.footer = footer
+
+        self._handler = TerminalLoggingHandler()
+        self._handler.setFormatter(log_manager.terminal_formatter)
+        self._handler.footer = self.footer
+
+        old = log_manager.replace_terminal_handler(self._handler)
+        self._handler.level = old.level
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        if self.footer:
+            self.footer.clear()
+            # Prevents the footer from being redrawn if logging occurs.
+            self._handler.footer = None
+
+    def write_line(self, line):
+        if self.footer:
+            self.footer.clear()
+
+        print(line)
+
+        if self.footer:
+            self.footer.draw()
+
+    def refresh(self):
+        if not self.footer:
+            return
+
+        self.footer.clear()
+        self.footer.draw()
+
+
+class BuildOutputManager(OutputManager):
+    """Handles writing build output to a terminal, to logs, etc."""
+
+    def __init__(self, log_manager, monitor, footer):
+        self.monitor = monitor
+        OutputManager.__init__(self, log_manager, footer)
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        OutputManager.__exit__(self, exc_type, exc_value, traceback)
+
+        # Ensure the resource monitor is stopped because leaving it running
+        # could result in the process hanging on exit because the resource
+        # collection child process hasn't been told to stop.
+        self.monitor.stop_resource_recording()
+
+
+    def on_line(self, line):
+        warning, state_changed, relevant = self.monitor.on_line(line)
+
+        if relevant:
+            self.log(logging.INFO, 'build_output', {'line': line}, '{line}')
+        elif state_changed:
+            have_handler = hasattr(self, 'handler')
+            if have_handler:
+                self.handler.acquire()
+            try:
+                self.refresh()
+            finally:
+                if have_handler:
+                    self.handler.release()
+
+
+class StaticAnalysisFooter(Footer):
+    """Handles display of a static analysis progress indicator in a terminal.
+    """
+
+    def __init__(self, terminal, monitor):
+        Footer.__init__(self, terminal)
+        self.monitor = monitor
+
+    def draw(self):
+        """Draws this footer in the terminal."""
+
+        monitor = self.monitor
+        total = monitor.num_files
+        processed = monitor.num_files_processed
+        percent = '(%.2f%%)' % (processed * 100.0 / total)
+        parts = [
+            ('dim', 'Processing'),
+            ('yellow', str(processed)),
+            ('dim', 'of'),
+            ('yellow', str(total)),
+            ('dim', 'files'),
+            ('green', percent)
+        ]
+        if monitor.current_file:
+            parts.append(('bold', monitor.current_file))
+
+        self.write(parts)
+
+
+class StaticAnalysisOutputManager(OutputManager):
+    """Handles writing static analysis output to a terminal."""
+
+    def __init__(self, log_manager, monitor, footer):
+        self.monitor = monitor
+        OutputManager.__init__(self, log_manager, footer)
+
+    def on_line(self, line):
+        warning, relevant = self.monitor.on_line(line)
+
+        if warning:
+            self.log(logging.INFO, 'compiler_warning', warning,
+                'Warning: {flag} in {filename}: {message}')
+
+        if relevant:
+            self.log(logging.INFO, 'build_output', {'line': line}, '{line}')
+        else:
+            have_handler = hasattr(self, 'handler')
+            if have_handler:
+                self.handler.acquire()
+            try:
+                self.refresh()
+            finally:
+                if have_handler:
+                    self.handler.release()
+
+
 class CCacheStats(object):
     """Holds statistics from ccache.
 
     Instances can be subtracted from each other to obtain differences.
     print() or str() the object to show a ``ccache -s`` like output
     of the captured stats.
 
     """
@@ -673,16 +973,338 @@ class CCacheStats(object):
             return '%.1f Mbytes' % (float(v) / CCacheStats.MiB)
         else:
             return '%.1f Kbytes' % (float(v) / CCacheStats.KiB)
 
 
 class BuildDriver(MozbuildObject):
     """Provides a high-level API for build actions."""
 
+    def build(self, what=None, disable_extra_make_dependencies=None, jobs=0,
+              directory=None, verbose=False, keep_going=False, mach_context=None):
+        """Invoke the build backend.
+
+        ``what`` defines the thing to build. If not defined, the default
+        target is used.
+        """
+        warnings_path = self._get_state_filename('warnings.json')
+        monitor = self._spawn(BuildMonitor)
+        monitor.init(warnings_path)
+        ccache_start = monitor.ccache_stats()
+        footer = BuildProgressFooter(self.log_manager.terminal, monitor)
+
+        # Disable indexing in objdir because it is not necessary and can slow
+        # down builds.
+        mkdir(self.topobjdir, not_indexed=True)
+
+        with BuildOutputManager(self.log_manager, monitor, footer) as output:
+            monitor.start()
+
+            if directory is not None and not what:
+                print('Can only use -C/--directory with an explicit target '
+                    'name.')
+                return 1
+
+            if directory is not None:
+                disable_extra_make_dependencies=True
+                directory = mozpath.normsep(directory)
+                if directory.startswith('/'):
+                    directory = directory[1:]
+
+            status = None
+            monitor.start_resource_recording()
+            if what:
+                top_make = os.path.join(self.topobjdir, 'Makefile')
+                if not os.path.exists(top_make):
+                    print('Your tree has not been configured yet. Please run '
+                        '|mach build| with no arguments.')
+                    return 1
+
+                # Collect target pairs.
+                target_pairs = []
+                for target in what:
+                    path_arg = self._wrap_path_argument(target)
+
+                    if directory is not None:
+                        make_dir = os.path.join(self.topobjdir, directory)
+                        make_target = target
+                    else:
+                        make_dir, make_target = \
+                            resolve_target_to_make(self.topobjdir,
+                                path_arg.relpath())
+
+                    if make_dir is None and make_target is None:
+                        return 1
+
+                    # See bug 886162 - we don't want to "accidentally" build
+                    # the entire tree (if that's really the intent, it's
+                    # unlikely they would have specified a directory.)
+                    if not make_dir and not make_target:
+                        print("The specified directory doesn't contain a "
+                              "Makefile and the first parent with one is the "
+                              "root of the tree. Please specify a directory "
+                              "with a Makefile or run |mach build| if you "
+                              "want to build the entire tree.")
+                        return 1
+
+                    target_pairs.append((make_dir, make_target))
+
+                # Possibly add extra make depencies using dumbmake.
+                if not disable_extra_make_dependencies:
+                    from dumbmake.dumbmake import (dependency_map,
+                                                   add_extra_dependencies)
+                    depfile = os.path.join(self.topsrcdir, 'build',
+                                           'dumbmake-dependencies')
+                    with open(depfile) as f:
+                        dm = dependency_map(f.readlines())
+                    new_pairs = list(add_extra_dependencies(target_pairs, dm))
+                    self.log(logging.DEBUG, 'dumbmake',
+                             {'target_pairs': target_pairs,
+                              'new_pairs': new_pairs},
+                             'Added extra dependencies: will build {new_pairs} ' +
+                             'instead of {target_pairs}.')
+                    target_pairs = new_pairs
+
+                # Ensure build backend is up to date. The alternative is to
+                # have rules in the invoked Makefile to rebuild the build
+                # backend. But that involves make reinvoking itself and there
+                # are undesired side-effects of this. See bug 877308 for a
+                # comprehensive history lesson.
+                self._run_make(directory=self.topobjdir, target='backend',
+                    line_handler=output.on_line, log=False,
+                    print_directory=False, keep_going=keep_going)
+
+                # Build target pairs.
+                for make_dir, make_target in target_pairs:
+                    # We don't display build status messages during partial
+                    # tree builds because they aren't reliable there. This
+                    # could potentially be fixed if the build monitor were more
+                    # intelligent about encountering undefined state.
+                    status = self._run_make(directory=make_dir, target=make_target,
+                        line_handler=output.on_line, log=False, print_directory=False,
+                        ensure_exit_code=False, num_jobs=jobs, silent=not verbose,
+                        append_env={b'NO_BUILDSTATUS_MESSAGES': b'1'},
+                        keep_going=keep_going)
+
+                    if status != 0:
+                        break
+            else:
+                # Try to call the default backend's build() method. This will
+                # run configure to determine BUILD_BACKENDS if it hasn't run
+                # yet.
+                config = None
+                try:
+                    config = self.config_environment
+                except Exception:
+                    config_rc = self.configure(buildstatus_messages=True,
+                                               line_handler=output.on_line)
+                    if config_rc != 0:
+                        return config_rc
+
+                    # Even if configure runs successfully, we may have trouble
+                    # getting the config_environment for some builds, such as
+                    # OSX Universal builds. These have to go through client.mk
+                    # regardless.
+                    try:
+                        config = self.config_environment
+                    except Exception:
+                        pass
+
+                if config:
+                    active_backend = config.substs.get('BUILD_BACKENDS', [None])[0]
+                    if active_backend:
+                        backend_cls = get_backend_class(active_backend)(config)
+                        status = backend_cls.build(self, output, jobs, verbose)
+
+                # If the backend doesn't specify a build() method, then just
+                # call client.mk directly.
+                if status is None:
+                    status = self._run_make(srcdir=True, filename='client.mk',
+                        line_handler=output.on_line, log=False, print_directory=False,
+                        allow_parallel=False, ensure_exit_code=False, num_jobs=jobs,
+                        silent=not verbose, keep_going=keep_going)
+
+                self.log(logging.WARNING, 'warning_summary',
+                    {'count': len(monitor.warnings_database)},
+                    '{count} compiler warnings present.')
+
+            monitor.finish(record_usage=status == 0)
+
+        # Print the collected compiler warnings. This is redundant with
+        # inline output from the compiler itself. However, unlike inline
+        # output, this list is sorted and grouped by file, making it
+        # easier to triage output.
+        #
+        # Only do this if we had a successful build. If the build failed,
+        # there are more important things in the log to look for than
+        # whatever code we warned about.
+        if not status:
+            # Suppress warnings for 3rd party projects in local builds
+            # until we suppress them for real.
+            # TODO remove entries/feature once we stop generating warnings
+            # in these directories.
+            pathToThirdparty = os.path.join(self.topsrcdir,
+                                            "tools",
+                                           "rewriting",
+                                           "ThirdPartyPaths.txt")
+
+            if os.path.exists(pathToThirdparty):
+                with open(pathToThirdparty) as f:
+                    # Normalize the path (no trailing /)
+                    LOCAL_SUPPRESS_DIRS = tuple(d.rstrip('/') for d in f.read().splitlines())
+            else:
+                # For application based on gecko like thunderbird
+                LOCAL_SUPPRESS_DIRS = ()
+
+            suppressed_by_dir = Counter()
+
+            for warning in sorted(monitor.instance_warnings):
+                path = mozpath.normsep(warning['filename'])
+                if path.startswith(self.topsrcdir):
+                    path = path[len(self.topsrcdir) + 1:]
+
+                warning['normpath'] = path
+
+                if (path.startswith(LOCAL_SUPPRESS_DIRS) and
+                        'MOZ_AUTOMATION' not in os.environ):
+                    for d in LOCAL_SUPPRESS_DIRS:
+                        if path.startswith(d):
+                            suppressed_by_dir[d] += 1
+                            break
+
+                    continue
+
+                if warning['column'] is not None:
+                    self.log(logging.WARNING, 'compiler_warning', warning,
+                             'warning: {normpath}:{line}:{column} [{flag}] '
+                             '{message}')
+                else:
+                    self.log(logging.WARNING, 'compiler_warning', warning,
+                             'warning: {normpath}:{line} [{flag}] {message}')
+
+            for d, count in sorted(suppressed_by_dir.items()):
+                self.log(logging.WARNING, 'suppressed_warning',
+                         {'dir': d, 'count': count},
+                         '(suppressed {count} warnings in {dir})')
+
+        high_finder, finder_percent = monitor.have_high_finder_usage()
+        if high_finder:
+            print(FINDER_SLOW_MESSAGE % finder_percent)
+
+        ccache_end = monitor.ccache_stats()
+
+        ccache_diff = None
+        if ccache_start and ccache_end:
+            ccache_diff = ccache_end - ccache_start
+            if ccache_diff:
+                self.log(logging.INFO, 'ccache',
+                         {'msg': ccache_diff.hit_rate_message()}, "{msg}")
+
+        notify_minimum_time = 300
+        try:
+            notify_minimum_time = int(os.environ.get('MACH_NOTIFY_MINTIME', '300'))
+        except ValueError:
+            # Just stick with the default
+            pass
+
+        if monitor.elapsed > notify_minimum_time:
+            # Display a notification when the build completes.
+            self.notify('Build complete' if not status else 'Build failed')
+
+        if status:
+            return status
+
+        long_build = monitor.elapsed > 600
+
+        if long_build:
+            output.on_line('We know it took a while, but your build finally finished successfully!')
+        else:
+            output.on_line('Your build was successful!')
+
+        if monitor.have_resource_usage:
+            excessive, swap_in, swap_out = monitor.have_excessive_swapping()
+            # if excessive:
+            #    print(EXCESSIVE_SWAP_MESSAGE)
+
+            print('To view resource usage of the build, run |mach '
+                'resource-usage|.')
+
+            telemetry_handler = getattr(mach_context,
+                                        'telemetry_handler', None)
+            telemetry_data = monitor.get_resource_usage()
+
+            # Record build configuration data. For now, we cherry pick
+            # items we need rather than grabbing everything, in order
+            # to avoid accidentally disclosing PII.
+            telemetry_data['substs'] = {}
+            try:
+                for key in ['MOZ_ARTIFACT_BUILDS', 'MOZ_USING_CCACHE', 'MOZ_USING_SCCACHE']:
+                    value = self.substs.get(key, False)
+                    telemetry_data['substs'][key] = value
+            except BuildEnvironmentNotFoundException:
+                pass
+
+            # Grab ccache stats if available. We need to be careful not
+            # to capture information that can potentially identify the
+            # user (such as the cache location)
+            if ccache_diff:
+                telemetry_data['ccache'] = {}
+                for key in [key[0] for key in ccache_diff.STATS_KEYS]:
+                    try:
+                        telemetry_data['ccache'][key] = ccache_diff._values[key]
+                    except KeyError:
+                        pass
+
+            if telemetry_handler:
+                telemetry_handler(mach_context, telemetry_data)
+
+        # Only for full builds because incremental builders likely don't
+        # need to be burdened with this.
+        if not what:
+            try:
+                # Fennec doesn't have useful output from just building. We should
+                # arguably make the build action useful for Fennec. Another day...
+                if self.substs['MOZ_BUILD_APP'] != 'mobile/android':
+                    print('To take your build for a test drive, run: |mach run|')
+                app = self.substs['MOZ_BUILD_APP']
+                if app in ('browser', 'mobile/android'):
+                    print('For more information on what to do now, see '
+                        'https://developer.mozilla.org/docs/Developer_Guide/So_You_Just_Built_Firefox')
+            except Exception:
+                # Ignore Exceptions in case we can't find config.status (such
+                # as when doing OSX Universal builds)
+                pass
+
+        return status
+
+    def configure(self, options=None, buildstatus_messages=False,
+                  line_handler=None):
+        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')}
+
+        # Only print build status messages when we have an active
+        # monitor.
+        if not buildstatus_messages:
+            append_env[b'NO_BUILDSTATUS_MESSAGES'] =  b'1'
+        status = self._run_make(srcdir=True, filename='client.mk',
+            target='configure', line_handler=line_handler, log=False,
+            print_directory=False, allow_parallel=False, ensure_exit_code=False,
+            append_env=append_env)
+
+        if not status:
+            print('Configure complete!')
+            print('Be sure to run |mach build| to pick up any changes');
+
+        return status
+
     def install_tests(self, test_objs):
         """Install test files."""
 
         if self.is_clobber_needed():
             print(INSTALL_TESTS_CLOBBER.format(
                   clobber_file=os.path.join(self.topobjdir, 'CLOBBER')))
             sys.exit(1)
 
--- a/python/mozbuild/mozbuild/mach_commands.py
+++ b/python/mozbuild/mozbuild/mach_commands.py
@@ -1,17 +1,15 @@
 # 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 argparse
-import collections
-import errno
 import hashlib
 import itertools
 import json
 import logging
 import operator
 import os
 import subprocess
 import sys
@@ -24,59 +22,38 @@ from mach.decorators import (
     CommandArgument,
     CommandArgumentGroup,
     CommandProvider,
     Command,
     SettingsProvider,
     SubCommand,
 )
 
-from mach.mixin.logging import LoggingMixin
-
 from mach.main import Mach
 
 from mozbuild.base import (
-    BuildEnvironmentNotFoundException,
     MachCommandBase,
     MachCommandConditions as conditions,
     MozbuildObject,
-    MozconfigFindException,
-    MozconfigLoadException,
-    ObjdirMismatchException,
 )
 from mozbuild.util import ensureParentDir
 
 from mozbuild.backend import (
     backends,
-    get_backend_class,
 )
-from mozbuild.shellutil import quote as shell_quote
 
 
 BUILD_WHAT_HELP = '''
 What to build. Can be a top-level make target or a relative directory. If
 multiple options are provided, they will be built serially. Takes dependency
 information from `topsrcdir/build/dumbmake-dependencies` to build additional
 targets as needed. BUILDING ONLY PARTS OF THE TREE CAN RESULT IN BAD TREE
 STATE. USE AT YOUR OWN RISK.
 '''.strip()
 
-FINDER_SLOW_MESSAGE = '''
-===================
-PERFORMANCE WARNING
-
-The OS X Finder application (file indexing used by Spotlight) used a lot of CPU
-during the build - an average of %f%% (100%% is 1 core). This made your build
-slower.
-
-Consider adding ".noindex" to the end of your object directory name to have
-Finder ignore it. Or, add an indexing exclusion through the Spotlight System
-Preferences.
-===================
-'''.strip()
 
 EXCESSIVE_SWAP_MESSAGE = '''
 ===================
 PERFORMANCE WARNING
 
 Your machine experienced a lot of swap activity during the build. This is
 possibly a sign that your machine doesn't have enough physical memory or
 not enough available memory to perform the build. It's also possible some
@@ -86,227 +63,16 @@ If you feel this message is not appropri
 please file a Core :: Build Config bug at
 https://bugzilla.mozilla.org/enter_bug.cgi?product=Core&component=Build%20Config
 and tell us about your machine and build configuration so we can adjust the
 warning heuristic.
 ===================
 '''
 
 
-class TerminalLoggingHandler(logging.Handler):
-    """Custom logging handler that works with terminal window dressing.
-
-    This class should probably live elsewhere, like the mach core. Consider
-    this a proving ground for its usefulness.
-    """
-    def __init__(self):
-        logging.Handler.__init__(self)
-
-        self.fh = sys.stdout
-        self.footer = None
-
-    def flush(self):
-        self.acquire()
-
-        try:
-            self.fh.flush()
-        finally:
-            self.release()
-
-    def emit(self, record):
-        msg = self.format(record)
-
-        self.acquire()
-
-        try:
-            if self.footer:
-                self.footer.clear()
-
-            self.fh.write(msg)
-            self.fh.write('\n')
-
-            if self.footer:
-                self.footer.draw()
-
-            # If we don't flush, the footer may not get drawn.
-            self.fh.flush()
-        finally:
-            self.release()
-
-
-class Footer(object):
-    """Handles display of a footer in a terminal.
-
-    This class implements the functionality common to all mach commands
-    that render a footer.
-    """
-
-    def __init__(self, terminal):
-        # terminal is a blessings.Terminal.
-        self._t = terminal
-        self._fh = sys.stdout
-
-    def clear(self):
-        """Removes the footer from the current terminal."""
-        self._fh.write(self._t.move_x(0))
-        self._fh.write(self._t.clear_eol())
-
-    def write(self, parts):
-        """Write some output in the footer, accounting for terminal width.
-
-        parts is a list of 2-tuples of (encoding_function, input).
-        None means no encoding."""
-
-        # We don't want to write more characters than the current width of the
-        # terminal otherwise wrapping may result in weird behavior. We can't
-        # simply truncate the line at terminal width characters because a)
-        # non-viewable escape characters count towards the limit and b) we
-        # don't want to truncate in the middle of an escape sequence because
-        # subsequent output would inherit the escape sequence.
-        max_width = self._t.width
-        written = 0
-        write_pieces = []
-        for part in parts:
-            try:
-                func, part = part
-                encoded = getattr(self._t, func)(part)
-            except ValueError:
-                encoded = part
-
-            len_part = len(part)
-            len_spaces = len(write_pieces)
-            if written + len_part + len_spaces > max_width:
-                write_pieces.append(part[0:max_width - written - len_spaces])
-                written += len_part
-                break
-
-            write_pieces.append(encoded)
-            written += len_part
-
-        with self._t.location():
-            self._t.move(self._t.height-1,0)
-            self._fh.write(' '.join(write_pieces))
-
-
-class BuildProgressFooter(Footer):
-    """Handles display of a build progress indicator in a terminal.
-
-    When mach builds inside a blessings-supported terminal, it will render
-    progress information collected from a BuildMonitor. This class converts the
-    state of BuildMonitor into terminal output.
-    """
-
-    def __init__(self, terminal, monitor):
-        Footer.__init__(self, terminal)
-        self.tiers = monitor.tiers.tier_status.viewitems()
-
-    def draw(self):
-        """Draws this footer in the terminal."""
-
-        if not self.tiers:
-            return
-
-        # The drawn terminal looks something like:
-        # TIER: static export libs tools
-
-        parts = [('bold', 'TIER:')]
-        append = parts.append
-        for tier, status in self.tiers:
-            if status is None:
-                append(tier)
-            elif status == 'finished':
-                append(('green', tier))
-            else:
-                append(('underline_yellow', tier))
-
-        self.write(parts)
-
-
-class OutputManager(LoggingMixin):
-    """Handles writing job output to a terminal or log."""
-
-    def __init__(self, log_manager, footer):
-        self.populate_logger()
-
-        self.footer = None
-        terminal = log_manager.terminal
-
-        # TODO convert terminal footer to config file setting.
-        if not terminal or os.environ.get('MACH_NO_TERMINAL_FOOTER', None):
-            return
-        if os.environ.get('INSIDE_EMACS', None):
-            return
-
-        self.t = terminal
-        self.footer = footer
-
-        self._handler = TerminalLoggingHandler()
-        self._handler.setFormatter(log_manager.terminal_formatter)
-        self._handler.footer = self.footer
-
-        old = log_manager.replace_terminal_handler(self._handler)
-        self._handler.level = old.level
-
-    def __enter__(self):
-        return self
-
-    def __exit__(self, exc_type, exc_value, traceback):
-        if self.footer:
-            self.footer.clear()
-            # Prevents the footer from being redrawn if logging occurs.
-            self._handler.footer = None
-
-    def write_line(self, line):
-        if self.footer:
-            self.footer.clear()
-
-        print(line)
-
-        if self.footer:
-            self.footer.draw()
-
-    def refresh(self):
-        if not self.footer:
-            return
-
-        self.footer.clear()
-        self.footer.draw()
-
-class BuildOutputManager(OutputManager):
-    """Handles writing build output to a terminal, to logs, etc."""
-
-    def __init__(self, log_manager, monitor, footer):
-        self.monitor = monitor
-        OutputManager.__init__(self, log_manager, footer)
-
-    def __exit__(self, exc_type, exc_value, traceback):
-        OutputManager.__exit__(self, exc_type, exc_value, traceback)
-
-        # Ensure the resource monitor is stopped because leaving it running
-        # could result in the process hanging on exit because the resource
-        # collection child process hasn't been told to stop.
-        self.monitor.stop_resource_recording()
-
-
-    def on_line(self, line):
-        warning, state_changed, relevant = self.monitor.on_line(line)
-
-        if relevant:
-            self.log(logging.INFO, 'build_output', {'line': line}, '{line}')
-        elif state_changed:
-            have_handler = hasattr(self, 'handler')
-            if have_handler:
-                self.handler.acquire()
-            try:
-                self.refresh()
-            finally:
-                if have_handler:
-                    self.handler.release()
-
-
 class StoreDebugParamsAndWarnAction(argparse.Action):
     def __call__(self, parser, namespace, values, option_string=None):
         sys.stderr.write('The --debugparams argument is deprecated. Please ' +
                          'use --debugger-args instead.\n\n')
         setattr(namespace, self.dest, values)
 
 
 @CommandProvider
@@ -382,341 +148,48 @@ class Build(MachCommandBase):
           libraries and executables (binaries).
 
         * faster - builds JavaScript, XUL, CSS, etc files.
 
         "binaries" and "faster" almost fully complement each other. However,
         there are build actions not captured by either. If things don't appear to
         be rebuilding, perform a vanilla `mach build` to rebuild the world.
         """
-        import which
-        from mozbuild.controller.building import BuildMonitor
-        from mozbuild.util import (
-            mkdir,
-            resolve_target_to_make,
+        from mozbuild.controller.building import (
+            BuildDriver,
         )
 
-        self.log_manager.register_structured_logger(logging.getLogger('mozbuild'))
-
-        warnings_path = self._get_state_filename('warnings.json')
-        monitor = self._spawn(BuildMonitor)
-        monitor.init(warnings_path)
-        ccache_start = monitor.ccache_stats()
-        footer = BuildProgressFooter(self.log_manager.terminal, monitor)
-
-        # Disable indexing in objdir because it is not necessary and can slow
-        # down builds.
-        mkdir(self.topobjdir, not_indexed=True)
-
-        with BuildOutputManager(self.log_manager, monitor, footer) as output:
-            monitor.start()
-
-            if directory is not None and not what:
-                print('Can only use -C/--directory with an explicit target '
-                    'name.')
-                return 1
-
-            if directory is not None:
-                disable_extra_make_dependencies=True
-                directory = mozpath.normsep(directory)
-                if directory.startswith('/'):
-                    directory = directory[1:]
-
-            status = None
-            monitor.start_resource_recording()
-            if what:
-                top_make = os.path.join(self.topobjdir, 'Makefile')
-                if not os.path.exists(top_make):
-                    print('Your tree has not been configured yet. Please run '
-                        '|mach build| with no arguments.')
-                    return 1
-
-                # Collect target pairs.
-                target_pairs = []
-                for target in what:
-                    path_arg = self._wrap_path_argument(target)
-
-                    if directory is not None:
-                        make_dir = os.path.join(self.topobjdir, directory)
-                        make_target = target
-                    else:
-                        make_dir, make_target = \
-                            resolve_target_to_make(self.topobjdir,
-                                path_arg.relpath())
-
-                    if make_dir is None and make_target is None:
-                        return 1
-
-                    # See bug 886162 - we don't want to "accidentally" build
-                    # the entire tree (if that's really the intent, it's
-                    # unlikely they would have specified a directory.)
-                    if not make_dir and not make_target:
-                        print("The specified directory doesn't contain a "
-                              "Makefile and the first parent with one is the "
-                              "root of the tree. Please specify a directory "
-                              "with a Makefile or run |mach build| if you "
-                              "want to build the entire tree.")
-                        return 1
-
-                    target_pairs.append((make_dir, make_target))
-
-                # Possibly add extra make depencies using dumbmake.
-                if not disable_extra_make_dependencies:
-                    from dumbmake.dumbmake import (dependency_map,
-                                                   add_extra_dependencies)
-                    depfile = os.path.join(self.topsrcdir, 'build',
-                                           'dumbmake-dependencies')
-                    with open(depfile) as f:
-                        dm = dependency_map(f.readlines())
-                    new_pairs = list(add_extra_dependencies(target_pairs, dm))
-                    self.log(logging.DEBUG, 'dumbmake',
-                             {'target_pairs': target_pairs,
-                              'new_pairs': new_pairs},
-                             'Added extra dependencies: will build {new_pairs} ' +
-                             'instead of {target_pairs}.')
-                    target_pairs = new_pairs
-
-                # Ensure build backend is up to date. The alternative is to
-                # have rules in the invoked Makefile to rebuild the build
-                # backend. But that involves make reinvoking itself and there
-                # are undesired side-effects of this. See bug 877308 for a
-                # comprehensive history lesson.
-                self._run_make(directory=self.topobjdir, target='backend',
-                    line_handler=output.on_line, log=False,
-                    print_directory=False, keep_going=keep_going)
-
-                # Build target pairs.
-                for make_dir, make_target in target_pairs:
-                    # We don't display build status messages during partial
-                    # tree builds because they aren't reliable there. This
-                    # could potentially be fixed if the build monitor were more
-                    # intelligent about encountering undefined state.
-                    status = self._run_make(directory=make_dir, target=make_target,
-                        line_handler=output.on_line, log=False, print_directory=False,
-                        ensure_exit_code=False, num_jobs=jobs, silent=not verbose,
-                        append_env={b'NO_BUILDSTATUS_MESSAGES': b'1'},
-                        keep_going=keep_going)
-
-                    if status != 0:
-                        break
-            else:
-                # Try to call the default backend's build() method. This will
-                # run configure to determine BUILD_BACKENDS if it hasn't run
-                # yet.
-                config = None
-                try:
-                    config = self.config_environment
-                except Exception:
-                    config_rc = self.configure(buildstatus_messages=True,
-                                               line_handler=output.on_line)
-                    if config_rc != 0:
-                        return config_rc
-
-                    # Even if configure runs successfully, we may have trouble
-                    # getting the config_environment for some builds, such as
-                    # OSX Universal builds. These have to go through client.mk
-                    # regardless.
-                    try:
-                        config = self.config_environment
-                    except Exception:
-                        pass
-
-                if config:
-                    active_backend = config.substs.get('BUILD_BACKENDS', [None])[0]
-                    if active_backend:
-                        backend_cls = get_backend_class(active_backend)(config)
-                        status = backend_cls.build(self, output, jobs, verbose)
-
-                # If the backend doesn't specify a build() method, then just
-                # call client.mk directly.
-                if status is None:
-                    status = self._run_make(srcdir=True, filename='client.mk',
-                        line_handler=output.on_line, log=False, print_directory=False,
-                        allow_parallel=False, ensure_exit_code=False, num_jobs=jobs,
-                        silent=not verbose, keep_going=keep_going)
-
-                self.log(logging.WARNING, 'warning_summary',
-                    {'count': len(monitor.warnings_database)},
-                    '{count} compiler warnings present.')
+        self.log_manager.enable_all_structured_loggers()
 
-            # Print the collected compiler warnings. This is redundant with
-            # inline output from the compiler itself. However, unlike inline
-            # output, this list is sorted and grouped by file, making it
-            # easier to triage output.
-            #
-            # Only do this if we had a successful build. If the build failed,
-            # there are more important things in the log to look for than
-            # whatever code we warned about.
-            if not status:
-                # Suppress warnings for 3rd party projects in local builds
-                # until we suppress them for real.
-                # TODO remove entries/feature once we stop generating warnings
-                # in these directories.
-                pathToThirdparty = os.path.join(self.topsrcdir,
-                                                "tools",
-                                               "rewriting",
-                                               "ThirdPartyPaths.txt")
-
-                if os.path.exists(pathToThirdparty):
-                    with open(pathToThirdparty) as f:
-                        # Normalize the path (no trailing /)
-                        LOCAL_SUPPRESS_DIRS = tuple(d.rstrip('/') for d in f.read().splitlines())
-                else:
-                    # For application based on gecko like thunderbird
-                    LOCAL_SUPPRESS_DIRS = ()
-
-                suppressed_by_dir = collections.Counter()
-
-                for warning in sorted(monitor.instance_warnings):
-                    path = mozpath.normsep(warning['filename'])
-                    if path.startswith(self.topsrcdir):
-                        path = path[len(self.topsrcdir) + 1:]
-
-                    warning['normpath'] = path
-
-                    if (path.startswith(LOCAL_SUPPRESS_DIRS) and
-                            'MOZ_AUTOMATION' not in os.environ):
-                        for d in LOCAL_SUPPRESS_DIRS:
-                            if path.startswith(d):
-                                suppressed_by_dir[d] += 1
-                                break
-
-                        continue
-
-                    if warning['column'] is not None:
-                        self.log(logging.WARNING, 'compiler_warning', warning,
-                                 'warning: {normpath}:{line}:{column} [{flag}] '
-                                 '{message}')
-                    else:
-                        self.log(logging.WARNING, 'compiler_warning', warning,
-                                 'warning: {normpath}:{line} [{flag}] {message}')
-
-                for d, count in sorted(suppressed_by_dir.items()):
-                    self.log(logging.WARNING, 'suppressed_warning',
-                             {'dir': d, 'count': count},
-                             '(suppressed {count} warnings in {dir})')
-
-            monitor.finish(record_usage=status==0)
-
-        high_finder, finder_percent = monitor.have_high_finder_usage()
-        if high_finder:
-            print(FINDER_SLOW_MESSAGE % finder_percent)
-
-        ccache_end = monitor.ccache_stats()
-
-        ccache_diff = None
-        if ccache_start and ccache_end:
-            ccache_diff = ccache_end - ccache_start
-            if ccache_diff:
-                self.log(logging.INFO, 'ccache',
-                         {'msg': ccache_diff.hit_rate_message()}, "{msg}")
-
-        notify_minimum_time = 300
-        try:
-            notify_minimum_time = int(os.environ.get('MACH_NOTIFY_MINTIME', '300'))
-        except ValueError:
-            # Just stick with the default
-            pass
-
-        if monitor.elapsed > notify_minimum_time:
-            # Display a notification when the build completes.
-            self.notify('Build complete' if not status else 'Build failed')
-
-        if status:
-            return status
-
-        long_build = monitor.elapsed > 600
-
-        if long_build:
-            output.on_line('We know it took a while, but your build finally finished successfully!')
-        else:
-            output.on_line('Your build was successful!')
-
-        if monitor.have_resource_usage:
-            excessive, swap_in, swap_out = monitor.have_excessive_swapping()
-            # if excessive:
-            #    print(EXCESSIVE_SWAP_MESSAGE)
-
-            print('To view resource usage of the build, run |mach '
-                'resource-usage|.')
-
-            telemetry_handler = getattr(self._mach_context,
-                                        'telemetry_handler', None)
-            telemetry_data = monitor.get_resource_usage()
-
-            # Record build configuration data. For now, we cherry pick
-            # items we need rather than grabbing everything, in order
-            # to avoid accidentally disclosing PII.
-            telemetry_data['substs'] = {}
-            try:
-                for key in ['MOZ_ARTIFACT_BUILDS', 'MOZ_USING_CCACHE', 'MOZ_USING_SCCACHE']:
-                    value = self.substs.get(key, False)
-                    telemetry_data['substs'][key] = value
-            except BuildEnvironmentNotFoundException:
-                pass
-
-            # Grab ccache stats if available. We need to be careful not
-            # to capture information that can potentially identify the
-            # user (such as the cache location)
-            if ccache_diff:
-                telemetry_data['ccache'] = {}
-                for key in [key[0] for key in ccache_diff.STATS_KEYS]:
-                    try:
-                        telemetry_data['ccache'][key] = ccache_diff._values[key]
-                    except KeyError:
-                        pass
-
-            telemetry_handler(self._mach_context, telemetry_data)
-
-        # Only for full builds because incremental builders likely don't
-        # need to be burdened with this.
-        if not what:
-            try:
-                # Fennec doesn't have useful output from just building. We should
-                # arguably make the build action useful for Fennec. Another day...
-                if self.substs['MOZ_BUILD_APP'] != 'mobile/android':
-                    print('To take your build for a test drive, run: |mach run|')
-                app = self.substs['MOZ_BUILD_APP']
-                if app in ('browser', 'mobile/android'):
-                    print('For more information on what to do now, see '
-                        'https://developer.mozilla.org/docs/Developer_Guide/So_You_Just_Built_Firefox')
-            except Exception:
-                # Ignore Exceptions in case we can't find config.status (such
-                # as when doing OSX Universal builds)
-                pass
-
-        return status
+        driver = self._spawn(BuildDriver)
+        return driver.build(
+            what=what,
+            disable_extra_make_dependencies=disable_extra_make_dependencies,
+            jobs=jobs,
+            directory=directory,
+            verbose=verbose,
+            keep_going=keep_going,
+            mach_context=self._mach_context)
 
     @Command('configure', category='build',
         description='Configure the tree (run configure and config.status).')
     @CommandArgument('options', default=None, nargs=argparse.REMAINDER,
                      help='Configure options')
     def configure(self, options=None, buildstatus_messages=False, line_handler=None):
-        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')}
+        from mozbuild.controller.building import (
+            BuildDriver,
+        )
 
-        # Only print build status messages when we have an active
-        # monitor.
-        if not buildstatus_messages:
-            append_env[b'NO_BUILDSTATUS_MESSAGES'] =  b'1'
-        status = self._run_make(srcdir=True, filename='client.mk',
-            target='configure', line_handler=line_handler, log=False,
-            print_directory=False, allow_parallel=False, ensure_exit_code=False,
-            append_env=append_env)
+        self.log_manager.enable_all_structured_loggers()
+        driver = self._spawn(BuildDriver)
 
-        if not status:
-            print('Configure complete!')
-            print('Be sure to run |mach build| to pick up any changes');
-
-        return status
+        return driver.configure(
+            options=options,
+            buildstatus_messages=buildstatus_messages,
+            line_handler=line_handler)
 
     @Command('resource-usage', category='post-build',
         description='Show information about system resource usage for a build.')
     @CommandArgument('--address', default='localhost',
         help='Address the HTTP server should listen on.')
     @CommandArgument('--port', type=int, default=0,
         help='Port number the HTTP server should listen on.')
     @CommandArgument('--browser', default='firefox',
@@ -2116,72 +1589,16 @@ class StaticAnalysisMonitor(object):
                 self._current = os.path.relpath(filename, self._srcdir)
             else:
                 self._current = None
             self._processed = self._processed + 1
             return (warning, False)
         return (warning, True)
 
 
-class StaticAnalysisFooter(Footer):
-    """Handles display of a static analysis progress indicator in a terminal.
-    """
-
-    def __init__(self, terminal, monitor):
-        Footer.__init__(self, terminal)
-        self.monitor = monitor
-
-    def draw(self):
-        """Draws this footer in the terminal."""
-
-        monitor = self.monitor
-        total = monitor.num_files
-        processed = monitor.num_files_processed
-        percent = '(%.2f%%)' % (processed * 100.0 / total)
-        parts = [
-            ('dim', 'Processing'),
-            ('yellow', str(processed)),
-            ('dim', 'of'),
-            ('yellow', str(total)),
-            ('dim', 'files'),
-            ('green', percent)
-        ]
-        if monitor.current_file:
-            parts.append(('bold', monitor.current_file))
-
-        self.write(parts)
-
-
-class StaticAnalysisOutputManager(OutputManager):
-    """Handles writing static analysis output to a terminal."""
-
-    def __init__(self, log_manager, monitor, footer):
-        self.monitor = monitor
-        OutputManager.__init__(self, log_manager, footer)
-
-    def on_line(self, line):
-        warning, relevant = self.monitor.on_line(line)
-
-        if warning:
-            self.log(logging.INFO, 'compiler_warning', warning,
-                'Warning: {flag} in {filename}: {message}')
-
-        if relevant:
-            self.log(logging.INFO, 'build_output', {'line': line}, '{line}')
-        else:
-            have_handler = hasattr(self, 'handler')
-            if have_handler:
-                self.handler.acquire()
-            try:
-                self.refresh()
-            finally:
-                if have_handler:
-                    self.handler.release()
-
-
 @CommandProvider
 class StaticAnalysis(MachCommandBase):
     """Utilities for running C++ static analysis checks."""
 
     @Command('static-analysis', category='testing',
              description='Run C++ static analysis checks')
     def static_analysis(self):
         # If not arguments are provided, just print a help message.
@@ -2208,17 +1625,24 @@ class StaticAnalysis(MachCommandBase):
     @CommandArgument('--fix', '-f', default=False, action='store_true',
                      help='Try to autofix errors detected by clang-tidy checkers.')
     @CommandArgument('--header-filter', '-h-f', default='', metavar='header_filter',
                      help='Regular expression matching the names of the headers to '
                           'output diagnostics from. Diagnostics from the main file '
                           'of each translation unit are always displayed')
     def check(self, source=None, jobs=2, strip=1, verbose=False,
               checks='-*', fix=False, header_filter=''):
+        from mozbuild.controller.building import (
+            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
 
@@ -2241,18 +1665,16 @@ class StaticAnalysis(MachCommandBase):
         # When no value is specified the default value is considered to be the source
         # in order to limit the dianostic message to the source files or folders.
         common_args.append('-header-filter=%s' %
                            (header_filter if len(header_filter) else ''.join(source)))
 
         if fix:
             common_args.append('-fix')
 
-        self.log_manager.register_structured_logger(logging.getLogger('mozbuild'))
-
         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:
             if name_re.search(f['file']):
                 total = total + 1
 
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
@@ -26,16 +26,20 @@ function add_ocsp_test(aHost, aExpectedR
             "Should have made " + aExpectedRequestCount +
             " fallback OCSP request" + (aExpectedRequestCount == 1 ? "" : "s"));
     });
 }
 
 do_get_profile();
 Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
 Services.prefs.setIntPref("security.OCSP.enabled", 1);
+// Sometimes this test will fail on android due to an OCSP request timing out.
+// That aspect of OCSP requests is not what we're testing here, so we can just
+// bump the timeout and hopefully avoid these failures.
+Services.prefs.setIntPref("security.OCSP.timeoutMilliseconds.soft", 5000);
 Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 4);
 var args = [["good", "default-ee", "unused", 0],
              ["expiredresponse", "default-ee", "unused", 0],
              ["oldvalidperiod", "default-ee", "unused", 0],
              ["revoked", "default-ee", "unused", 0],
              ["unknown", "default-ee", "unused", 0],
             ];
 var ocspResponses = generateOCSPResponses(args, "ocsp_certs");
--- a/services/sync/tests/tps/.eslintrc.js
+++ b/services/sync/tests/tps/.eslintrc.js
@@ -1,33 +1,27 @@
 "use strict";
 
 module.exports = {
   "extends": [
     "plugin:mozilla/mochitest-test"
   ],
 
   globals: {
-    // Globals specific to mozmill
-    "assert": false,
-    "controller": false,
-    "findElement": false,
-    "mozmill": false,
     // Injected into tests via tps.jsm
     "Addons": false,
     "Bookmarks": false,
     "EnableEngines": false,
     "EnsureTracking": false,
     "Formdata": false,
     "History": false,
     "Login": false,
     "Passwords": false,
     "Phase": false,
     "Prefs": false,
-    "RunMozmillTest": false,
     "STATE_DISABLED": false,
     "STATE_ENABLED": false,
     "Sync": false,
     "SYNC_WIPE_CLIENT": false,
     "SYNC_WIPE_REMOTE": false,
     "Tabs": false,
     "Windows": false,
     "WipeServer": false,
deleted file mode 100644
--- a/services/sync/tests/tps/mozmill_sanity.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-Components.utils.import("resource://tps/tps.jsm");
-
-var setupModule = function(module) {
-  module.controller = mozmill.getBrowserController();
-  assert.ok(true, "SetupModule passes");
-};
-
-var setupTest = function(module) {
-  assert.ok(true, "SetupTest passes");
-};
-
-var testTestStep = function() {
-  assert.ok(true, "test Passes");
-  controller.open("http://www.mozilla.org");
-
-  TPS.Login();
-  TPS.Sync(ACTIONS.ACTION_SYNC_WIPE_CLIENT);
-};
-
-var teardownTest = function() {
-  assert.ok(true, "teardownTest passes");
-};
-
-var teardownModule = function() {
-  assert.ok(true, "teardownModule passes");
-};
deleted file mode 100644
--- a/services/sync/tests/tps/mozmill_sanity2.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var setupModule = function(module) {
-  module.controller = mozmill.getBrowserController();
-};
-
-var testGetNode = function() {
-  controller.open("about:support");
-  controller.waitForPageLoad();
-
-  var appbox = findElement.ID(controller.tabs.activeTab, "application-box");
-  assert.waitFor(() => appbox.getNode().textContent == "Firefox", "correct app name");
-};
deleted file mode 100644
--- a/services/sync/tests/tps/test_mozmill_sanity.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/*
- * The list of phases mapped to their corresponding profiles.  The object
- * here must be in strict JSON format, as it will get parsed by the Python
- * testrunner (no single quotes, extra comma's, etc).
- */
-
-var phases = { "phase1": "profile1",
-               "phase2": "profile2" };
-
-/*
- * Test phases
- */
-
-Phase("phase1", [
-  [RunMozmillTest, "mozmill_sanity.js"],
-]);
-
-Phase("phase2", [
-  [Sync],
-  [RunMozmillTest, "mozmill_sanity2.js"],
-]);
--- a/services/sync/tests/unit/test_doctor.js
+++ b/services/sync/tests/unit/test_doctor.js
@@ -161,17 +161,17 @@ add_task(async function test_repairs_ski
   let engine = {
     name: "test-engine",
     getValidator() {
       return validator;
     }
   };
   let requestor = {
     async startRepairs(validationInfo, flowID) {
-      assert.ok(false, "Never should start repairs");
+      ok(false, "Never should start repairs");
     },
     tryServerOnlyRepairs() {
       return false;
     }
   };
   let doctor = mockDoctor({
     _getEnginesToValidate(recentlySyncedEngines) {
       deepEqual(recentlySyncedEngines, [engine]);
deleted file mode 100755
--- a/services/sync/tps/extensions/mozmill/chrome.manifest
+++ /dev/null
@@ -1,2 +0,0 @@
-resource    mozmill                 resource/
-
deleted file mode 100755
--- a/services/sync/tps/extensions/mozmill/install.rdf
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0"?>
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
-   <Description about="urn:mozilla:install-manifest">
-     <em:id>mozmill@mozilla.com</em:id>
-     <em:name>Mozmill</em:name>
-     <em:version>2.0.8</em:version>
-     <em:description>UI Automation tool for Mozilla applications</em:description>
-     <em:unpack>true</em:unpack>
-
-     <em:creator>Mozilla Automation and Testing Team</em:creator>
-     <em:contributor>Adam Christian</em:contributor>
-     <em:contributor>Mikeal Rogers</em:contributor>
-
-     <em:targetApplication>
-       <Description>
-         <em:id>toolkit@mozilla.org</em:id>
-         <em:minVersion>10.0</em:minVersion>
-         <em:maxVersion>38.*</em:maxVersion>
-       </Description>
-     </em:targetApplication>
-     <em:multiprocessCompatible>true</em:multiprocessCompatible>
-   </Description>
-</RDF>
deleted file mode 100644
--- a/services/sync/tps/extensions/mozmill/resource/driver/controller.js
+++ /dev/null
@@ -1,1139 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var EXPORTED_SYMBOLS = ["MozMillController", "globalEventRegistry",
-                        "sleep", "windowMap"];
-
-var Cc = Components.classes;
-var Ci = Components.interfaces;
-var Cu = Components.utils;
-
-var EventUtils = {}; Cu.import('resource://mozmill/stdlib/EventUtils.js', EventUtils);
-
-var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions);
-var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker);
-var elementslib = {}; Cu.import('resource://mozmill/driver/elementslib.js', elementslib);
-var errors = {}; Cu.import('resource://mozmill/modules/errors.js', errors);
-var mozelement = {}; Cu.import('resource://mozmill/driver/mozelement.js', mozelement);
-var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
-var windows = {}; Cu.import('resource://mozmill/modules/windows.js', windows);
-
-// Declare most used utils functions in the controller namespace
-var assert = new assertions.Assert();
-var waitFor = assert.waitFor;
-
-var sleep = utils.sleep;
-
-// For Mozmill 1.5 backward compatibility
-var windowMap = windows.map;
-
-var waitForEvents = function () {
-}
-
-waitForEvents.prototype = {
-  /**
-   * Initialize list of events for given node
-   */
-  init: function waitForEvents_init(node, events) {
-    if (node.getNode != undefined)
-      node = node.getNode();
-
-    this.events = events;
-    this.node = node;
-    node.firedEvents = {};
-    this.registry = {};
-
-    if (!events) {
-      return;
-    }
-    for (var key in events) {
-      var e = events[key];
-      var listener = function (event) {
-        this.firedEvents[event.type] = true;
-      }
-
-      this.registry[e] = listener;
-      this.registry[e].result = false;
-      this.node.addEventListener(e, this.registry[e], true);
-    }
-  },
-
-  /**
-   * Wait until all assigned events have been fired
-   */
-  wait: function waitForEvents_wait(timeout, interval) {
-    for (var e in this.registry) {
-      assert.waitFor(function () {
-        return this.node.firedEvents[e] == true;
-      }, "waitForEvents.wait(): Event '" + e + "' has been fired.", timeout, interval);
-
-      this.node.removeEventListener(e, this.registry[e], true);
-    }
-  }
-}
-
-/**
- * Class to handle menus and context menus
- *
- * @constructor
- * @param {MozMillController} controller
- *        Mozmill controller of the window under test
- * @param {string} menuSelector
- *        jQuery like selector string of the element
- * @param {object} document
- *        Document to use for finding the menu
- *        [optional - default: aController.window.document]
- */
-var Menu = function (controller, menuSelector, document) {
-  this._controller = controller;
-  this._menu = null;
-
-  document = document || controller.window.document;
-  var node = document.querySelector(menuSelector);
-  if (node) {
-    // We don't unwrap nodes automatically yet (Bug 573185)
-    node = node.wrappedJSObject || node;
-    this._menu = new mozelement.Elem(node);
-  } else {
-    throw new Error("Menu element '" + menuSelector + "' not found.");
-  }
-}
-
-Menu.prototype = {
-
-  /**
-   * Open and populate the menu
-   *
-   * @param {ElemBase} contextElement
-   *        Element whose context menu has to be opened
-   * @returns {Menu} The Menu instance
-   */
-  open: function Menu_open(contextElement) {
-    // We have to open the context menu
-    var menu = this._menu.getNode();
-    if ((menu.localName == "popup" || menu.localName == "menupopup") &&
-        contextElement && contextElement.exists()) {
-      this._controller.rightClick(contextElement);
-      assert.waitFor(function () {
-        return menu.state == "open";
-      }, "Context menu has been opened.");
-    }
-
-    // Run through the entire menu and populate with dynamic entries
-    this._buildMenu(menu);
-
-    return this;
-  },
-
-  /**
-   * Close the menu
-   *
-   * @returns {Menu} The Menu instance
-   */
-  close: function Menu_close() {
-    var menu = this._menu.getNode();
-
-    this._controller.keypress(this._menu, "VK_ESCAPE", {});
-    assert.waitFor(function () {
-      return menu.state == "closed";
-    }, "Context menu has been closed.");
-
-    return this;
-  },
-
-  /**
-   * Retrieve the specified menu entry
-   *
-   * @param {string} itemSelector
-   *        jQuery like selector string of the menu item
-   * @returns {ElemBase} Menu element
-   * @throws Error If menu element has not been found
-   */
-  getItem: function Menu_getItem(itemSelector) {
-    // Run through the entire menu and populate with dynamic entries
-    this._buildMenu(this._menu.getNode());
-
-    var node = this._menu.getNode().querySelector(itemSelector);
-
-    if (!node) {
-      throw new Error("Menu entry '" + itemSelector + "' not found.");
-    }
-
-    return new mozelement.Elem(node);
-  },
-
-  /**
-   * Click the specified menu entry
-   *
-   * @param {string} itemSelector
-   *        jQuery like selector string of the menu item
-   *
-   * @returns {Menu} The Menu instance
-   */
-  click: function Menu_click(itemSelector) {
-    this._controller.click(this.getItem(itemSelector));
-
-    return this;
-  },
-
-  /**
-   * Synthesize a keypress against the menu
-   *
-   * @param {string} key
-   *        Key to press
-   * @param {object} modifier
-   *        Key modifiers
-   * @see MozMillController#keypress
-   *
-   * @returns {Menu} The Menu instance
-   */
-  keypress: function Menu_keypress(key, modifier) {
-    this._controller.keypress(this._menu, key, modifier);
-
-    return this;
-  },
-
-  /**
-   * Opens the context menu, click the specified entry and
-   * make sure that the menu has been closed.
-   *
-   * @param {string} itemSelector
-   *        jQuery like selector string of the element
-   * @param {ElemBase} contextElement
-   *        Element whose context menu has to be opened
-   *
-   * @returns {Menu} The Menu instance
-   */
-  select: function Menu_select(itemSelector, contextElement) {
-    this.open(contextElement);
-    this.click(itemSelector);
-    this.close();
-  },
-
-  /**
-   * Recursive function which iterates through all menu elements and
-   * populates the menus with dynamic menu entries.
-   *
-   * @param {node} menu
-   *        Top menu node whose elements have to be populated
-   */
-  _buildMenu: function Menu__buildMenu(menu) {
-    var items = menu ? menu.childNodes : null;
-
-    Array.forEach(items, function (item) {
-      // When we have a menu node, fake a click onto it to populate
-      // the sub menu with dynamic entries
-      if (item.tagName == "menu") {
-        var popup = item.querySelector("menupopup");
-
-        if (popup) {
-          var popupEvent = this._controller.window.document.createEvent("MouseEvent");
-          popupEvent.initMouseEvent("popupshowing", true, true,
-                                    this._controller.window, 0, 0, 0, 0, 0,
-                                    false, false, false, false, 0, null);
-          popup.dispatchEvent(popupEvent);
-
-          this._buildMenu(popup);
-        }
-      }
-    }, this);
-  }
-};
-
-var MozMillController = function (window) {
-  this.window = window;
-
-  this.mozmillModule = {};
-  Cu.import('resource://mozmill/driver/mozmill.js', this.mozmillModule);
-
-  var self = this;
-  assert.waitFor(function () {
-    return window != null && self.isLoaded();
-  }, "controller(): Window has been initialized.");
-
-  // Ensure to focus the window which will move it virtually into the foreground
-  // when focusmanager.testmode is set enabled.
-  this.window.focus();
-
-  var windowType = window.document.documentElement.getAttribute('windowtype');
-  if (controllerAdditions[windowType] != undefined ) {
-    this.prototype = new utils.Copy(this.prototype);
-    controllerAdditions[windowType](this);
-    this.windowtype = windowType;
-  }
-}
-
-/**
- * Returns the global browser object of the window
- *
- * @returns {Object} The browser object
- */
-MozMillController.prototype.__defineGetter__("browserObject", function () {
-  return utils.getBrowserObject(this.window);
-});
-
-// constructs a MozMillElement from the controller's window
-MozMillController.prototype.__defineGetter__("rootElement", function () {
-  if (this._rootElement == undefined) {
-    let docElement = this.window.document.documentElement;
-    this._rootElement = new mozelement.MozMillElement("Elem", docElement);
-  }
-
-  return this._rootElement;
-});
-
-MozMillController.prototype.sleep = utils.sleep;
-MozMillController.prototype.waitFor = assert.waitFor;
-
-// Open the specified url in the current tab
-MozMillController.prototype.open = function (url) {
-  switch (this.mozmillModule.Application) {
-    case "Firefox":
-      // Stop a running page load to not overlap requests
-      if (this.browserObject.selectedBrowser) {
-        this.browserObject.selectedBrowser.stop();
-      }
-
-      this.browserObject.loadURI(url);
-      break;
-
-    default:
-      throw new Error("MozMillController.open not supported.");
-  }
-
-  broker.pass({'function':'Controller.open()'});
-}
-
-/**
- * Take a screenshot of specified node
- *
- * @param {Element} node
- *        The window or DOM element to capture
- * @param {String} name
- *        The name of the screenshot used in reporting and as filename
- * @param {Boolean} save
- *        If true saves the screenshot as 'name.jpg' in tempdir,
- *        otherwise returns a dataURL
- * @param {Element[]} highlights
- *        A list of DOM elements to highlight by drawing a red rectangle around them
- *
- * @returns {Object} Object which contains properties like filename, dataURL,
- *          name and timestamp of the screenshot
- */
-MozMillController.prototype.screenshot = function (node, name, save, highlights) {
-  if (!node) {
-    throw new Error("node is undefined");
-  }
-
-  // Unwrap the node and highlights
-  if ("getNode" in node) {
-    node = node.getNode();
-  }
-
-  if (highlights) {
-    for (var i = 0; i < highlights.length; ++i) {
-      if ("getNode" in highlights[i]) {
-        highlights[i] = highlights[i].getNode();
-      }
-    }
-  }
-
-  // If save is false, a dataURL is used
-  // Include both in the report anyway to avoid confusion and make the report easier to parse
-  var screenshot = {"filename": undefined,
-                    "dataURL": utils.takeScreenshot(node, highlights),
-                    "name": name,
-                    "timestamp": new Date().toLocaleString()};
-
-  if (!save) {
-    return screenshot;
-  }
-
-  // Save the screenshot to disk
-
-  let {filename, failure} = utils.saveDataURL(screenshot.dataURL, name);
-  screenshot.filename = filename;
-  screenshot.failure = failure;
-
-  if (failure) {
-    broker.log({'function': 'controller.screenshot()',
-                'message': 'Error writing to file: ' + screenshot.filename});
-  } else {
-    // Send the screenshot object to python over jsbridge
-    broker.sendMessage("screenshot", screenshot);
-    broker.pass({'function': 'controller.screenshot()'});
-  }
-
-  return screenshot;
-}
-
-/**
- * Checks if the specified window has been loaded
- *
- * @param {DOMWindow} [aWindow=this.window] Window object to check for loaded state
- */
-MozMillController.prototype.isLoaded = function (aWindow) {
-  var win = aWindow || this.window;
-
-  return windows.map.getValue(utils.getWindowId(win), "loaded") || false;
-};
-
-MozMillController.prototype.__defineGetter__("waitForEvents", function () {
-  if (this._waitForEvents == undefined) {
-    this._waitForEvents = new waitForEvents();
-  }
-
-  return this._waitForEvents;
-});
-
-/**
- * Wrapper function to create a new instance of a menu
- * @see Menu
- */
-MozMillController.prototype.getMenu = function (menuSelector, document) {
-  return new Menu(this, menuSelector, document);
-};
-
-MozMillController.prototype.__defineGetter__("mainMenu", function () {
-  return this.getMenu("menubar");
-});
-
-MozMillController.prototype.__defineGetter__("menus", function () {
-  logDeprecated('controller.menus', 'Use controller.mainMenu instead');
-});
-
-MozMillController.prototype.waitForImage = function (aElement, timeout, interval) {
-  this.waitFor(function () {
-    return aElement.getNode().complete == true;
-  }, "timeout exceeded for waitForImage " + aElement.getInfo(), timeout, interval);
-
-  broker.pass({'function':'Controller.waitForImage()'});
-}
-
-MozMillController.prototype.startUserShutdown = function (timeout, restart, next, resetProfile) {
-  if (restart && resetProfile) {
-    throw new Error("You can't have a user-restart and reset the profile; there is a race condition");
-  }
-
-  let shutdownObj = {
-    'user': true,
-    'restart': Boolean(restart),
-    'next': next,
-    'resetProfile': Boolean(resetProfile),
-    'timeout': timeout
-  };
-
-  broker.sendMessage('shutdown', shutdownObj);
-}
-
-/**
- * Restart the application
- *
- * @param {string} aNext
- *        Name of the next test function to run after restart
- * @param {boolean} [aFlags=undefined]
- *        Additional flags how to handle the shutdown or restart.
- * @see https://developer.mozilla.org/nsIAppStartup#Attributes
- */
-MozMillController.prototype.restartApplication = function (aNext, aFlags) {
-  var flags = Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart;
-
-  if (aFlags) {
-    flags |= aFlags;
-  }
-
-  broker.sendMessage('shutdown', {'user': false,
-                                  'restart': true,
-                                  'flags': flags,
-                                  'next': aNext,
-                                  'timeout': 0 });
-
-  // We have to ensure to stop the test from continuing until the application is
-  // shutting down. The only way to do that is by throwing an exception.
-  throw new errors.ApplicationQuitError();
-}
-
-/**
- * Stop the application
- *
- * @param {boolean} [aResetProfile=false]
- *        Whether to reset the profile during restart
- * @param {boolean} [aFlags=undefined]
- *        Additional flags how to handle the shutdown or restart.
- * @see https://developer.mozilla.org/nsIAppStartup#Attributes
- */
-MozMillController.prototype.stopApplication = function (aResetProfile, aFlags) {
-  var flags = Ci.nsIAppStartup.eAttemptQuit;
-
-  if (aFlags) {
-    flags |= aFlags;
-  }
-
-  broker.sendMessage('shutdown', {'user': false,
-                                  'restart': false,
-                                  'flags': flags,
-                                  'resetProfile': aResetProfile,
-                                  'timeout': 0 });
-
-  // We have to ensure to stop the test from continuing until the application is
-  // shutting down. The only way to do that is by throwing an exception.
-  throw new errors.ApplicationQuitError();
-}
-
-//Browser navigation functions
-MozMillController.prototype.goBack = function () {
-  this.window.content.history.back();
-  broker.pass({'function':'Controller.goBack()'});
-
-  return true;
-}
-
-MozMillController.prototype.goForward = function () {
-  this.window.content.history.forward();
-  broker.pass({'function':'Controller.goForward()'});
-
-  return true;
-}
-
-MozMillController.prototype.refresh = function () {
-  this.window.content.location.reload(true);
-  broker.pass({'function':'Controller.refresh()'});
-
-  return true;
-}
-
-function logDeprecated(funcName, message) {
-  broker.log({'function': funcName + '() - DEPRECATED',
-              'message': funcName + '() is deprecated. ' + message});
-}
-
-function logDeprecatedAssert(funcName) {
-   logDeprecated('controller.' + funcName,
-                 '. Use the generic `assertion` module instead.');
-}
-
-MozMillController.prototype.assertText = function (el, text) {
-  logDeprecatedAssert("assertText");
-
-  var n = el.getNode();
-
-  if (n && n.innerHTML == text) {
-    broker.pass({'function': 'Controller.assertText()'});
-  } else {
-    throw new Error("could not validate element " + el.getInfo() +
-                    " with text "+ text);
-  }
-
-  return true;
-};
-
-/**
- * Assert that a specified node exists
- */
-MozMillController.prototype.assertNode = function (el) {
-  logDeprecatedAssert("assertNode");
-
-  //this.window.focus();
-  var element = el.getNode();
-  if (!element) {
-    throw new Error("could not find element " + el.getInfo());
-  }
-
-  broker.pass({'function': 'Controller.assertNode()'});
-  return true;
-};
-
-/**
- * Assert that a specified node doesn't exist
- */
-MozMillController.prototype.assertNodeNotExist = function (el) {
-  logDeprecatedAssert("assertNodeNotExist");
-
-  try {
-    var element = el.getNode();
-  } catch (e) {
-    broker.pass({'function': 'Controller.assertNodeNotExist()'});
-  }
-
-  if (element) {
-    throw new Error("Unexpectedly found element " + el.getInfo());
-  } else {
-    broker.pass({'function':'Controller.assertNodeNotExist()'});
-  }
-
-  return true;
-};
-
-/**
- * Assert that a form element contains the expected value
- */
-MozMillController.prototype.assertValue = function (el, value) {
-  logDeprecatedAssert("assertValue");
-
-  var n = el.getNode();
-
-  if (n && n.value == value) {
-    broker.pass({'function': 'Controller.assertValue()'});
-  } else {
-    throw new Error("could not validate element " + el.getInfo() +
-                    " with value " + value);
-  }
-
-  return false;
-};
-
-/**
- * Check if the callback function evaluates to true
- */
-MozMillController.prototype.assert = function (callback, message, thisObject) {
-  logDeprecatedAssert("assert");
-
-  utils.assert(callback, message, thisObject);
-  broker.pass({'function': ": controller.assert('" + callback + "')"});
-
-  return true;
-}
-
-/**
- * Assert that a provided value is selected in a select element
- */
-MozMillController.prototype.assertSelected = function (el, value) {
-  logDeprecatedAssert("assertSelected");
-
-  var n = el.getNode();
-  var validator = value;
-
-  if (n && n.options[n.selectedIndex].value == validator) {
-    broker.pass({'function':'Controller.assertSelected()'});
-  } else {
-    throw new Error("could not assert value for element " + el.getInfo() +
-                    " with value " + value);
-  }
-
-  return true;
-};
-
-/**
- * Assert that a provided checkbox is checked
- */
-MozMillController.prototype.assertChecked = function (el) {
-  logDeprecatedAssert("assertChecked");
-
-  var element = el.getNode();
-
-  if (element && element.checked == true) {
-    broker.pass({'function':'Controller.assertChecked()'});
-  } else {
-    throw new Error("assert failed for checked element " + el.getInfo());
-  }
-
-  return true;
-};
-
-/**
- * Assert that a provided checkbox is not checked
- */
-MozMillController.prototype.assertNotChecked = function (el) {
-  logDeprecatedAssert("assertNotChecked");
-
-  var element = el.getNode();
-
-  if (!element) {
-    throw new Error("Could not find element" + el.getInfo());
-  }
-
-  if (!element.hasAttribute("checked") || element.checked != true) {
-    broker.pass({'function': 'Controller.assertNotChecked()'});
-  } else {
-    throw new Error("assert failed for not checked element " + el.getInfo());
-  }
-
-  return true;
-};
-
-/**
- * Assert that an element's javascript property exists or has a particular value
- *
- * if val is undefined, will return true if the property exists.
- * if val is specified, will return true if the property exists and has the correct value
- */
-MozMillController.prototype.assertJSProperty = function (el, attrib, val) {
-  logDeprecatedAssert("assertJSProperty");
-
-  var element = el.getNode();
-
-  if (!element){
-    throw new Error("could not find element " + el.getInfo());
-  }
-
-  var value = element[attrib];
-  var res = (value !== undefined && (val === undefined ? true :
-                                                         String(value) == String(val)));
-  if (res) {
-    broker.pass({'function':'Controller.assertJSProperty("' + el.getInfo() + '") : ' + val});
-  } else {
-    throw new Error("Controller.assertJSProperty(" + el.getInfo() + ") : " +
-                    (val === undefined ? "property '" + attrib +
-                    "' doesn't exist" : val + " == " + value));
-  }
-
-  return true;
-};
-
-/**
- * Assert that an element's javascript property doesn't exist or doesn't have a particular value
- *
- * if val is undefined, will return true if the property doesn't exist.
- * if val is specified, will return true if the property doesn't exist or doesn't have the specified value
- */
-MozMillController.prototype.assertNotJSProperty = function (el, attrib, val) {
-  logDeprecatedAssert("assertNotJSProperty");
-
-  var element = el.getNode();
-
-  if (!element){
-    throw new Error("could not find element " + el.getInfo());
-  }
-
-  var value = element[attrib];
-  var res = (val === undefined ? value === undefined : String(value) != String(val));
-  if (res) {
-    broker.pass({'function':'Controller.assertNotProperty("' + el.getInfo() + '") : ' + val});
-  } else {
-    throw new Error("Controller.assertNotJSProperty(" + el.getInfo() + ") : " +
-                    (val === undefined ? "property '" + attrib +
-                    "' exists" : val + " != " + value));
-  }
-
-  return true;
-};
-
-/**
- * Assert that an element's dom property exists or has a particular value
- *
- * if val is undefined, will return true if the property exists.
- * if val is specified, will return true if the property exists and has the correct value
- */
-MozMillController.prototype.assertDOMProperty = function (el, attrib, val) {
-  logDeprecatedAssert("assertDOMProperty");
-
-  var element = el.getNode();
-
-  if (!element){
-    throw new Error("could not find element " + el.getInfo());
-  }
-
-  var value, res = element.hasAttribute(attrib);
-  if (res && val !== undefined) {
-    value = element.getAttribute(attrib);
-    res = (String(value) == String(val));
-  }
-
-  if (res) {
-    broker.pass({'function':'Controller.assertDOMProperty("' + el.getInfo() + '") : ' + val});
-  } else {
-    throw new Error("Controller.assertDOMProperty(" + el.getInfo() + ") : " +
-                    (val === undefined ? "property '" + attrib +
-                    "' doesn't exist" : val + " == " + value));
-  }
-
-  return true;
-};
-
-/**
- * Assert that an element's dom property doesn't exist or doesn't have a particular value
- *
- * if val is undefined, will return true if the property doesn't exist.
- * if val is specified, will return true if the property doesn't exist or doesn't have the specified value
- */
-MozMillController.prototype.assertNotDOMProperty = function (el, attrib, val) {
-  logDeprecatedAssert("assertNotDOMProperty");
-
-  var element = el.getNode();
-
-  if (!element) {
-    throw new Error("could not find element " + el.getInfo());
-  }
-
-  var value, res = element.hasAttribute(attrib);
-  if (res && val !== undefined) {
-    value = element.getAttribute(attrib);
-    res = (String(value) == String(val));
-  }
-
-  if (!res) {
-    broker.pass({'function':'Controller.assertNotDOMProperty("' + el.getInfo() + '") : ' + val});
-  } else {
-    throw new Error("Controller.assertNotDOMProperty(" + el.getInfo() + ") : " +
-                    (val == undefined ? "property '" + attrib +
-                    "' exists" : val + " == " + value));
-  }
-
-  return true;
-};
-
-/**
- * Assert that a specified image has actually loaded. The Safari workaround results
- * in additional requests for broken images (in Safari only) but works reliably
- */
-MozMillController.prototype.assertImageLoaded = function (el) {
-  logDeprecatedAssert("assertImageLoaded");
-
-  var img = el.getNode();
-
-  if (!img || img.tagName != 'IMG') {
-    throw new Error('Controller.assertImageLoaded() failed.')
-    return false;
-  }
-
-  var comp = img.complete;
-  var ret = null; // Return value
-
-  // Workaround for Safari -- it only supports the
-  // complete attrib on script-created images
-  if (typeof comp == 'undefined') {
-    let test = new Image();
-    // If the original image was successfully loaded,
-    // src for new one should be pulled from cache
-    test.src = img.src;
-    comp = test.complete;
-  }
-
-  // Check the complete attrib. Note the strict
-  // equality check -- we don't want undefined, null, etc.
-  // --------------------------
-  if (comp === false) {
-    // False -- Img failed to load in IE/Safari, or is
-    // still trying to load in FF
-    ret = false;
-  } else if (comp === true && img.naturalWidth == 0) {
-    // True, but image has no size -- image failed to
-    // load in FF
-    ret = false;
-  } else {
-    // Otherwise all we can do is assume everything's
-    // hunky-dory
-   ret = true;
-  }
-
-  if (ret) {
-    broker.pass({'function':'Controller.assertImageLoaded'});
-  } else {
-    throw new Error('Controller.assertImageLoaded() failed.')
-  }
-
-  return true;
-};
-
-/**
- * Drag one element to the top x,y coords of another specified element
- */
-MozMillController.prototype.mouseMove = function (doc, start, dest) {
-  // if one of these elements couldn't be looked up
-  if (typeof start != 'object'){
-    throw new Error("received bad coordinates");
-  }
-
-  if (typeof dest != 'object'){
-    throw new Error("received bad coordinates");
-  }
-
-  var triggerMouseEvent = function (element, clientX, clientY) {
-    clientX = clientX ? clientX: 0;
-    clientY = clientY ? clientY: 0;
-
-    // make the mouse understand where it is on the screen
-    var screenX = element.boxObject.screenX ? element.boxObject.screenX : 0;
-    var screenY = element.boxObject.screenY ? element.boxObject.screenY : 0;
-
-    var evt = element.ownerDocument.createEvent('MouseEvents');
-    if (evt.initMouseEvent) {
-      evt.initMouseEvent('mousemove', true, true, element.ownerGlobal,
-                         1, screenX, screenY, clientX, clientY);
-    } else {
-      evt.initEvent('mousemove', true, true);
-    }
-
-    element.dispatchEvent(evt);
-  };
-
-  // Do the initial move to the drag element position
-  triggerMouseEvent(doc.body, start[0], start[1]);
-  triggerMouseEvent(doc.body, dest[0], dest[1]);
-
-  broker.pass({'function':'Controller.mouseMove()'});
-  return true;
-}
-
-/**
- * Drag an element to the specified offset on another element, firing mouse and
- * drag events. Adapted from EventUtils.js synthesizeDrop()
- *
- * @deprecated Use the MozMillElement object
- *
- * @param {MozElement} aSrc
- *        Source element to be dragged
- * @param {MozElement} aDest
- *        Destination element over which the drop occurs
- * @param {Number} [aOffsetX=element.width/2]
- *        Relative x offset for dropping on the aDest element
- * @param {Number} [aOffsetY=element.height/2]
- *        Relative y offset for dropping on the aDest element
- * @param {DOMWindow} [aSourceWindow=this.element.ownerDocument.defaultView]
- *        Custom source Window to be used.
- * @param {String} [aDropEffect="move"]
- *        Effect used for the drop event
- * @param {Object[]} [aDragData]
- *        An array holding custom drag data to be used during the drag event
- *        Format: [{ type: "text/plain", "Text to drag"}, ...]
- *
- * @returns {String} the captured dropEffect
- */
-MozMillController.prototype.dragToElement = function (aSrc, aDest, aOffsetX,
-                                                      aOffsetY, aSourceWindow,
-                                                      aDropEffect, aDragData) {
-  logDeprecated("controller.dragToElement", "Use the MozMillElement object.");
-  return aSrc.dragToElement(aDest, aOffsetX, aOffsetY, aSourceWindow, null,
-                            aDropEffect, aDragData);
-};
-
-function Tabs(controller) {
-  this.controller = controller;
-}
-
-Tabs.prototype.getTab = function (index) {
-  return this.controller.browserObject.browsers[index].contentDocument;
-}
-
-Tabs.prototype.__defineGetter__("activeTab", function () {
-  return this.controller.browserObject.selectedBrowser.contentDocument;
-});
-
-Tabs.prototype.selectTab = function (index) {
-  // GO in to tab manager and grab the tab by index and call focus.
-}
-
-Tabs.prototype.findWindow = function (doc) {
-  for (var i = 0; i <= (this.controller.window.frames.length - 1); i++) {
-    if (this.controller.window.frames[i].document == doc) {
-      return this.controller.window.frames[i];
-    }
-  }
-
-  throw new Error("Cannot find window for document. Doc title == " + doc.title);
-}
-
-Tabs.prototype.getTabWindow = function (index) {
-  return this.findWindow(this.getTab(index));
-}
-
-Tabs.prototype.__defineGetter__("activeTabWindow", function () {
-  return this.findWindow(this.activeTab);
-});
-
-Tabs.prototype.__defineGetter__("length", function () {
-  return this.controller.browserObject.browsers.length;
-});
-
-Tabs.prototype.__defineGetter__("activeTabIndex", function () {
-  var browser = this.controller.browserObject;
-  return browser.tabContainer.selectedIndex;
-});
-
-Tabs.prototype.selectTabIndex = function (aIndex) {
-  var browser = this.controller.browserObject;
-  browser.selectTabAtIndex(aIndex);
-}
-
-function browserAdditions (controller) {
-  controller.tabs = new Tabs(controller);
-
-  controller.waitForPageLoad = function (aDocument, aTimeout, aInterval) {
-    var timeout = aTimeout || 30000;
-    var win = null;
-    var timed_out = false;
-
-    // If a user tries to do waitForPageLoad(2000), this will assign the
-    // interval the first arg which is most likely what they were expecting
-    if (typeof(aDocument) == "number"){
-      timeout = aDocument;
-    }
-
-    // If we have a real document use its default view
-    if (aDocument && (typeof(aDocument) === "object") &&
-        "defaultView" in aDocument)
-      win = aDocument.defaultView;
-
-    // If no document has been specified, fallback to the default view of the
-    // currently selected tab browser
-    win = win || this.browserObject.selectedBrowser.contentWindow;
-
-    // Wait until the content in the tab has been loaded
-    try {
-      this.waitFor(function () {
-        return windows.map.hasPageLoaded(utils.getWindowId(win));
-      }, "Timeout", timeout, aInterval);
-    }
-    catch (ex) {
-      if (!(ex instanceof errors.TimeoutError)) {
-        throw ex;
-      }
-      timed_out = true;
-    }
-    finally {
-      let state = 'URI=' + win.document.location.href +
-              ', readyState=' + win.document.readyState;
-      let message = "controller.waitForPageLoad(" + state + ")";
-
-      if (timed_out) {
-        throw new errors.AssertionError(message);
-      }
-
-      broker.pass({'function': message});
-    }
-  }
-}
-
-var controllerAdditions = {
-  'navigator:browser'  :browserAdditions
-};
-
-/**
- *  DEPRECATION WARNING
- *
- * The following methods have all been DEPRECATED as of Mozmill 2.0
- */
-MozMillController.prototype.assertProperty = function (el, attrib, val) {
-  logDeprecatedAssert("assertProperty");
-
-  return this.assertJSProperty(el, attrib, val);
-};
-
-MozMillController.prototype.assertPropertyNotExist = function (el, attrib) {
-  logDeprecatedAssert("assertPropertyNotExist");
-  return this.assertNotJSProperty(el, attrib);
-};
-
-/**
- *  DEPRECATION WARNING
- *
- * The following methods have all been DEPRECATED as of Mozmill 2.0
- * Use the MozMillElement object instead (https://developer.mozilla.org/en/Mozmill/Mozmill_Element_Object)
- */
-MozMillController.prototype.select = function (aElement, index, option, value) {
-  logDeprecated("controller.select", "Use the MozMillElement object.");
-
-  return aElement.select(index, option, value);
-};
-
-MozMillController.prototype.keypress = function (aElement, aKey, aModifiers, aExpectedEvent) {
-  logDeprecated("controller.keypress", "Use the MozMillElement object.");
-
-  if (!aElement) {
-    aElement = new mozelement.MozMillElement("Elem", this.window);
-  }
-
-  return aElement.keypress(aKey, aModifiers, aExpectedEvent);
-}
-
-MozMillController.prototype.type = function (aElement, aText, aExpectedEvent) {
-  logDeprecated("controller.type", "Use the MozMillElement object.");
-
-  if (!aElement) {
-    aElement = new mozelement.MozMillElement("Elem", this.window);
-  }
-
-  var that = this;
-  var retval = true;
-  Array.forEach(aText, function (letter) {
-    if (!that.keypress(aElement, letter, {}, aExpectedEvent)) {
-      retval = false; }
-  });
-
-  return retval;
-}
-
-MozMillController.prototype.mouseEvent = function (aElement, aOffsetX, aOffsetY, aEvent, aExpectedEvent) {
-  logDeprecated("controller.mouseEvent", "Use the MozMillElement object.");
-
-  return aElement.mouseEvent(aOffsetX, aOffsetY, aEvent, aExpectedEvent);
-}
-
-MozMillController.prototype.click = function (aElement, left, top, expectedEvent) {
-  logDeprecated("controller.click", "Use the MozMillElement object.");
-
-  return aElement.click(left, top, expectedEvent);
-}
-
-MozMillController.prototype.doubleClick = function (aElement, left, top, expectedEvent) {
-  logDeprecated("controller.doubleClick", "Use the MozMillElement object.");
-
-  return aElement.doubleClick(left, top, expectedEvent);
-}
-
-MozMillController.prototype.mouseDown = function (aElement, button, left, top, expectedEvent) {
-  logDeprecated("controller.mouseDown", "Use the MozMillElement object.");
-
-  return aElement.mouseDown(button, left, top, expectedEvent);
-};
-
-MozMillController.prototype.mouseOut = function (aElement, button, left, top, expectedEvent) {
-  logDeprecated("controller.mouseOut", "Use the MozMillElement object.");
-
-  return aElement.mouseOut(button, left, top, expectedEvent);
-};
-
-MozMillController.prototype.mouseOver = function (aElement, button, left, top, expectedEvent) {
-  logDeprecated("controller.mouseOver", "Use the MozMillElement object.");
-
-  return aElement.mouseOver(button, left, top, expectedEvent);
-};
-
-MozMillController.prototype.mouseUp = function (aElement, button, left, top, expectedEvent) {
-  logDeprecated("controller.mouseUp", "Use the MozMillElement object.");
-
-  return aElement.mouseUp(button, left, top, expectedEvent);
-};
-
-MozMillController.prototype.middleClick = function (aElement, left, top, expectedEvent) {
-  logDeprecated("controller.middleClick", "Use the MozMillElement object.");
-
-  return aElement.middleClick(aElement, left, top, expectedEvent);
-}
-
-MozMillController.prototype.rightClick = function (aElement, left, top, expectedEvent) {
-  logDeprecated("controller.rightClick", "Use the MozMillElement object.");
-
-  return aElement.rightClick(left, top, expectedEvent);
-}
-
-MozMillController.prototype.check = function (aElement, state) {
-  logDeprecated("controller.check", "Use the MozMillElement object.");
-
-  return aElement.check(state);
-}
-
-MozMillController.prototype.radio = function (aElement) {
-  logDeprecated("controller.radio", "Use the MozMillElement object.");
-
-  return aElement.select();
-}
-
-MozMillController.prototype.waitThenClick = function (aElement, timeout, interval) {
-  logDeprecated("controller.waitThenClick", "Use the MozMillElement object.");
-
-  return aElement.waitThenClick(timeout, interval);
-}
-
-MozMillController.prototype.waitForElement = function (aElement, timeout, interval) {
-  logDeprecated("controller.waitForElement", "Use the MozMillElement object.");
-
-  return aElement.waitForElement(timeout, interval);
-}
-
-MozMillController.prototype.waitForElementNotPresent = function (aElement, timeout, interval) {
-  logDeprecated("controller.waitForElementNotPresent", "Use the MozMillElement object.");
-
-  return aElement.waitForElementNotPresent(timeout, interval);
-}
deleted file mode 100644
--- a/services/sync/tps/extensions/mozmill/resource/driver/elementslib.js
+++ /dev/null
@@ -1,537 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var EXPORTED_SYMBOLS = ["ID", "Link", "XPath", "Selector", "Name", "Anon", "AnonXPath",
-                        "Lookup", "_byID", "_byName", "_byAttrib", "_byAnonAttrib",
-                       ];
-
-var Cc = Components.classes;
-var Ci = Components.interfaces;
-var Cu = Components.utils;
-
-Cu.import("resource://gre/modules/Services.jsm");
-
-var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
-var strings = {}; Cu.import('resource://mozmill/stdlib/strings.js', strings);
-var arrays = {}; Cu.import('resource://mozmill/stdlib/arrays.js', arrays);
-var json2 = {}; Cu.import('resource://mozmill/stdlib/json2.js', json2);
-var withs = {}; Cu.import('resource://mozmill/stdlib/withs.js', withs);
-var dom = {}; Cu.import('resource://mozmill/stdlib/dom.js', dom);
-var objects = {}; Cu.import('resource://mozmill/stdlib/objects.js', objects);
-
-var countQuotes = function (str) {
-  var count = 0;
-  var i = 0;
-
-  while (i < str.length) {
-    i = str.indexOf('"', i);
-    if (i != -1) {
-      count++;
-      i++;
-    } else {
-      break;
-    }
-  }
-
-  return count;
-};
-
-/**
- * smartSplit()
- *
- * Takes a lookup string as input and returns
- * a list of each node in the string
- */
-var smartSplit = function (str) {
-  // Ensure we have an even number of quotes
-  if (countQuotes(str) % 2 != 0) {
-    throw new Error ("Invalid Lookup Expression");
-  }
-
-  /**
-   * This regex matches a single "node" in a lookup string.
-   * In otherwords, it matches the part between the two '/'s
-   *
-   * Regex Explanation:
-   * \/ - start matching at the first forward slash
-   * ([^\/"]*"[^"]*")* - match as many pairs of quotes as possible until we hit a slash (ignore slashes inside quotes)
-   * [^\/]* - match the remainder of text outside of last quote but before next slash
-   */
-  var re = /\/([^\/"]*"[^"]*")*[^\/]*/g
-  var ret = []
-  var match = re.exec(str);
-
-  while (match != null) {
-    ret.push(match[0].replace(/^\//, ""));
-    match = re.exec(str);
-  }
-
-  return ret;
-};
-
-/**
- * defaultDocuments()
- *
- * Returns a list of default documents in which to search for elements
- * if no document is provided
- */
-function defaultDocuments() {
-  var win = Services.wm.getMostRecentWindow("navigator:browser");
-
-  return [
-    win.document,
-    utils.getBrowserObject(win).selectedBrowser.contentWindow.document
-  ];
-};
-
-/**
- * nodeSearch()
- *
- * Takes an optional document, callback and locator string
- * Returns a handle to the located element or null
- */
-function nodeSearch(doc, func, string) {
-  if (doc != undefined) {
-    var documents = [doc];
-  } else {
-    var documents = defaultDocuments();
-  }
-
-  var e = null;
-  var element = null;
-
-  //inline function to recursively find the element in the DOM, cross frame.
-  var search = function (win, func, string) {
-    if (win == null) {
-      return;
-    }
-
-    //do the lookup in the current window
-    element = func.call(win, string);
-
-    if (!element || (element.length == 0)) {
-      var frames = win.frames;
-      for (var i = 0; i < frames.length; i++) {
-        search(frames[i], func, string);
-      }
-    } else {
-      e = element;
-    }
-  };
-
-  for (var i = 0; i < documents.length; ++i) {
-    var win = documents[i].defaultView;
-    search(win, func, string);
-    if (e) {
-      break;
-    }
-  }
-
-  return e;
-};
-
-/**
- * Selector()
- *
- * Finds an element by selector string
- */
-function Selector(_document, selector, index) {
-  if (selector == undefined) {
-    throw new Error('Selector constructor did not recieve enough arguments.');
-  }
-
-  this.selector = selector;
-
-  this.getNodeForDocument = function (s) {
-    return this.document.querySelectorAll(s);
-  };
-
-  var nodes = nodeSearch(_document, this.getNodeForDocument, this.selector);
-
-  return nodes ? nodes[index || 0] : null;
-};
-
-/**
- * ID()
- *
- * Finds an element by ID
- */
-function ID(_document, nodeID) {
-  if (nodeID == undefined) {
-    throw new Error('ID constructor did not recieve enough arguments.');
-  }
-
-  this.getNodeForDocument = function (nodeID) {
-    return this.document.getElementById(nodeID);
-  };
-
-  return nodeSearch(_document, this.getNodeForDocument, nodeID);
-};
-
-/**
- * Link()
- *
- * Finds a link by innerHTML
- */
-function Link(_document, linkName) {
-  if (linkName == undefined) {
-    throw new Error('Link constructor did not recieve enough arguments.');
-  }
-
-  this.getNodeForDocument = function (linkName) {
-    var getText = function (el) {
-      var text = "";
-
-      if (el.nodeType == 3) { //textNode
-        if (el.data != undefined) {
-          text = el.data;
-        } else {
-          text = el.innerHTML;
-        }
-
-        text = text.replace(/n|r|t/g, " ");
-      }
-      else if (el.nodeType == 1) { //elementNode
-        for (var i = 0; i < el.childNodes.length; i++) {
-          var child = el.childNodes.item(i);
-          text += getText(child);
-        }
-
-        if (el.tagName == "P" || el.tagName == "BR" ||
-            el.tagName == "HR" || el.tagName == "DIV") {
-          text += "\n";
-        }
-      }
-
-      return text;
-    };
-
-    //sometimes the windows won't have this function
-    try {
-      var links = this.document.getElementsByTagName('a');
-    } catch (e) {
-      // ADD LOG LINE mresults.write('Error: '+ e, 'lightred');
-    }
-
-    for (var i = 0; i < links.length; i++) {
-      var el = links[i];
-      //if (getText(el).indexOf(this.linkName) != -1) {
-      if (el.innerHTML.indexOf(linkName) != -1) {
-        return el;
-      }
-    }
-
-    return null;
-  };
-
-  return nodeSearch(_document, this.getNodeForDocument, linkName);
-};
-
-/**
- * XPath()
- *
- * Finds an element by XPath
- */
-function XPath(_document, expr) {
-  if (expr == undefined) {
-    throw new Error('XPath constructor did not recieve enough arguments.');
-  }
-
-  this.getNodeForDocument = function (s) {
-    var aNode = this.document;
-    var aExpr = s;
-    var xpe = null;
-
-    if (this.document.defaultView == null) {
-      xpe = new getMethodInWindows('XPathEvaluator')();
-    } else {
-      xpe = new this.document.defaultView.XPathEvaluator();
-    }
-
-    var nsResolver = xpe.createNSResolver(aNode.ownerDocument == null ? aNode.documentElement
-                                                                      : aNode.ownerDocument.documentElement);
-    var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
-    var found = [];
-    var res;
-
-    while (res = result.iterateNext()) {
-      found.push(res);
-    }
-
-    return found[0];
-  };
-
-  return nodeSearch(_document, this.getNodeForDocument, expr);
-};
-
-/**
- * Name()
- *
- * Finds an element by Name
- */
-function Name(_document, nName) {
-  if (nName == undefined) {
-    throw new Error('Name constructor did not recieve enough arguments.');
-  }
-
-  this.getNodeForDocument = function (s) {
-    try{
-      var els = this.document.getElementsByName(s);
-      if (els.length > 0) {
-        return els[0];
-      }
-    } catch (e) {
-    }
-
-    return null;
-  };
-
-  return nodeSearch(_document, this.getNodeForDocument, nName);
-};
-
-
-var _returnResult = function (results) {
-  if (results.length == 0) {
-    return null
-  }
-  else if (results.length == 1) {
-    return results[0];
-  } else {
-    return results;
-  }
-}
-
-var _forChildren = function (element, name, value) {
-  var results = [];
-  var nodes = Array.from(element.childNodes).filter(e => e);
-
-  for (var i in nodes) {
-    var n = nodes[i];
-    if (n[name] == value) {
-      results.push(n);
-    }
-  }
-
-  return results;
-}
-
-var _forAnonChildren = function (_document, element, name, value) {
-  var results = [];
-  var nodes = Array.from(_document.getAnoymousNodes(element)).filter(e => e);
-
-  for (var i in nodes ) {
-    var n = nodes[i];
-    if (n[name] == value) {
-      results.push(n);
-    }
-  }
-
-  return results;
-}
-
-var _byID = function (_document, parent, value) {
-  return _returnResult(_forChildren(parent, 'id', value));
-}
-
-var _byName = function (_document, parent, value) {
-  return _returnResult(_forChildren(parent, 'tagName', value));
-}
-
-var _byAttrib = function (parent, attributes) {
-  var results = [];
-  var nodes = parent.childNodes;
-
-  for (var i in nodes) {
-    var n = nodes[i];
-    let requirementPass = 0;
-    let requirementLength = 0;
-
-    for (var a in attributes) {
-      requirementLength++;
-      try {
-        if (n.getAttribute(a) == attributes[a]) {
-          requirementPass++;
-        }
-      } catch (e) {
-        // Workaround any bugs in custom attribute crap in XUL elements
-      }
-    }
-
-    if (requirementPass == requirementLength) {
-      results.push(n);
-    }
-  }
-
-  return _returnResult(results)
-}
-
-var _byAnonAttrib = function (_document, parent, attributes) {
-  var results = [];
-
-  if (objects.getLength(attributes) == 1) {
-    for (var i in attributes) {
-      var k = i;
-      var v = attributes[i];
-    }
-
-    var result = _document.getAnonymousElementByAttribute(parent, k, v);
-    if (result) {
-      return result;
-    }
-  }
-
-  var nodes = Array.from(_document.getAnonymousNodes(parent)).filter(n => n.getAttribute);
-
-  function resultsForNodes (nodes) {
-    for (var i in nodes) {
-      var n = nodes[i];
-      requirementPass = 0;
-      requirementLength = 0;
-
-      for (var a in attributes) {
-        requirementLength++;
-        if (n.getAttribute(a) == attributes[a]) {
-          requirementPass++;
-        }
-      }
-
-      if (requirementPass == requirementLength) {
-        results.push(n);
-      }
-    }
-  }
-
-  resultsForNodes(nodes);
-  if (results.length == 0) {
-    resultsForNodes(Array.from(parent.childNodes).filter(n => n != undefined && n.getAttribute));
-  }
-
-  return _returnResult(results)
-}
-
-var _byIndex = function (_document, parent, i) {
-  if (parent instanceof Array) {
-    return parent[i];
-  }
-
-  return parent.childNodes[i];
-}
-
-var _anonByName = function (_document, parent, value) {
-  return _returnResult(_forAnonChildren(_document, parent, 'tagName', value));
-}
-
-var _anonByAttrib = function (_document, parent, value) {
-  return _byAnonAttrib(_document, parent, value);
-}
-
-var _anonByIndex = function (_document, parent, i) {
-  return _document.getAnonymousNodes(parent)[i];
-}
-
-/**
- * Lookup()
- *
- * Finds an element by Lookup expression
- */
-function Lookup(_document, expression) {
-  if (expression == undefined) {
-    throw new Error('Lookup constructor did not recieve enough arguments.');
-  }
-
-  var expSplit = smartSplit(expression).filter(e => e != '');
-  expSplit.unshift(_document);
-
-  var nCases = {'id':_byID, 'name':_byName, 'attrib':_byAttrib, 'index':_byIndex};
-  var aCases = {'name':_anonByName, 'attrib':_anonByAttrib, 'index':_anonByIndex};
-
-  /**
-   * Reduces the lookup expression
-   * @param {Object} parentNode
-   *        Parent node (previousValue of the formerly executed reduce callback)
-   * @param {String} exp
-   *        Lookup expression for the parents child node
-   *
-   * @returns {Object} Node found by the given expression
-   */
-  var reduceLookup = function (parentNode, exp) {
-    // Abort in case the parent node was not found
-    if (!parentNode) {
-      return false;
-    }
-
-    // Handle case where only index is provided
-    var cases = nCases;
-
-    // Handle ending index before any of the expression gets mangled
-    if (withs.endsWith(exp, ']')) {
-      var expIndex = json2.JSON.parse(strings.vslice(exp, '[', ']'));
-    }
-
-    // Handle anon
-    if (withs.startsWith(exp, 'anon')) {
-      exp = strings.vslice(exp, '(', ')');
-      cases = aCases;
-    }
-
-    if (withs.startsWith(exp, '[')) {
-      try {
-        var obj = json2.JSON.parse(strings.vslice(exp, '[', ']'));
-      } catch (e) {
-        throw new SyntaxError(e + '. String to be parsed was || ' +
-                              strings.vslice(exp, '[', ']') + ' ||');
-      }
-
-      var r = cases['index'](_document, parentNode, obj);
-      if (r == null) {
-        throw new SyntaxError('Expression "' + exp +
-                              '" returned null. Anonymous == ' + (cases == aCases));
-      }
-
-      return r;
-    }
-
-    for (var c in cases) {
-      if (withs.startsWith(exp, c)) {
-        try {
-          var obj = json2.JSON.parse(strings.vslice(exp, '(', ')'))
-        } catch (e) {
-           throw new SyntaxError(e + '. String to be parsed was || ' +
-                                 strings.vslice(exp, '(', ')') + '  ||');
-        }
-        var result = cases[c](_document, parentNode, obj);
-      }
-    }
-
-    if (!result) {
-      if (withs.startsWith(exp, '{')) {
-        try {
-          var obj = json2.JSON.parse(exp);
-        } catch (e) {
-          throw new SyntaxError(e + '. String to be parsed was || ' + exp + ' ||');
-        }
-
-        if (cases == aCases) {
-          var result = _anonByAttrib(_document, parentNode, obj);
-        } else {
-          var result = _byAttrib(parentNode, obj);
-        }
-      }
-    }
-
-    // Final return
-    if (expIndex) {
-      // TODO: Check length and raise error
-      return result[expIndex];
-    } else {
-      // TODO: Check length and raise error
-      return result;
-    }
-
-    // Maybe we should cause an exception here
-    return false;
-  };
-
-  return expSplit.reduce(reduceLookup);
-};
deleted file mode 100644
--- a/services/sync/tps/extensions/mozmill/resource/driver/mozelement.js
+++ /dev/null
@@ -1,1157 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var EXPORTED_SYMBOLS = ["Elem", "Selector", "ID", "Link", "XPath", "Name", "Lookup",
-                        "MozMillElement", "MozMillCheckBox", "MozMillRadio", "MozMillDropList",
-                        "MozMillTextBox", "subclasses"
-                       ];
-
-const NAMESPACE_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-
-var Cc = Components.classes;
-var Ci = Components.interfaces;
-var Cu = Components.utils;
-
-var EventUtils = {};  Cu.import('resource://mozmill/stdlib/EventUtils.js', EventUtils);
-
-var assertions = {};  Cu.import('resource://mozmill/modules/assertions.js', assertions);
-var broker = {};      Cu.import('resource://mozmill/driver/msgbroker.js', broker);
-var elementslib = {}; Cu.import('resource://mozmill/driver/elementslib.js', elementslib);
-var utils = {};       Cu.import('resource://mozmill/stdlib/utils.js', utils);
-
-var assert = new assertions.Assert();
-
-// A list of all the subclasses available.  Shared modules can push their own subclasses onto this list
-var subclasses = [MozMillCheckBox, MozMillRadio, MozMillDropList, MozMillTextBox];
-
-/**
- * createInstance()
- *
- * Returns an new instance of a MozMillElement
- * The type of the element is automatically determined
- */
-function createInstance(locatorType, locator, elem, document) {
-  var args = { "document": document, "element": elem };
-
-  // If we already have an element lets determine the best MozMillElement type
-  if (elem) {
-    for (var i = 0; i < subclasses.length; ++i) {
-      if (subclasses[i].isType(elem)) {
-        return new subclasses[i](locatorType, locator, args);
-      }
-    }
-  }
-
-  // By default we create a base MozMillElement
-  if (MozMillElement.isType(elem)) {
-    return new MozMillElement(locatorType, locator, args);
-  }
-
-  throw new Error("Unsupported element type " + locatorType + ": " + locator);
-}
-
-var Elem = function (node) {
-  return createInstance("Elem", node, node);
-};
-
-var Selector = function (document, selector, index) {
-  return createInstance("Selector", selector, elementslib.Selector(document, selector, index), document);
-};
-
-var ID = function (document, nodeID) {
-  return createInstance("ID", nodeID, elementslib.ID(document, nodeID), document);
-};
-
-var Link = function (document, linkName) {
-  return createInstance("Link", linkName, elementslib.Link(document, linkName), document);
-};
-
-var XPath = function (document, expr) {
-  return createInstance("XPath", expr, elementslib.XPath(document, expr), document);
-};
-
-var Name = function (document, nName) {
-  return createInstance("Name", nName, elementslib.Name(document, nName), document);
-};
-
-var Lookup = function (document, expression) {
-  var elem = createInstance("Lookup", expression, elementslib.Lookup(document, expression), document);
-
-  // Bug 864268 - Expose the expression property to maintain backwards compatibility
-  elem.expression = elem._locator;
-
-  return elem;
-};
-
-/**
- * MozMillElement
- * The base class for all mozmill elements
- */
-function MozMillElement(locatorType, locator, args) {
-  args = args || {};
-  this._locatorType = locatorType;
-  this._locator = locator;
-  this._element = args["element"];
-  this._owner = args["owner"];
-
-  this._document = this._element ? this._element.ownerDocument : args["document"];
-  this._defaultView = this._document ? this._document.defaultView : null;
-
-  // Used to maintain backwards compatibility with controller.js
-  this.isElement = true;
-}
-
-// Static method that returns true if node is of this element type
-MozMillElement.isType = function (node) {
-  return true;
-};
-
-// This getter is the magic behind lazy loading (note distinction between _element and element)
-MozMillElement.prototype.__defineGetter__("element", function () {
-  // If the document is invalid (e.g. reload of the page), invalidate the cached
-  // element and update the document cache
-  if (this._defaultView && this._defaultView.document !== this._document) {
-    this._document = this._defaultView.document;
-    this._element = undefined;
-  }
-
-  if (this._element == undefined) {
-    if (elementslib[this._locatorType]) {
-      this._element = elementslib[this._locatorType](this._document, this._locator);
-    } else if (this._locatorType == "Elem") {
-      this._element = this._locator;
-    } else {
-      throw new Error("Unknown locator type: " + this._locatorType);
-    }
-  }
-
-  return this._element;
-});
-
-/**
- * Drag an element to the specified offset on another element, firing mouse and
- * drag events. Adapted from EventUtils.js synthesizeDrop()
- *
- * By default it will drag the source element over the destination's element
- * center with a "move" dropEffect.
- *
- * @param {MozElement} aElement
- *        Destination element over which the drop occurs
- * @param {Number} [aOffsetX=aElement.width/2]
- *        Relative x offset for dropping on aElement
- * @param {Number} [aOffsetY=aElement.height/2]
- *        Relative y offset for dropping on aElement
- * @param {DOMWindow} [aSourceWindow=this.element.ownerDocument.defaultView]
- *        Custom source Window to be used.
- * @param {DOMWindow} [aDestWindow=aElement.getNode().ownerDocument.defaultView]
- *        Custom destination Window to be used.
- * @param {String} [aDropEffect="move"]
- *        Possible values: copy, move, link, none
- * @param {Object[]} [aDragData]
- *        An array holding custom drag data to be used during the drag event
- *        Format: [{ type: "text/plain", "Text to drag"}, ...]
- *
- * @returns {String} the captured dropEffect
- */
-MozMillElement.prototype.dragToElement = function(aElement, aOffsetX, aOffsetY,
-                                                  aSourceWindow, aDestWindow,
-                                                  aDropEffect, aDragData) {
-  if (!this.element) {
-    throw new Error("Could not find element " + this.getInfo());
-  }
-  if (!aElement) {
-    throw new Error("Missing destination element");
-  }
-
-  var srcNode = this.element;
-  var destNode = aElement.getNode();
-  var srcWindow = aSourceWindow || srcNode.ownerGlobal || srcNode;
-  var destWindow = aDestWindow || destNode.ownerGlobal || destNode;
-
-  var srcRect = srcNode.getBoundingClientRect();
-  var srcCoords = {
-    x: srcRect.width / 2,
-    y: srcRect.height / 2
-  };
-  var destRect = destNode.getBoundingClientRect();
-  var destCoords = {
-    x: (!aOffsetX || isNaN(aOffsetX)) ? (destRect.width / 2) : aOffsetX,
-    y: (!aOffsetY || isNaN(aOffsetY)) ? (destRect.height / 2) : aOffsetY
-  };
-
-  var windowUtils = destWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                              .getInterface(Ci.nsIDOMWindowUtils);
-  var ds = Cc["@mozilla.org/widget/dragservice;1"].getService(Ci.nsIDragService);
-
-  var dataTransfer;
-  var trapDrag = function (event) {
-    srcWindow.removeEventListener("dragstart", trapDrag, true);
-    dataTransfer = event.dataTransfer;
-
-    if (!aDragData) {
-      return;
-    }
-
-    for (var i = 0; i < aDragData.length; i++) {
-      var item = aDragData[i];
-      for (var j = 0; j < item.length; j++) {
-        dataTransfer.mozSetDataAt(item[j].type, item[j].data, i);
-      }
-    }
-
-    dataTransfer.dropEffect = aDropEffect || "move";
-    event.preventDefault();
-    event.stopPropagation();
-  }
-
-  ds.startDragSession();
-
-  try {
-    srcWindow.addEventListener("dragstart", trapDrag, true);
-    EventUtils.synthesizeMouse(srcNode, srcCoords.x, srcCoords.y,
-                               { type: "mousedown" }, srcWindow);
-    EventUtils.synthesizeMouse(destNode, destCoords.x, destCoords.y,
-                               { type: "mousemove" }, destWindow);
-
-    var event = destWindow.document.createEvent("DragEvent");
-    event.initDragEvent("dragenter", true, true, destWindow, 0, 0, 0, 0, 0,
-                        false, false, false, false, 0, null, dataTransfer);
-    event.initDragEvent("dragover", true, true, destWindow, 0, 0, 0, 0, 0,
-                        false, false, false, false, 0, null, dataTransfer);
-    event.initDragEvent("drop", true, true, destWindow, 0, 0, 0, 0, 0,
-                        false, false, false, false, 0, null, dataTransfer);
-    windowUtils.dispatchDOMEventViaPresShell(destNode, event, true);
-
-    EventUtils.synthesizeMouse(destNode, destCoords.x, destCoords.y,
-                               { type: "mouseup" }, destWindow);
-
-    return dataTransfer.dropEffect;
-  } finally {
-    ds.endDragSession(true);
-  }
-
-};
-
-// Returns the actual wrapped DOM node
-MozMillElement.prototype.getNode = function () {
-  return this.element;
-};
-
-MozMillElement.prototype.getInfo = function () {
-  return this._locatorType + ": " + this._locator;
-};
-
-/**
- * Sometimes an element which once existed will no longer exist in the DOM
- * This function re-searches for the element
- */
-MozMillElement.prototype.exists = function () {
-  this._element = undefined;
-  if (this.element) {
-    return true;
-  }
-
-  return false;
-};
-
-/**
- * Synthesize a keypress event on the given element
- *
- * @param {string} aKey
- *        Key to use for synthesizing the keypress event. It can be a simple
- *        character like "k" or a string like "VK_ESCAPE" for command keys
- * @param {object} aModifiers
- *        Information about the modifier keys to send
- *        Elements: accelKey   - Hold down the accelerator key (ctrl/meta)
- *                               [optional - default: false]
- *                  altKey     - Hold down the alt key
- *                              [optional - default: false]
- *                  ctrlKey    - Hold down the ctrl key
- *                               [optional - default: false]
- *                  metaKey    - Hold down the meta key (command key on Mac)
- *                               [optional - default: false]
- *                  shiftKey   - Hold down the shift key
- *                               [optional - default: false]
- * @param {object} aExpectedEvent
- *        Information about the expected event to occur
- *        Elements: target     - Element which should receive the event
- *                               [optional - default: current element]
- *                  type       - Type of the expected key event
- */
-MozMillElement.prototype.keypress = function (aKey, aModifiers, aExpectedEvent) {
-  if (!this.element) {
-    throw new Error("Could not find element " + this.getInfo());
-  }
-
-  var win = this.element.ownerGlobal || this.element;
-  this.element.focus();
-
-  if (aExpectedEvent) {
-    if (!aExpectedEvent.type) {
-      throw new Error(arguments.callee.name + ": Expected event type not specified");
-    }
-
-    var target = aExpectedEvent.target ? aExpectedEvent.target.getNode()
-                                       : this.element;
-    EventUtils.synthesizeKeyExpectEvent(aKey, aModifiers || {}, target, aExpectedEvent.type,
-                                        "MozMillElement.keypress()", win);
-  } else {
-    EventUtils.synthesizeKey(aKey, aModifiers || {}, win);
-  }
-
-  broker.pass({'function':'MozMillElement.keypress()'});
-
-  return true;
-};
-
-
-/**
- * Synthesize a general mouse event on the given element
- *
- * @param {number} aOffsetX
- *        Relative x offset in the elements bounds to click on
- * @param {number} aOffsetY
- *        Relative y offset in the elements bounds to click on
- * @param {object} aEvent
- *        Information about the event to send
- *        Elements: accelKey   - Hold down the accelerator key (ctrl/meta)
- *                               [optional - default: false]
- *                  altKey     - Hold down the alt key
- *                               [optional - default: false]
- *                  button     - Mouse button to use
- *                               [optional - default: 0]
- *                  clickCount - Number of counts to click
- *                               [optional - default: 1]
- *                  ctrlKey    - Hold down the ctrl key
- *                               [optional - default: false]
- *                  metaKey    - Hold down the meta key (command key on Mac)
- *                               [optional - default: false]
- *                  shiftKey   - Hold down the shift key
- *                               [optional - default: false]
- *                  type       - Type of the mouse event ('click', 'mousedown',
- *                               'mouseup', 'mouseover', 'mouseout')
- *                               [optional - default: 'mousedown' + 'mouseup']
- * @param {object} aExpectedEvent
- *        Information about the expected event to occur
- *        Elements: target     - Element which should receive the event
- *                               [optional - default: current element]
- *                  type       - Type of the expected mouse event
- */
-MozMillElement.prototype.mouseEvent = function (aOffsetX, aOffsetY, aEvent, aExpectedEvent) {
-  if (!this.element) {
-    throw new Error(arguments.callee.name + ": could not find element " + this.getInfo());
-  }
-
-  if ("document" in this.element) {
-    throw new Error("A window cannot be a target for mouse events.");
-  }
-
-  var rect = this.element.getBoundingClientRect();
-
-  if (!aOffsetX || isNaN(aOffsetX)) {
-    aOffsetX = rect.width / 2;
-  }
-
-  if (!aOffsetY || isNaN(aOffsetY)) {
-    aOffsetY = rect.height / 2;
-  }
-
-  // Scroll element into view otherwise the click will fail
-  if ("scrollIntoView" in this.element)
-    this.element.scrollIntoView();
-
-  if (aExpectedEvent) {
-    // The expected event type has to be set
-    if (!aExpectedEvent.type) {
-      throw new Error(arguments.callee.name + ": Expected event type not specified");
-    }
-
-    // If no target has been specified use the specified element
-    var target = aExpectedEvent.target ? aExpectedEvent.target.getNode()
-                                       : this.element;
-    if (!target) {
-      throw new Error(arguments.callee.name + ": could not find element " +
-                      aExpectedEvent.target.getInfo());
-    }
-
-    EventUtils.synthesizeMouseExpectEvent(this.element, aOffsetX, aOffsetY, aEvent,
-                                          target, aExpectedEvent.type,
-                                          "MozMillElement.mouseEvent()",
-                                          this.element.ownerGlobal);
-  } else {
-    EventUtils.synthesizeMouse(this.element, aOffsetX, aOffsetY, aEvent,
-                               this.element.ownerGlobal);
-  }
-
-  // Bug 555347
-  // We don't know why this sleep is necessary but more investigation is needed
-  // before it can be removed
-  utils.sleep(0);
-
-  return true;
-};
-
-/**
- * Synthesize a mouse click event on the given element
- */
-MozMillElement.prototype.click = function (aOffsetX, aOffsetY, aExpectedEvent) {
-  // Handle menu items differently
-  if (this.element && this.element.tagName == "menuitem") {
-    this.element.click();
-  } else {
-    this.mouseEvent(aOffsetX, aOffsetY, {}, aExpectedEvent);
-  }
-
-  broker.pass({'function':'MozMillElement.click()'});
-
-  return true;
-};
-
-/**
- * Synthesize a double click on the given element
- */
-MozMillElement.prototype.doubleClick = function (aOffsetX, aOffsetY, aExpectedEvent) {
-  this.mouseEvent(aOffsetX, aOffsetY, {clickCount: 2}, aExpectedEvent);
-
-  broker.pass({'function':'MozMillElement.doubleClick()'});
-
-  return true;
-};
-
-/**
- * Synthesize a mouse down event on the given element
- */
-MozMillElement.prototype.mouseDown = function (aButton, aOffsetX, aOffsetY, aExpectedEvent) {
-  this.mouseEvent(aOffsetX, aOffsetY, {button: aButton, type: "mousedown"}, aExpectedEvent);
-
-  broker.pass({'function':'MozMillElement.mouseDown()'});
-
-  return true;
-};
-
-/**
- * Synthesize a mouse out event on the given element
- */
-MozMillElement.prototype.mouseOut = function (aButton, aOffsetX, aOffsetY, aExpectedEvent) {
-  this.mouseEvent(aOffsetX, aOffsetY, {button: aButton, type: "mouseout"}, aExpectedEvent);
-
-  broker.pass({'function':'MozMillElement.mouseOut()'});
-
-  return true;
-};
-
-/**
- * Synthesize a mouse over event on the given element
- */
-MozMillElement.prototype.mouseOver = function (aButton, aOffsetX, aOffsetY, aExpectedEvent) {
-  this.mouseEvent(aOffsetX, aOffsetY, {button: aButton, type: "mouseover"}, aExpectedEvent);
-
-  broker.pass({'function':'MozMillElement.mouseOver()'});
-
-  return true;
-};
-
-/**
- * Synthesize a mouse up event on the given element
- */
-MozMillElement.prototype.mouseUp = function (aButton, aOffsetX, aOffsetY, aExpectedEvent) {
-  this.mouseEvent(aOffsetX, aOffsetY, {button: aButton, type: "mouseup"}, aExpectedEvent);
-
-  broker.pass({'function':'MozMillElement.mouseUp()'});
-
-  return true;
-};
-
-/**
- * Synthesize a mouse middle click event on the given element
- */
-MozMillElement.prototype.middleClick = function (aOffsetX, aOffsetY, aExpectedEvent) {
-  this.mouseEvent(aOffsetX, aOffsetY, {button: 1}, aExpectedEvent);
-
-  broker.pass({'function':'MozMillElement.middleClick()'});
-
-  return true;
-};
-
-/**
- * Synthesize a mouse right click event on the given element
- */
-MozMillElement.prototype.rightClick = function (aOffsetX, aOffsetY, aExpectedEvent) {
-  this.mouseEvent(aOffsetX, aOffsetY, {type : "contextmenu", button: 2 }, aExpectedEvent);
-
-  broker.pass({'function':'MozMillElement.rightClick()'});
-
-  return true;
-};
-
-/**
- * Synthesize a general touch event on the given element
- *
- * @param {Number} [aOffsetX=aElement.width / 2]
- *        Relative x offset in the elements bounds to click on
- * @param {Number} [aOffsetY=aElement.height / 2]
- *        Relative y offset in the elements bounds to click on
- * @param {Object} [aEvent]
- *        Information about the event to send
- * @param {Boolean} [aEvent.altKey=false]
- *        A Boolean value indicating whether or not the alt key was down when
- *        the touch event was fired
- * @param {Number} [aEvent.angle=0]
- *        The angle (in degrees) that the ellipse described by rx and
- *        ry must be rotated, clockwise, to most accurately cover the area
- *        of contact between the user and the surface.
- * @param {Touch[]} [aEvent.changedTouches]
- *        A TouchList of all the Touch objects representing individual points of
- *        contact whose states changed between the previous touch event and
- *        this one
- * @param {Boolean} [aEvent.ctrlKey]
- *        A Boolean value indicating whether or not the control key was down
- *        when the touch event was fired
- * @param {Number} [aEvent.force=1]
- *        The amount of pressure being applied to the surface by the user, as a
- *        float between 0.0 (no pressure) and 1.0 (maximum pressure)
- * @param {Number} [aEvent.id=0]
- *        A unique identifier for this Touch object. A given touch (say, by a
- *        finger) will have the same identifier for the duration of its movement
- *        around the surface. This lets you ensure that you're tracking the same
- *        touch all the time
- * @param {Boolean} [aEvent.metaKey]
- *        A Boolean value indicating whether or not the meta key was down when
- *        the touch event was fired.
- * @param {Number} [aEvent.rx=1]
- *        The X radius of the ellipse that most closely circumscribes the area
- *        of contact with the screen.
- * @param {Number} [aEvent.ry=1]
- *        The Y radius of the ellipse that most closely circumscribes the area
- *        of contact with the screen.
- * @param {Boolean} [aEvent.shiftKey]
- *        A Boolean value indicating whether or not the shift key was down when
- *        the touch event was fired
- * @param {Touch[]} [aEvent.targetTouches]
- *        A TouchList of all the Touch objects that are both currently in
- *        contact with the touch surface and were also started on the same
- *        element that is the target of the event
- * @param {Touch[]} [aEvent.touches]
- *        A TouchList of all the Touch objects representing all current points
- *        of contact with the surface, regardless of target or changed status
- * @param {Number} [aEvent.type=*|touchstart|touchend|touchmove|touchenter|touchleave|touchcancel]
- *        The type of touch event that occurred
- * @param {Element} [aEvent.target]
- *        The target of the touches associated with this event. This target
- *        corresponds to the target of all the touches in the targetTouches
- *        attribute, but note that other touches in this event may have a
- *        different target. To be careful, you should use the target associated
- *        with individual touches
- */
-MozMillElement.prototype.touchEvent = function (aOffsetX, aOffsetY, aEvent) {
-  if (!this.element) {
-    throw new Error(arguments.callee.name + ": could not find element " + this.getInfo());
-  }
-
-  if ("document" in this.element) {
-    throw new Error("A window cannot be a target for touch events.");
-  }
-
-  var rect = this.element.getBoundingClientRect();
-
-  if (!aOffsetX || isNaN(aOffsetX)) {
-    aOffsetX = rect.width / 2;
-  }
-
-  if (!aOffsetY || isNaN(aOffsetY)) {
-    aOffsetY = rect.height / 2;
-  }
-
-  // Scroll element into view otherwise the click will fail
-  if ("scrollIntoView" in this.element) {
-    this.element.scrollIntoView();
-  }
-
-  EventUtils.synthesizeTouch(this.element, aOffsetX, aOffsetY, aEvent,
-                             this.element.ownerGlobal);
-
-  return true;
-};
-
-/**
- * Synthesize a touch tap event on the given element
- *
- * @param {Number} [aOffsetX=aElement.width / 2]
- *        Left offset in px where the event is triggered
- * @param {Number} [aOffsetY=aElement.height / 2]
- *        Top offset in px where the event is triggered
- * @param {Object} [aExpectedEvent]
- *        Information about the expected event to occur
- * @param {MozMillElement} [aExpectedEvent.target=this.element]
- *        Element which should receive the event
- * @param {MozMillElement} [aExpectedEvent.type]
- *        Type of the expected mouse event
- */
-MozMillElement.prototype.tap = function (aOffsetX, aOffsetY, aExpectedEvent) {
-  this.mouseEvent(aOffsetX, aOffsetY, {
-    clickCount: 1,
-    inputSource: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH
-  }, aExpectedEvent);
-
-  broker.pass({'function':'MozMillElement.tap()'});
-
-  return true;
-};
-
-/**
- * Synthesize a double tap on the given element
- *
- * @param {Number} [aOffsetX=aElement.width / 2]
- *        Left offset in px where the event is triggered
- * @param {Number} [aOffsetY=aElement.height / 2]
- *        Top offset in px where the event is triggered
- * @param {Object} [aExpectedEvent]
- *        Information about the expected event to occur
- * @param {MozMillElement} [aExpectedEvent.target=this.element]
- *        Element which should receive the event
- * @param {MozMillElement} [aExpectedEvent.type]
- *        Type of the expected mouse event
- */
-MozMillElement.prototype.doubleTap = function (aOffsetX, aOffsetY, aExpectedEvent) {
-  this.mouseEvent(aOffsetX, aOffsetY, {
-    clickCount: 2,
-    inputSource: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH
-  }, aExpectedEvent);
-
-  broker.pass({'function':'MozMillElement.doubleTap()'});
-
-  return true;
-};
-
-/**
- * Synthesize a long press
- *
- * @param {Number} aOffsetX
- *        Left offset in px where the event is triggered
- * @param {Number} aOffsetY
- *        Top offset in px where the event is triggered
- * @param {Number} [aTime=1000]
- *        Duration of the "press" event in ms
- */
-MozMillElement.prototype.longPress = function (aOffsetX, aOffsetY, aTime) {
-  var time = aTime || 1000;
-
-  this.touchStart(aOffsetX, aOffsetY);
-  utils.sleep(time);
-  this.touchEnd(aOffsetX, aOffsetY);
-
-  broker.pass({'function':'MozMillElement.longPress()'});
-
-  return true;
-};
-
-/**
- * Synthesize a touch & drag event on the given element
- *
- * @param {Number} aOffsetX1
- *        Left offset of the start position
- * @param {Number} aOffsetY1
- *        Top offset of the start position
- * @param {Number} aOffsetX2
- *        Left offset of the end position
- * @param {Number} aOffsetY2
- *        Top offset of the end position
- */
-MozMillElement.prototype.touchDrag = function (aOffsetX1, aOffsetY1, aOffsetX2, aOffsetY2) {
-  this.touchStart(aOffsetX1, aOffsetY1);
-  this.touchMove(aOffsetX2, aOffsetY2);
-  this.touchEnd(aOffsetX2, aOffsetY2);
-
-  broker.pass({'function':'MozMillElement.move()'});
-
-  return true;
-};
-
-/**
- * Synthesize a press / touchstart event on the given element
- *
- * @param {Number} aOffsetX
- *        Left offset where the event is triggered
- * @param {Number} aOffsetY
- *        Top offset where the event is triggered
- */
-MozMillElement.prototype.touchStart = function (aOffsetX, aOffsetY) {
-  this.touchEvent(aOffsetX, aOffsetY, { type: "touchstart" });
-
-  broker.pass({'function':'MozMillElement.touchStart()'});
-
-  return true;
-};
-
-/**
- * Synthesize a release / touchend event on the given element
- *
- * @param {Number} aOffsetX
- *        Left offset where the event is triggered
- * @param {Number} aOffsetY
- *        Top offset where the event is triggered
- */
-MozMillElement.prototype.touchEnd = function (aOffsetX, aOffsetY) {
-  this.touchEvent(aOffsetX, aOffsetY, { type: "touchend" });
-
-  broker.pass({'function':'MozMillElement.touchEnd()'});
-
-  return true;
-};
-
-/**
- * Synthesize a touchMove event on the given element
- *
- * @param {Number} aOffsetX
- *        Left offset where the event is triggered
- * @param {Number} aOffsetY
- *        Top offset where the event is triggered
- */
-MozMillElement.prototype.touchMove = function (aOffsetX, aOffsetY) {
-  this.touchEvent(aOffsetX, aOffsetY, { type: "touchmove" });
-
-  broker.pass({'function':'MozMillElement.touchMove()'});
-
-  return true;
-};
-
-MozMillElement.prototype.waitForElement = function (timeout, interval) {
-  var elem = this;
-
-  assert.waitFor(function () {
-    return elem.exists();
-  }, "Element.waitForElement(): Element '" + this.getInfo() +
-     "' has been found", timeout, interval);
-
-  broker.pass({'function':'MozMillElement.waitForElement()'});
-};
-
-MozMillElement.prototype.waitForElementNotPresent = function (timeout, interval) {
-  var elem = this;
-
-  assert.waitFor(function () {
-    return !elem.exists();
-  }, "Element.waitForElementNotPresent(): Element '" + this.getInfo() +
-     "' has not been found", timeout, interval);
-
-  broker.pass({'function':'MozMillElement.waitForElementNotPresent()'});
-};
-
-MozMillElement.prototype.waitThenClick = function (timeout, interval,
-                                                   aOffsetX, aOffsetY, aExpectedEvent) {
-  this.waitForElement(timeout, interval);
-  this.click(aOffsetX, aOffsetY, aExpectedEvent);
-};
-
-/**
- * Waits for the element to be available in the DOM, then trigger a tap event
- *
- * @param {Number} [aTimeout=5000]
- *        Time to wait for the element to be available
- * @param {Number} [aInterval=100]
- *        Interval to check for availability
- * @param {Number} [aOffsetX=aElement.width / 2]
- *        Left offset where the event is triggered
- * @param {Number} [aOffsetY=aElement.height / 2]
- *        Top offset where the event is triggered
- * @param {Object} [aExpectedEvent]
- *        Information about the expected event to occur
- * @param {MozMillElement} [aExpectedEvent.target=this.element]
- *        Element which should receive the event
- * @param {MozMillElement} [aExpectedEvent.type]
- *        Type of the expected mouse event
- */
-MozMillElement.prototype.waitThenTap = function (aTimeout, aInterval,
-                                                 aOffsetX, aOffsetY, aExpectedEvent) {
-  this.waitForElement(aTimeout, aInterval);
-  this.tap(aOffsetX, aOffsetY, aExpectedEvent);
-};
-
-// Dispatches an HTMLEvent
-MozMillElement.prototype.dispatchEvent = function (eventType, canBubble, modifiers) {
-  canBubble = canBubble || true;
-  modifiers = modifiers || { };
-
-  let document = 'ownerDocument' in this.element ? this.element.ownerDocument
-                                                 : this.element.document;
-
-  let evt = document.createEvent('HTMLEvents');
-  evt.shiftKey = modifiers["shift"];
-  evt.metaKey = modifiers["meta"];
-  evt.altKey = modifiers["alt"];
-  evt.ctrlKey = modifiers["ctrl"];
-  evt.initEvent(eventType, canBubble, true);
-
-  this.element.dispatchEvent(evt);
-};
-
-
-/**
- * MozMillCheckBox, which inherits from MozMillElement
- */
-function MozMillCheckBox(locatorType, locator, args) {
-  MozMillElement.call(this, locatorType, locator, args);
-}
-
-
-MozMillCheckBox.prototype = Object.create(MozMillElement.prototype, {
-  check : {
-    /**
-     * Enable/Disable a checkbox depending on the target state
-     *
-     * @param {boolean} state State to set
-     * @return {boolean} Success state
-     */
-    value : function MMCB_check(state) {
-      var result = false;
-
-      if (!this.element) {
-        throw new Error("could not find element " + this.getInfo());
-      }
-
-      // If we have a XUL element, unwrap its XPCNativeWrapper
-      if (this.element.namespaceURI == NAMESPACE_XUL) {
-        this.element = utils.unwrapNode(this.element);
-      }
-
-      state = (typeof(state) == "boolean") ? state : false;
-      if (state != this.element.checked) {
-        this.click();
-        var element = this.element;
-
-        assert.waitFor(function () {
-          return element.checked == state;
-        }, "CheckBox.check(): Checkbox " + this.getInfo() + " could not be checked/unchecked", 500);
-
-        result = true;
-      }
-
-      broker.pass({'function':'MozMillCheckBox.check(' + this.getInfo() +
-                   ', state: ' + state + ')'});
-
-      return result;
-    }
-  }
-});
-
-
-/**
- * Returns true if node is of type MozMillCheckBox
- *
- * @static
- * @param {DOMNode} node Node to check for its type
- * @return {boolean} True if node is of type checkbox
- */
-MozMillCheckBox.isType = function MMCB_isType(node) {
-  return ((node.localName.toLowerCase() == "input" && node.getAttribute("type") == "checkbox") ||
-    (node.localName.toLowerCase() == 'toolbarbutton' && node.getAttribute('type') == 'checkbox') ||
-    (node.localName.toLowerCase() == 'checkbox'));
-};
-
-
-/**
- * MozMillRadio, which inherits from MozMillElement
- */
-function MozMillRadio(locatorType, locator, args) {
-  MozMillElement.call(this, locatorType, locator, args);
-}
-
-
-MozMillRadio.prototype = Object.create(MozMillElement.prototype, {
-  select : {
-    /**
-     * Select the given radio button
-     *
-     * @param {number} [index=0]
-     *        Specifies which radio button in the group to select (only
-     *        applicable to radiogroup elements)
-     * @return {boolean} Success state
-     */
-    value : function MMR_select(index) {
-      if (!this.element) {
-        throw new Error("could not find element " + this.getInfo());
-      }
-
-      if (this.element.localName.toLowerCase() == "radiogroup") {
-        var element = this.element.getElementsByTagName("radio")[index || 0];
-        new MozMillRadio("Elem", element).click();
-      } else {
-        var element = this.element;
-        this.click();
-      }
-
-      assert.waitFor(function () {
-        // If we have a XUL element, unwrap its XPCNativeWrapper
-        if (element.namespaceURI == NAMESPACE_XUL) {
-          element = utils.unwrapNode(element);
-          return element.selected == true;
-        }
-
-        return element.checked == true;
-      }, "Radio.select(): Radio button " + this.getInfo() + " has been selected", 500);
-
-      broker.pass({'function':'MozMillRadio.select(' + this.getInfo() + ')'});
-
-      return true;
-    }
-  }
-});
-
-
-/**
- * Returns true if node is of type MozMillRadio
- *
- * @static
- * @param {DOMNode} node Node to check for its type
- * @return {boolean} True if node is of type radio
- */
-MozMillRadio.isType = function MMR_isType(node) {
-  return ((node.localName.toLowerCase() == 'input' && node.getAttribute('type') == 'radio') ||
-    (node.localName.toLowerCase() == 'toolbarbutton' && node.getAttribute('type') == 'radio') ||
-    (node.localName.toLowerCase() == 'radio') ||
-    (node.localName.toLowerCase() == 'radiogroup'));
-};
-
-
-/**
- * MozMillDropList, which inherits from MozMillElement
- */
-function MozMillDropList(locatorType, locator, args) {
-  MozMillElement.call(this, locatorType, locator, args);
-}
-
-
-MozMillDropList.prototype = Object.create(MozMillElement.prototype, {
-  select : {
-    /**
-     * Select the specified option and trigger the relevant events of the element
-     * @return {boolean}
-     */
-    value : function MMDL_select(index, option, value) {
-      if (!this.element){
-        throw new Error("Could not find element " + this.getInfo());
-      }
-
-      //if we have a select drop down
-      if (this.element.localName.toLowerCase() == "select"){
-        var item = null;
-
-        // The selected item should be set via its index
-        if (index != undefined) {
-          // Resetting a menulist has to be handled separately
-          if (index == -1) {
-            this.dispatchEvent('focus', false);
-            this.element.selectedIndex = index;
-            this.dispatchEvent('change', true);
-
-            broker.pass({'function':'MozMillDropList.select()'});
-
-            return true;
-          } else {
-            item = this.element.options.item(index);
-          }
-        } else {
-          for (var i = 0; i < this.element.options.length; i++) {
-            var entry = this.element.options.item(i);
-            if (option != undefined && entry.innerHTML == option ||
-              value != undefined && entry.value == value) {
-              item = entry;
-              break;
-            }
-          }
-        }
-
-        // Click the item
-        try {
-          // EventUtils.synthesizeMouse doesn't work.
-          this.dispatchEvent('focus', false);
-          item.selected = true;
-          this.dispatchEvent('change', true);
-
-          var self = this;
-          var selected = index || option || value;
-          assert.waitFor(function () {
-            switch (selected) {
-              case index:
-                return selected === self.element.selectedIndex;
-                break;
-              case option:
-                return selected === item.label;
-                break;
-              case value:
-                return selected === item.value;
-                break;
-            }
-          }, "DropList.select(): The correct item has been selected");
-
-          broker.pass({'function':'MozMillDropList.select()'});
-
-          return true;
-        } catch (e) {
-          throw new Error("No item selected for element " + this.getInfo());
-        }
-      }
-      //if we have a xul menupopup select accordingly
-      else if (this.element.namespaceURI.toLowerCase() == NAMESPACE_XUL) {
-        var ownerDoc = this.element.ownerDocument;
-        // Unwrap the XUL element's XPCNativeWrapper
-        this.element = utils.unwrapNode(this.element);
-        // Get the list of menuitems
-        var menuitems = this.element.
-                        getElementsByTagNameNS(NAMESPACE_XUL, "menupopup")[0].
-                        getElementsByTagNameNS(NAMESPACE_XUL, "menuitem");
-
-        var item = null;
-
-        if (index != undefined) {
-          if (index == -1) {
-            this.dispatchEvent('focus', false);
-            this.element.boxObject.activeChild = null;
-            this.dispatchEvent('change', true);
-
-            broker.pass({'function':'MozMillDropList.select()'});
-
-            return true;
-          } else {
-            item = menuitems[index];
-          }
-        } else {
-          for (var i = 0; i < menuitems.length; i++) {
-            var entry = menuitems[i];
-            if (option != undefined && entry.label == option ||
-              value != undefined && entry.value == value) {
-              item = entry;
-              break;
-            }
-          }
-        }
-
-        // Click the item
-        try {
-          item.click();
-
-          var self = this;
-          var selected = index || option || value;
-          assert.waitFor(function () {
-            switch (selected) {
-              case index:
-                return selected === self.element.selectedIndex;
-                break;
-              case option:
-                return selected === self.element.label;
-                break;
-              case value:
-                return selected === self.element.value;
-                break;
-            }
-          }, "DropList.select(): The correct item has been selected");
-
-          broker.pass({'function':'MozMillDropList.select()'});
-
-          return true;
-        } catch (e) {
-          throw new Error('No item selected for element ' + this.getInfo());
-        }
-      }
-    }
-  }
-});
-
-
-/**
- * Returns true if node is of type MozMillDropList
- *
- * @static
- * @param {DOMNode} node Node to check for its type
- * @return {boolean} True if node is of type dropdown list
- */
-MozMillDropList.isType = function MMR_isType(node) {
-  return ((node.localName.toLowerCase() == 'toolbarbutton' &&
-    (node.getAttribute('type') == 'menu' || node.getAttribute('type') == 'menu-button')) ||
-    (node.localName.toLowerCase() == 'menu') ||
-    (node.localName.toLowerCase() == 'menulist') ||
-    (node.localName.toLowerCase() == 'select' ));
-};
-
-
-/**
- * MozMillTextBox, which inherits from MozMillElement
- */
-function MozMillTextBox(locatorType, locator, args) {
-  MozMillElement.call(this, locatorType, locator, args);
-}
-
-
-MozMillTextBox.prototype = Object.create(MozMillElement.prototype, {
-  sendKeys : {
-    /**
-     * Synthesize keypress events for each character on the given element
-     *
-     * @param {string} aText
-     *        The text to send as single keypress events
-     * @param {object} aModifiers
-     *        Information about the modifier keys to send
-     *        Elements: accelKey   - Hold down the accelerator key (ctrl/meta)
-     *                               [optional - default: false]
-     *                  altKey     - Hold down the alt key
-     *                              [optional - default: false]
-     *                  ctrlKey    - Hold down the ctrl key
-     *                               [optional - default: false]
-     *                  metaKey    - Hold down the meta key (command key on Mac)
-     *                               [optional - default: false]
-     *                  shiftKey   - Hold down the shift key
-     *                               [optional - default: false]
-     * @param {object} aExpectedEvent
-     *        Information about the expected event to occur
-     *        Elements: target     - Element which should receive the event
-     *                               [optional - default: current element]
-     *                  type       - Type of the expected key event
-     * @return {boolean} Success state
-     */
-    value : function MMTB_sendKeys(aText, aModifiers, aExpectedEvent) {
-      if (!this.element) {
-        throw new Error("could not find element " + this.getInfo());
-      }
-
-      var element = this.element;
-      Array.forEach(aText, function (letter) {
-        var win = element.ownerGlobal || element;
-        element.focus();
-
-        if (aExpectedEvent) {
-          if (!aExpectedEvent.type) {
-            throw new Error(arguments.callee.name + ": Expected event type not specified");
-          }
-
-          var target = aExpectedEvent.target ? aExpectedEvent.target.getNode()
-            : element;
-          EventUtils.synthesizeKeyExpectEvent(letter, aModifiers || {}, target,
-            aExpectedEvent.type,
-            "MozMillTextBox.sendKeys()", win);
-        } else {
-          EventUtils.synthesizeKey(letter, aModifiers || {}, win);
-        }
-      });
-
-      broker.pass({'function':'MozMillTextBox.type()'});
-
-      return true;
-    }
-  }
-});
-
-
-/**
- * Returns true if node is of type MozMillTextBox
- *
- * @static
- * @param {DOMNode} node Node to check for its type
- * @return {boolean} True if node is of type textbox
- */
-MozMillTextBox.isType = function MMR_isType(node) {
-  return ((node.localName.toLowerCase() == 'input' &&
-    (node.getAttribute('type') == 'text' || node.getAttribute('type') == 'search')) ||
-    (node.localName.toLowerCase() == 'textarea') ||
-    (node.localName.toLowerCase() == 'textbox'));
-};
deleted file mode 100644
--- a/services/sync/tps/extensions/mozmill/resource/driver/mozmill.js
+++ /dev/null
@@ -1,283 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var EXPORTED_SYMBOLS = ["controller", "utils", "elementslib", "os",
-                        "getBrowserController", "newBrowserController",
-                        "getAddonsController", "getPreferencesController",
-                        "newMail3PaneController", "getMail3PaneController",
-                        "wm", "platform", "getAddrbkController",
-                        "getMsgComposeController", "getDownloadsController",
-                        "Application", "findElement",
-                        "getPlacesController", 'isMac', 'isLinux', 'isWindows',
-                        "firePythonCallback", "getAddons"
-                       ];
-
-var Cc = Components.classes;
-var Ci = Components.interfaces;
-var Cu = Components.utils;
-
-
-Cu.import("resource://gre/modules/AddonManager.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-// imports
-var assertions = {};  Cu.import('resource://mozmill/modules/assertions.js', assertions);
-var broker = {};      Cu.import('resource://mozmill/driver/msgbroker.js', broker);
-var controller = {};  Cu.import('resource://mozmill/driver/controller.js', controller);
-var elementslib = {}; Cu.import('resource://mozmill/driver/elementslib.js', elementslib);
-var findElement = {}; Cu.import('resource://mozmill/driver/mozelement.js', findElement);
-var os = {};          Cu.import('resource://mozmill/stdlib/os.js', os);
-var utils = {};       Cu.import('resource://mozmill/stdlib/utils.js', utils);
-var windows = {};     Cu.import('resource://mozmill/modules/windows.js', windows);
-
-
-const DEBUG = false;
-
-// This is a useful "check" timer. See utils.js, good for debugging
-if (DEBUG) {
-  utils.startTimer();
-}
-
-var assert = new assertions.Assert();
-
-// platform information
-var platform = os.getPlatform();
-var isMac = false;
-var isWindows = false;
-var isLinux = false;
-
-if (platform == "darwin"){
-  isMac = true;
-}
-
-if (platform == "winnt"){
-  isWindows = true;
-}
-
-if (platform == "linux"){
-  isLinux = true;
-}
-
-var wm = Services.wm;
-
-var appInfo = Services.appinfo;
-var Application = utils.applicationName;
-
-
-/**
- * Retrieves the list with information about installed add-ons.
- *
- * @returns {String} JSON data of installed add-ons
- */
-function getAddons() {
-  var addons = null;
-
-  AddonManager.getAllAddons(function (addonList) {
-    var tmp_list = [ ];
-
-    addonList.forEach(function (addon) {
-      var tmp = { };
-
-      // We have to filter out properties of type 'function' of the addon
-      // object, which will break JSON.stringify() and result in incomplete
-      // addon information.
-      for (var key in addon) {
-        if (typeof(addon[key]) !== "function") {
-          tmp[key] = addon[key];
-        }
-      }
-
-      tmp_list.push(tmp);
-    });
-
-    addons = tmp_list;
-  });
-
-  try {
-    // Sychronize with getAllAddons so we do not return too early
-    assert.waitFor(function () {
-      return !!addons;
-    })
-
-    return addons;
-  } catch (e) {
-    return null;
-  }
-}
-
-/**
- * Retrieves application details for the Mozmill report
- *
- * @return {String} JSON data of application details
- */
-function getApplicationDetails() {
-  var locale = Services.locale.getAppLocaleAsLangTag();
-
-  // Put all our necessary information into JSON and return it:
-  // appinfo, startupinfo, and addons
-  var details = {
-    application_id: appInfo.ID,
-    application_name: Application,
-    application_version: appInfo.version,
-    application_locale: locale,
-    platform_buildid: appInfo.platformBuildID,
-    platform_version: appInfo.platformVersion,
-    addons: getAddons(),
-    startupinfo: getStartupInfo(),
-    paths: {
-      appdata: Services.dirsvc.get('UAppData', Ci.nsIFile).path,
-      profile: Services.dirsvc.get('ProfD', Ci.nsIFile).path
-    }
-  };
-
-  return JSON.stringify(details);
-}
-
-// get startup time if available
-// see http://blog.mozilla.com/tglek/2011/04/26/measuring-startup-speed-correctly/
-function getStartupInfo() {
-  var startupInfo = {};
-
-  try {
-    var _startupInfo = Services.startup.getStartupInfo();
-    for (var time in _startupInfo) {
-      // convert from Date object to ms since epoch
-      startupInfo[time] = _startupInfo[time].getTime();
-    }
-  } catch (e) {
-    startupInfo = null;
-  }
-
-  return startupInfo;
-}
-
-
-
-function newBrowserController () {
-  return new controller.MozMillController(utils.getMethodInWindows('OpenBrowserWindow')());
-}
-
-function getBrowserController () {
-  var browserWindow = wm.getMostRecentWindow("navigator:browser");
-
-  if (browserWindow == null) {
-    return newBrowserController();
-  } else {
-    return new controller.MozMillController(browserWindow);
-  }
-}
-
-function getPlacesController () {
-  utils.getMethodInWindows('PlacesCommandHook').showPlacesOrganizer('AllBookmarks');
-
-  return new controller.MozMillController(wm.getMostRecentWindow(''));
-}
-
-function getAddonsController () {
-  if (Application == 'SeaMonkey') {
-    utils.getMethodInWindows('toEM')();
-  }
-  else if (Application == 'Thunderbird') {
-    utils.getMethodInWindows('openAddonsMgr')();
-  }
-  else if (Application == 'Sunbird') {
-    utils.getMethodInWindows('goOpenAddons')();
-  } else {
-    utils.getMethodInWindows('BrowserOpenAddonsMgr')();
-  }
-
-  return new controller.MozMillController(wm.getMostRecentWindow(''));
-}
-
-function getDownloadsController() {
-  utils.getMethodInWindows('BrowserDownloadsUI')();
-
-  return new controller.MozMillController(wm.getMostRecentWindow(''));
-}
-
-function getPreferencesController() {
-  if (Application == 'Thunderbird') {
-    utils.getMethodInWindows('openOptionsDialog')();
-  } else {
-    utils.getMethodInWindows('openPreferences')();
-  }
-
-  return new controller.MozMillController(wm.getMostRecentWindow(''));
-}
-
-// Thunderbird functions
-function newMail3PaneController () {
-  return new controller.MozMillController(utils.getMethodInWindows('toMessengerWindow')());
-}
-
-function getMail3PaneController () {
-  var mail3PaneWindow = wm.getMostRecentWindow("mail:3pane");
-
-  if (mail3PaneWindow == null) {
-    return newMail3PaneController();
-  } else {
-    return new controller.MozMillController(mail3PaneWindow);
-  }
-}
-
-// Thunderbird - Address book window
-function newAddrbkController () {
-  utils.getMethodInWindows("toAddressBook")();
-  utils.sleep(2000);
-  var addyWin = wm.getMostRecentWindow("mail:addressbook");
-
-  return new controller.MozMillController(addyWin);
-}
-
-function getAddrbkController () {
-  var addrbkWindow = wm.getMostRecentWindow("mail:addressbook");
-  if (addrbkWindow == null) {
-    return newAddrbkController();
-  } else {
-    return new controller.MozMillController(addrbkWindow);
-  }
-}
-
-function firePythonCallback (filename, method, args, kwargs) {
-  let obj = {'filename': filename, 'method': method};
-  obj['args'] = args || [];
-  obj['kwargs'] = kwargs || {};
-
-  broker.sendMessage("firePythonCallback", obj);
-}
-
-function timer (name) {
-  this.name = name;
-  this.timers = {};
-  this.actions = [];
-
-  frame.timers.push(this);
-}
-
-timer.prototype.start = function (name) {
-  this.timers[name].startTime = (new Date).getTime();
-}
-
-timer.prototype.stop = function (name) {
-  var t = this.timers[name];
-
-  t.endTime = (new Date).getTime();
-  t.totalTime = (t.endTime - t.startTime);
-}
-
-timer.prototype.end = function () {
-  frame.events.fireEvent("timer", this);
-  frame.timers.remove(this);
-}
-
-// Initialization
-
-/**
- * Initialize Mozmill
- */
-function initialize() {
-  windows.init();
-}
-
-initialize();
deleted file mode 100644
--- a/services/sync/tps/extensions/mozmill/resource/driver/msgbroker.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var EXPORTED_SYMBOLS = ['addListener', 'addObject',
-                        'removeListener',
-                        'sendMessage', 'log', 'pass', 'fail'];
-
-var listeners = {};
-
-// add a listener for a specific message type
-function addListener(msgType, listener) {
-  if (listeners[msgType] === undefined) {
-    listeners[msgType] = [];
-  }
-
-  listeners[msgType].push(listener);
-}
-
-// add each method in an object as a message listener
-function addObject(object) {
-  for (var msgType in object) {
-    addListener(msgType, object[msgType]);
-  }
-}
-
-// remove a listener for all message types
-function removeListener(listener) {
-  for (var msgType in listeners) {
-    for (let i = 0; i < listeners.length; ++i) {
-      if (listeners[msgType][i] == listener) {
-        listeners[msgType].splice(i, 1); // remove listener from array
-      }
-    }
-  }
-}
-
-function sendMessage(msgType, obj) {
-  if (listeners[msgType] === undefined) {
-    return;
-  }
-
-  for (let i = 0; i < listeners[msgType].length; ++i) {
-    listeners[msgType][i](obj);
-  }
-}
-
-function log(obj) {
-  sendMessage('log', obj);
-}
-
-function pass(obj) {
-  sendMessage('pass', obj);
-}
-
-function fail(obj) {
-  sendMessage('fail', obj);
-}
deleted file mode 100644
--- a/services/sync/tps/extensions/mozmill/resource/modules/assertions.js
+++ /dev/null
@@ -1,672 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var EXPORTED_SYMBOLS = ['Assert', 'Expect'];
-
-var Cu = Components.utils;
-
-Cu.import("resource://gre/modules/Services.jsm");
-
-var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker);
-var errors = {}; Cu.import('resource://mozmill/modules/errors.js', errors);
-var stack = {}; Cu.import('resource://mozmill/modules/stack.js', stack);
-
-/**
- * @name assertions
- * @namespace Defines expect and assert methods to be used for assertions.
- */
-
-/**
- * The Assert class implements fatal assertions, and can be used in cases
- * when a failing test has to directly abort the current test function. All
- * remaining tasks will not be performed.
- *
- */
-var Assert = function () {}
-
-Assert.prototype = {
-
-  // The following deepEquals implementation is from Narwhal under this license:
-
-  // http://wiki.commonjs.org/wiki/Unit_Testing/1.0
-  //
-  // THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
-  //
-  // Originally from narwhal.js (http://narwhaljs.org)
-  // Copyright (c) 2009 Thomas Robinson <280north.com>
-  //
-  // Permission is hereby granted, free of charge, to any person obtaining a copy
-  // of this software and associated documentation files (the 'Software'), to
-  // deal in the Software without restriction, including without limitation the
-  // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-  // sell copies of the Software, and to permit persons to whom the Software is
-  // furnished to do so, subject to the following conditions:
-  //
-  // The above copyright notice and this permission notice shall be included in
-  // all copies or substantial portions of the Software.
-  //
-  // THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-  // AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-  // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-  _deepEqual: function (actual, expected) {
-    // 7.1. All identical values are equivalent, as determined by ===.
-    if (actual === expected) {
-      return true;
-
-    // 7.2. If the expected value is a Date object, the actual value is
-    // equivalent if it is also a Date object that refers to the same time.
-    } else if (actual instanceof Date && expected instanceof Date) {
-      return actual.getTime() === expected.getTime();
-
-    // 7.3. Other pairs that do not both pass typeof value == 'object',
-    // equivalence is determined by ==.
-    } else if (typeof actual != 'object' && typeof expected != 'object') {
-      return actual == expected;
-
-    // 7.4. For all other Object pairs, including Array objects, equivalence is
-    // determined by having the same number of owned properties (as verified
-    // with Object.prototype.hasOwnProperty.call), the same set of keys
-    // (although not necessarily the same order), equivalent values for every
-    // corresponding key, and an identical 'prototype' property. Note: this
-    // accounts for both named and indexed properties on Arrays.
-    } else {
-      return this._objEquiv(actual, expected);
-    }
-  },
-
-  _objEquiv: function (a, b) {
-    if (a == null || a == undefined || b == null || b == undefined)
-      return false;
-    // an identical 'prototype' property.
-    if (a.prototype !== b.prototype) return false;
-
-    function isArguments(object) {
-      return Object.prototype.toString.call(object) == '[object Arguments]';
-    }
-
-    //~~~I've managed to break Object.keys through screwy arguments passing.
-    // Converting to array solves the problem.
-    if (isArguments(a)) {
-      if (!isArguments(b)) {
-        return false;
-      }
-      a = pSlice.call(a);
-      b = pSlice.call(b);
-      return this._deepEqual(a, b);
-    }
-    try {
-      var ka = Object.keys(a),
-          kb = Object.keys(b),
-          key, i;
-    } catch (e) {//happens when one is a string literal and the other isn't
-      return false;
-    }
-    // having the same number of owned properties (keys incorporates
-    // hasOwnProperty)
-    if (ka.length != kb.length)
-      return false;
-    //the same set of keys (although not necessarily the same order),
-    ka.sort();
-    kb.sort();
-    //~~~cheap key test
-    for (i = ka.length - 1; i >= 0; i--) {
-      if (ka[i] != kb[i])
-        return false;
-    }
-    //equivalent values for every corresponding key, and
-    //~~~possibly expensive deep test
-    for (i = ka.length - 1; i >= 0; i--) {
-      key = ka[i];
-      if (!this._deepEqual(a[key], b[key])) return false;
-    }
-    return true;
-  },
-
-  _expectedException : function Assert__expectedException(actual, expected) {
-    if (!actual || !expected) {
-      return false;
-    }
-
-    if (expected instanceof RegExp) {
-      return expected.test(actual);
-    } else if (actual instanceof expected) {
-      return true;
-    } else if (expected.call({}, actual) === true) {
-      return true;
-    } else if (actual.name === expected.name) {
-      return true;
-    }
-
-    return false;
-  },
-
-  /**
-   * Log a test as failing by throwing an AssertionException.
-   *
-   * @param {object} aResult
-   *   Test result details used for reporting.
-   *   <dl>
-   *     <dd>fileName</dd>
-   *     <dt>Name of the file in which the assertion failed.</dt>
-   *     <dd>functionName</dd>
-   *     <dt>Function in which the assertion failed.</dt>
-   *     <dd>lineNumber</dd>
-   *     <dt>Line number of the file in which the assertion failed.</dt>
-   *     <dd>message</dd>
-   *     <dt>Message why the assertion failed.</dt>
-   *   </dl>
-   * @throws {errors.AssertionError}
-   *
-   */
-  _logFail: function Assert__logFail(aResult) {
-    throw new errors.AssertionError(aResult.message,
-                                    aResult.fileName,
-                                    aResult.lineNumber,
-                                    aResult.functionName,
-                                    aResult.name);
-  },
-
-  /**
-   * Log a test as passing by adding a pass frame.
-   *
-   * @param {object} aResult
-   *   Test result details used for reporting.
-   *   <dl>
-   *     <dd>fileName</dd>
-   *     <dt>Name of the file in which the assertion failed.</dt>
-   *     <dd>functionName</dd>
-   *     <dt>Function in which the assertion failed.</dt>
-   *     <dd>lineNumber</dd>
-   *     <dt>Line number of the file in which the assertion failed.</dt>
-   *     <dd>message</dd>
-   *     <dt>Message why the assertion failed.</dt>
-   *   </dl>
-   */
-  _logPass: function Assert__logPass(aResult) {
-    broker.pass({pass: aResult});
-  },
-
-  /**
-   * Test the condition and mark test as passed or failed
-   *
-   * @param {boolean} aCondition
-   *   Condition to test.
-   * @param {string} aMessage
-   *   Message to show for the test result
-   * @param {string} aDiagnosis
-   *   Diagnose message to show for the test result
-   * @throws {errors.AssertionError}
-   *
-   * @returns {boolean} Result of the test.
-   */
-  _test: function Assert__test(aCondition, aMessage, aDiagnosis) {
-    let diagnosis = aDiagnosis || "";
-    let message = aMessage || "";
-
-    if (diagnosis)
-      message = aMessage ? message + " - " + diagnosis : diagnosis;
-
-    // Build result data
-    let frame = stack.findCallerFrame(Components.stack);
-
-    let result = {
-      'fileName'     : frame.filename.replace(/(.*)-> /, ""),
-      'functionName' : frame.name,
-      'lineNumber'   : frame.lineNumber,
-      'message'      : message
-    };
-
-    // Log test result
-    if (aCondition) {
-      this._logPass(result);
-    }
-    else {
-      result.stack = Components.stack;
-      this._logFail(result);
-    }
-
-    return aCondition;
-  },
-
-  /**
-   * Perform an always passing test
-   *
-   * @param {string} aMessage
-   *   Message to show for the test result.
-   * @returns {boolean} Always returns true.
-   */
-  pass: function Assert_pass(aMessage) {
-    return this._test(true, aMessage, undefined);
-  },
-
-  /**
-   * Perform an always failing test
-   *
-   * @param {string} aMessage
-   *   Message to show for the test result.
-   * @throws {errors.AssertionError}
-   *
-   * @returns {boolean} Always returns false.
-   */
-  fail: function Assert_fail(aMessage) {
-    return this._test(false, aMessage, undefined);
-  },
-
-  /**
-   * Test if the value pass
-   *
-   * @param {boolean|string|number|object} aValue
-   *   Value to test.
-   * @param {string} aMessage
-   *   Message to show for the test result.
-   * @throws {errors.AssertionError}
-   *
-   * @returns {boolean} Result of the test.
-   */
-  ok: function Assert_ok(aValue, aMessage) {
-    let condition = !!aValue;
-    let diagnosis = "got '" + aValue + "'";
-
-    return this._test(condition, aMessage, diagnosis);
-  },
-
- /**
-   * Test if both specified values are identical.
-   *
-   * @param {boolean|string|number|object} aValue
-   *   Value to test.
-   * @param {boolean|string|number|object} aExpected
-   *   Value to strictly compare with.
-   * @param {string} aMessage
-   *   Message to show for the test result
-   * @throws {errors.AssertionError}
-   *
-   * @returns {boolean} Result of the test.
-   */
-  equal: function Assert_equal(aValue, aExpected, aMessage) {
-    let condition = (aValue === aExpected);
-    let diagnosis = "'" + aValue + "' should equal '" + aExpected + "'";
-
-    return this._test(condition, aMessage, diagnosis);
-  },
-
- /**
-   * Test if both specified values are not identical.
-   *
-   * @param {boolean|string|number|object} aValue
-   *   Value to test.
-   * @param {boolean|string|number|object} aExpected
-   *   Value to strictly compare with.
-   * @param {string} aMessage
-   *   Message to show for the test result
-   * @throws {errors.AssertionError}
-   *
-   * @returns {boolean} Result of the test.
-   */
-  notEqual: function Assert_notEqual(aValue, aExpected, aMessage) {
-    let condition = (aValue !== aExpected);
-    let diagnosis = "'" + aValue + "' should not equal '" + aExpected + "'";
-
-    return this._test(condition, aMessage, diagnosis);
-  },
-
-  /**
-   * Test if an object equals another object
-   *
-   * @param {object} aValue
-   *   The object to test.
-   * @param {object} aExpected
-   *   The object to strictly compare with.
-   * @param {string} aMessage
-   *   Message to show for the test result
-   * @throws {errors.AssertionError}
-   *
-   * @returns {boolean} Result of the test.
-   */
-  deepEqual: function equal(aValue, aExpected, aMessage) {
-    let condition = this._deepEqual(aValue, aExpected);
-    try {
-      var aValueString = JSON.stringify(aValue);
-    } catch (e) {
-      var aValueString = String(aValue);
-    }
-    try {
-      var aExpectedString = JSON.stringify(aExpected);
-    } catch (e) {
-      var aExpectedString = String(aExpected);
-    }
-
-    let diagnosis = "'" + aValueString + "' should equal '" +
-                    aExpectedString + "'";
-
-    return this._test(condition, aMessage, diagnosis);
-  },
-
-  /**
-   * Test if an object does not equal another object
-   *
-   * @param {object} aValue
-   *   The object to test.
-   * @param {object} aExpected
-   *   The object to strictly compare with.
-   * @param {string} aMessage
-   *   Message to show for the test result
-   * @throws {errors.AssertionError}
-   *
-   * @returns {boolean} Result of the test.
-   */
-  notDeepEqual: function notEqual(aValue, aExpected, aMessage) {
-     let condition = !this._deepEqual(aValue, aExpected);
-     try {
-       var aValueString = JSON.stringify(aValue);
-     } catch (e) {
-       var aValueString = String(aValue);
-     }
-     try {
-       var aExpectedString = JSON.stringify(aExpected);
-     } catch (e) {
-       var aExpectedString = String(aExpected);
-     }
-
-     let diagnosis = "'" + aValueString + "' should not equal '" +
-                     aExpectedString + "'";
-
-     return this._test(condition, aMessage, diagnosis);
-  },
-
-  /**
-   * Test if the regular expression matches the string.
-   *
-   * @param {string} aString
-   *   String to test.
-   * @param {RegEx} aRegex
-   *   Regular expression to use for testing that a match exists.
-   * @param {string} aMessage
-   *   Message to show for the test result
-   * @throws {errors.AssertionError}
-   *
-   * @returns {boolean} Result of the test.
-   */
-  match: function Assert_match(aString, aRegex, aMessage) {
-    // XXX Bug 634948
-    // Regex objects are transformed to strings when evaluated in a sandbox
-    // For now lets re-create the regex from its string representation
-    let pattern = "";
-    let flags = "";
-    try {
-      let matches = aRegex.toString().match(/\/(.*)\/(.*)/);
-
-      pattern = matches[1];
-      flags = matches[2];
-    } catch (e) {
-    }
-
-    let regex = new RegExp(pattern, flags);
-    let condition = (aString.match(regex) !== null);
-    let diagnosis = "'" + regex + "' matches for '" + aString + "'";
-
-    return this._test(condition, aMessage, diagnosis);
-  },
-
-  /**
-   * Test if the regular expression does not match the string.
-   *
-   * @param {string} aString
-   *   String to test.
-   * @param {RegEx} aRegex
-   *   Regular expression to use for testing that a match does not exist.
-   * @param {string} aMessage
-   *   Message to show for the test result
-   * @throws {errors.AssertionError}
-   *
-   * @returns {boolean} Result of the test.
-   */
-  notMatch: function Assert_notMatch(aString, aRegex, aMessage) {
-    // XXX Bug 634948
-    // Regex objects are transformed to strings when evaluated in a sandbox
-    // For now lets re-create the regex from its string representation
-    let pattern = flags = "";
-    try {
-      let matches = aRegex.toString().match(/\/(.*)\/(.*)/);
-
-      pattern = matches[1];
-      flags = matches[2];
-    } catch (e) {
-    }
-
-    let regex = new RegExp(pattern, flags);
-    let condition = (aString.match(regex) === null);
-    let diagnosis = "'" + regex + "' doesn't match for '" + aString + "'";
-
-    return this._test(condition, aMessage, diagnosis);
-  },
-
-
-  /**
-   * Test if a code block throws an exception.
-   *
-   * @param {string} block
-   *   function to call to test for exception
-   * @param {RegEx} error
-   *   the expected error class
-   * @param {string} message
-   *   message to present if assertion fails
-   * @throws {errors.AssertionError}
-   *
-   * @returns {boolean} Result of the test.
-   */
-  throws : function Assert_throws(block, /*optional*/error, /*optional*/message) {
-    return this._throws.apply(this, [true].concat(Array.prototype.slice.call(arguments)));
-  },
-
-  /**
-   * Test if a code block doesn't throw an exception.
-   *
-   * @param {string} block
-   *   function to call to test for exception
-   * @param {RegEx} error
-   *   the expected error class
-   * @param {string} message
-   *   message to present if assertion fails
-   * @throws {errors.AssertionError}
-   *
-   * @returns {boolean} Result of the test.
-   */
-  doesNotThrow : function Assert_doesNotThrow(block, /*optional*/error, /*optional*/message) {
-    return this._throws.apply(this, [false].concat(Array.prototype.slice.call(arguments)));
-  },
-
-  /* Tests whether a code block throws the expected exception
-     class. helper for throws() and doesNotThrow()
-
-     adapted from node.js's assert._throws()
-     https://github.com/joyent/node/blob/master/lib/assert.js
-  */
-  _throws : function Assert__throws(shouldThrow, block, expected, message) {
-    var actual;
-
-    if (typeof expected === 'string') {
-      message = expected;
-      expected = null;
-    }
-
-    try {
-      block();
-    } catch (e) {
-      actual = e;
-    }
-
-    message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
-              (message ? ' ' + message : '.');
-
-    if (shouldThrow && !actual) {
-      return this._test(false, message, 'Missing expected exception');
-    }
-
-    if (!shouldThrow && this._expectedException(actual, expected)) {
-      return this._test(false, message, 'Got unwanted exception');
-    }
-
-    if ((shouldThrow && actual && expected &&
-        !this._expectedException(actual, expected)) || (!shouldThrow && actual)) {
-      throw actual;
-    }
-
-    return this._test(true, message);
-  },
-
-  /**
-   * Test if the string contains the pattern.
-   *
-   * @param {String} aString String to test.
-   * @param {String} aPattern Pattern to look for in the string
-   * @param {String} aMessage Message to show for the test result
-   * @throws {errors.AssertionError}
-   *
-   * @returns {Boolean} Result of the test.
-   */
-  contain: function Assert_contain(aString, aPattern, aMessage) {
-    let condition = (aString.indexOf(aPattern) !== -1);
-    let diagnosis = "'" + aString + "' should contain '" + aPattern + "'";
-
-    return this._test(condition, aMessage, diagnosis);
-  },
-
-  /**
-   * Test if the string does not contain the pattern.
-   *
-   * @param {String} aString String to test.
-   * @param {String} aPattern Pattern to look for in the string
-   * @param {String} aMessage Message to show for the test result
-   * @throws {errors.AssertionError}
-   *
-   * @returns {Boolean} Result of the test.
-   */
-  notContain: function Assert_notContain(aString, aPattern, aMessage) {
-    let condition = (aString.indexOf(aPattern) === -1);
-    let diagnosis = "'" + aString + "' should not contain '" + aPattern + "'";
-
-    return this._test(condition, aMessage, diagnosis);
-  },
-
-  /**
-   * Waits for the callback evaluates to true
-   *
-   * @param {Function} aCallback
-   *        Callback for evaluation
-   * @param {String} aMessage
-   *        Message to show for result
-   * @param {Number} aTimeout
-   *        Timeout in waiting for evaluation
-   * @param {Number} aInterval
-   *        Interval between evaluation attempts
-   * @param {Object} aThisObject
-   *        this object
-   * @throws {errors.AssertionError}
-   *
-   * @returns {Boolean} Result of the test.
-   */
-  waitFor: function Assert_waitFor(aCallback, aMessage, aTimeout, aInterval, aThisObject) {
-    var timeout = aTimeout || 5000;
-    var interval = aInterval || 100;
-
-    var self = {
-      timeIsUp: false,
-      result: aCallback.call(aThisObject)
-    };
-    var deadline = Date.now() + timeout;
-
-    function wait() {
-      if (self.result !== true) {
-        self.result = aCallback.call(aThisObject);
-        self.timeIsUp = Date.now() > deadline;
-      }
-    }
-
-    var hwindow = Services.appShell.hiddenDOMWindow;
-    var timeoutInterval = hwindow.setInterval(wait, interval);
-    var thread = Services.tm.currentThread;
-
-    Services.tm.spinEventLoopUntil(() => {
-      let type = typeof(self.result);
-      if (type !== 'boolean') {
-        throw TypeError("waitFor() callback has to return a boolean" +
-                        " instead of '" + type + "'");
-      }
-
-      return self.result === true || self.timeIsUp;
-    });
-
-    hwindow.clearInterval(timeoutInterval);
-
-    if (self.result !== true && self.timeIsUp) {
-      aMessage = aMessage || arguments.callee.name + ": Timeout exceeded for '" + aCallback + "'";
-      throw new errors.TimeoutError(aMessage);
-    }
-
-    broker.pass({'function':'assert.waitFor()'});
-    return true;
-  }
-}
-
-/* non-fatal assertions */
-var Expect = function () {}
-
-Expect.prototype = new Assert();
-
-/**
- * Log a test as failing by adding a fail frame.
- *
- * @param {object} aResult
- *   Test result details used for reporting.
- *   <dl>
- *     <dd>fileName</dd>
- *     <dt>Name of the file in which the assertion failed.</dt>
- *     <dd>functionName</dd>
- *     <dt>Function in which the assertion failed.</dt>
- *     <dd>lineNumber</dd>
- *     <dt>Line number of the file in which the assertion failed.</dt>
- *     <dd>message</dd>
- *     <dt>Message why the assertion failed.</dt>
- *   </dl>
- */
-Expect.prototype._logFail = function Expect__logFail(aResult) {
-  broker.fail({fail: aResult});
-}
-
-/**
- * Waits for the callback evaluates to true
- *
- * @param {Function} aCallback
- *        Callback for evaluation
- * @param {String} aMessage
- *        Message to show for result
- * @param {Number} aTimeout
- *        Timeout in waiting for evaluation
- * @param {Number} aInterval
- *        Interval between evaluation attempts
- * @param {Object} aThisObject
- *        this object
- */
-Expect.prototype.waitFor = function Expect_waitFor(aCallback, aMessage, aTimeout, aInterval, aThisObject) {
-  let condition = true;
-  let message = aMessage;
-
-  try {
-    Assert.prototype.waitFor.apply(this, arguments);
-  }
-  catch (ex) {
-    if (!(ex instanceof errors.AssertionError)) {
-      throw ex;
-    }
-    message = ex.message;
-    condition = false;
-  }
-
-  return this._test(condition, message);
-}
deleted file mode 100644
--- a/services/sync/tps/extensions/mozmill/resource/modules/driver.js
+++ /dev/null
@@ -1,290 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/**
- * @namespace Defines the Mozmill driver for global actions
- */
-var driver = exports;
-
-Cu.import("resource://gre/modules/Services.jsm");
-
-// Temporarily include utils module to re-use sleep
-var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions);
-var mozmill = {}; Cu.import("resource://mozmill/driver/mozmill.js", mozmill);
-var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
-
-/**
- * Gets the topmost browser window. If there are none at that time, optionally
- * opens one. Otherwise will raise an exception if none are found.
- *
- * @memberOf driver
- * @param {Boolean] [aOpenIfNone=true] Open a new browser window if none are found.
- * @returns {DOMWindow}
- */
-function getBrowserWindow(aOpenIfNone) {
-  // Set default
-  if (typeof aOpenIfNone === 'undefined') {
-    aOpenIfNone = true;
-  }
-
-  // If implicit open is off, turn on strict checking, and vice versa.
-  let win = getTopmostWindowByType("navigator:browser", !aOpenIfNone);
-
-  // Can just assume automatic open here. If we didn't want it and nothing found,
-  // we already raised above when getTopmostWindow was called.
-  if (!win)
-    win = openBrowserWindow();
-
-  return win;
-}
-
-
-/**
- * Retrieves the hidden window on OS X
- *
- * @memberOf driver
- * @returns {DOMWindow} The hidden window
- */
-function getHiddenWindow() {
-  return Services.appShell.hiddenDOMWindow;
-}
-
-
-/**
- * Opens a new browser window
- *
- * @memberOf driver
- * @returns {DOMWindow}
- */
-function openBrowserWindow() {
-  // On OS X we have to be able to create a new browser window even with no other
-  // window open. Therefore we have to use the hidden window. On other platforms
-  // at least one remaining browser window has to exist.
-  var win = mozmill.isMac ? getHiddenWindow() :
-                            getTopmostWindowByType("navigator:browser", true);
-  return win.OpenBrowserWindow();
-}
-
-
-/**
- * Pause the test execution for the given amount of time
- *
- * @type utils.sleep
- * @memberOf driver
- */
-var sleep = utils.sleep;
-
-/**
- * Wait until the given condition via the callback returns true.
- *
- * @type utils.waitFor
- * @memberOf driver
- */
-var waitFor = assertions.Assert.waitFor;
-
-//
-// INTERNAL WINDOW ENUMERATIONS
-//
-
-/**
- * Internal function to build a list of DOM windows using a given enumerator
- * and filter.
- *
- * @private
- * @memberOf driver
- * @param {nsISimpleEnumerator} aEnumerator Window enumerator to use.
- * @param {Function} [aFilterCallback] Function which is used to filter windows.
- * @param {Boolean} [aStrict=true] Throw an error if no windows found
- *
- * @returns {DOMWindow[]} The windows found, in the same order as the enumerator.
- */
-function _getWindows(aEnumerator, aFilterCallback, aStrict) {
-  // Set default
-  if (typeof aStrict === 'undefined')
-    aStrict = true;
-
-  let windows = [];
-
-  while (aEnumerator.hasMoreElements()) {
-    let window = aEnumerator.getNext();
-
-    if (!aFilterCallback || aFilterCallback(window)) {
-      windows.push(window);
-    }
-  }
-
-  // If this list is empty and we're strict, throw an error
-  if (windows.length === 0 && aStrict) {
-    var message = 'No windows were found';
-
-    // We'll throw a more detailed error if a filter was used.
-    if (aFilterCallback && aFilterCallback.name)
-      message += ' using filter "' + aFilterCallback.name + '"';
-
-    throw new Error(message);
-  }
-
-  return windows;
-}
-
-//
-// FILTER CALLBACKS
-//
-
-/**
- * Generator of a closure to filter a window based by a method
- *
- * @memberOf driver
- * @param {String} aName Name of the method in the window object.
- * @returns {Boolean} True if the condition is met.
- */
-function windowFilterByMethod(aName) {
-  return function byMethod(aWindow) { return (aName in aWindow); }
-}
-
-
-/**
- * Generator of a closure to filter a window based by the its title
- *
- * @param {String} aTitle Title of the window.
- * @returns {Boolean} True if the condition is met.
- */
-function windowFilterByTitle(aTitle) {
-  return function byTitle(aWindow) { return (aWindow.document.title === aTitle); }
-}
-
-
-/**
- * Generator of a closure to filter a window based by the its type
- *
- * @memberOf driver
- * @param {String} aType Type of the window.
- * @returns {Boolean} True if the condition is met.
- */
-function windowFilterByType(aType) {
-  return function byType(aWindow) {
-           var type = aWindow.document.documentElement.getAttribute("windowtype");
-           return (type === aType);
-         }
-}
-
-//
-// WINDOW LIST RETRIEVAL FUNCTIONS
-//
-
-/**
- * Retrieves a sorted list of open windows based on their age (newest to oldest),
- * optionally matching filter criteria.
- *
- * @memberOf driver
- * @param {Function} [aFilterCallback] Function which is used to filter windows.
- * @param {Boolean} [aStrict=true] Throw an error if no windows found
- *
- * @returns {DOMWindow[]} List of windows.
- */
-function getWindowsByAge(aFilterCallback, aStrict) {
-  var windows = _getWindows(Services.wm.getEnumerator(""),
-                            aFilterCallback, aStrict);
-
-  // Reverse the list, since naturally comes back old->new
-  return windows.reverse();
-}
-
-
-/**
- * Retrieves a sorted list of open windows based on their z order (topmost first),
- * optionally matching filter criteria.
- *
- * @memberOf driver
- * @param {Function} [aFilterCallback] Function which is used to filter windows.
- * @param {Boolean} [aStrict=true] Throw an error if no windows found
- *
- * @returns {DOMWindow[]} List of windows.
- */
-function getWindowsByZOrder(aFilterCallback, aStrict) {
-  return _getWindows(Services.wm.getZOrderDOMWindowEnumerator("", true),
-                     aFilterCallback, aStrict);
-}
-
-//
-// SINGLE WINDOW RETRIEVAL FUNCTIONS
-//
-
-/**
- * Retrieves the last opened window, optionally matching filter criteria.
- *
- * @memberOf driver
- * @param {Function} [aFilterCallback] Function which is used to filter windows.
- * @param {Boolean} [aStrict=true] If true, throws error if no window found.
- *
- * @returns {DOMWindow} The window, or null if none found and aStrict == false
- */
-function getNewestWindow(aFilterCallback, aStrict) {
-  var windows = getWindowsByAge(aFilterCallback, aStrict);
-  return windows.length ? windows[0] : null;
-}
-
-/**
- * Retrieves the topmost window, optionally matching filter criteria.
- *
- * @memberOf driver
- * @param {Function} [aFilterCallback] Function which is used to filter windows.
- * @param {Boolean} [aStrict=true] If true, throws error if no window found.
- *
- * @returns {DOMWindow} The window, or null if none found and aStrict == false
- */
-function getTopmostWindow(aFilterCallback, aStrict) {
-  var windows = getWindowsByZOrder(aFilterCallback, aStrict);
-  return windows.length ? windows[0] : null;
-}
-
-
-/**
- * Retrieves the topmost window given by the window type
- *
- * XXX: Bug 462222
- *      This function has to be used instead of getTopmostWindow until the
- *      underlying platform bug has been fixed.
- *
- * @memberOf driver
- * @param {String} [aWindowType=null] Window type to query for
- * @param {Boolean} [aStrict=true] Throw an error if no windows found
- *
- * @returns {DOMWindow} The window, or null if none found and aStrict == false
- */
-function getTopmostWindowByType(aWindowType, aStrict) {
-  if (typeof aStrict === 'undefined')
-    aStrict = true;
-
-  var win = Services.wm.getMostRecentWindow(aWindowType);
-
-  if (win === null && aStrict) {
-    var message = 'No windows of type "' + aWindowType + '" were found';
-    throw new errors.UnexpectedError(message);
-  }
-
-  return win;
-}
-
-
-// Export of functions
-driver.getBrowserWindow = getBrowserWindow;
-driver.getHiddenWindow = getHiddenWindow;
-driver.openBrowserWindow = openBrowserWindow;
-driver.sleep = sleep;
-driver.waitFor = waitFor;
-
-driver.windowFilterByMethod = windowFilterByMethod;
-driver.windowFilterByTitle = windowFilterByTitle;
-driver.windowFilterByType = windowFilterByType;
-
-driver.getWindowsByAge = getWindowsByAge;
-driver.getNewestWindow = getNewestWindow;
-driver.getTopmostWindowByType = getTopmostWindowByType;
-
-
-// XXX Bug: 462222
-//     Currently those functions cannot be used. So they shouldn't be exported.
-//driver.getWindowsByZOrder = getWindowsByZOrder;
-//driver.getTopmostWindow = getTopmostWindow;
deleted file mode 100644
--- a/services/sync/tps/extensions/mozmill/resource/modules/errors.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
-
-var EXPORTED_SYMBOLS = ['BaseError',
-                        'ApplicationQuitError',
-                        'AssertionError',
-                        'TimeoutError'];
-
-
-/**
- * Creates a new instance of a base error
- *
- * @class Represents the base for custom errors
- * @param {string} [aMessage=Error().message]
- *        The error message to show
- * @param {string} [aFileName=Error().fileName]
- *        The file name where the error has been raised
- * @param {string} [aLineNumber=Error().lineNumber]
- *        The line number of the file where the error has been raised
- * @param {string} [aFunctionName=undefined]
- *        The function name in which the error has been raised
- */
-function BaseError(aMessage, aFileName, aLineNumber, aFunctionName) {
-  this.name = this.constructor.name;