Merge mozilla-inbound to mozilla-central. a=merge
authordvarga <dvarga@mozilla.com>
Sat, 28 Jul 2018 01:32:38 +0300
changeset 483891 096d073c25be
parent 483884 71f410a4fe34 (current diff)
parent 483890 dd81b3a3070a (diff)
child 483910 0dacb45a6565
child 483937 b1713dde630a
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
096d073c25be / 63.0a1 / 20180727231224 / files
nightly linux64
096d073c25be / 63.0a1 / 20180727231224 / files
nightly mac
096d073c25be / 63.0a1 / 20180727231224 / files
nightly win32
096d073c25be / 63.0a1 / 20180727231224 / files
nightly win64
096d073c25be / 63.0a1 / 20180727231224 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central. a=merge
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -8,16 +8,17 @@ import time
 import re
 import os
 import posixpath
 import tempfile
 import shutil
 import sys
 
 from automation import Automation
+from mozdevice import ADBTimeoutError
 from mozlog import get_default_logger
 from mozscreenshot import dump_screen
 import mozcrash
 
 # signatures for logcat messages that we don't care about much
 fennecLogcatFilters = ["The character encoding of the HTML document was not declared",
                        "Use of Mutation Events is deprecated. Use MutationObserver instead.",
                        "Unexpected value from nativeGetEnabledTags: 0"]
@@ -289,16 +290,18 @@ class RemoteAutomation(Automation):
             Fetch the full remote log file, log any new content and return True if new
             content processed.
             """
             if not self.device.is_file(self.proc):
                 return False
             try:
                 newLogContent = self.device.get_file(
                     self.proc, offset=self.stdoutlen)
+            except ADBTimeoutError:
+                raise
             except Exception:
                 return False
             if not newLogContent:
                 return False
 
             self.stdoutlen += len(newLogContent)
 
             if self.messageLogger is None:
@@ -339,16 +342,18 @@ class RemoteAutomation(Automation):
                                 try:
                                     val = int(m.group(1))
                                     if "Passed:" in line:
                                         self.counts['pass'] += val
                                     elif "Failed:" in line:
                                         self.counts['fail'] += val
                                     elif "Todo:" in line:
                                         self.counts['todo'] += val
+                                except ADBTimeoutError:
+                                    raise
                                 except Exception:
                                     pass
 
             return True
 
         @property
         def getLastTestSeen(self):
             return self.lastTestSeen
@@ -407,33 +412,39 @@ class RemoteAutomation(Automation):
                 # options but they rarely work well with Firefox on the
                 # Android emulator. dump_screen provides an effective
                 # screenshot of the emulator and its host desktop.
                 dump_screen(self.utilityPath, get_default_logger())
             if stagedShutdown:
                 # Trigger an ANR report with "kill -3" (SIGQUIT)
                 try:
                     self.device.pkill(self.procName, sig=3, attempts=1)
+                except ADBTimeoutError:
+                    raise
                 except:  # NOQA: E722
                     pass
                 time.sleep(3)
                 # Trigger a breakpad dump with "kill -6" (SIGABRT)
                 try:
                     self.device.pkill(self.procName, sig=6, attempts=1)
+                except ADBTimeoutError:
+                    raise
                 except:  # NOQA: E722
                     pass
                 # Wait for process to end
                 retries = 0
                 while retries < 3:
                     if self.device.process_exist(self.procName):
                         print("%s still alive after SIGABRT: waiting..." % self.procName)
                         time.sleep(5)
                     else:
                         return
                     retries += 1
                 try:
                     self.device.pkill(self.procName, sig=9, attempts=1)
+                except ADBTimeoutError:
+                    raise
                 except:  # NOQA: E722
                     print("%s still alive after SIGKILL!" % self.procName)
                 if self.device.process_exist(self.procName):
                     self.device.stop_application(self.procName)
             else:
                 self.device.stop_application(self.procName)
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
@@ -1,9 +1,9 @@
-52849
+52853
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -22174,16 +22174,17 @@ dazzle/MZGDRS
 dazzler/M
 dazzling/Y
 db
 dbl
 dc
 dd/SDG
 dded/K
 dding/K
+de
 deacon/MS
 deaconess/MS
 dead/XTMNRY
 deadbeat/MS
 deadbolt/SM
 deaden/GD
 deadhead/SDG
 deadline/SM
@@ -25790,16 +25791,17 @@ facing/SM
 facsimile/DSM
 facsimileing
 fact/MS
 faction/SM
 factional
 factionalism/M
 factious
 factitious
+facto
 factoid/SM
 factor's
 factor/ASDG
 factorial/MS
 factorization
 factorize/GDS
 factory/SM
 factotum/SM
@@ -42047,16 +42049,17 @@ rearward/S
 reason/SMDRZGB
 reasonable/UP
 reasonableness/UM
 reasonably/U
 reasoner/M
 reasoning/M
 reassemble/DSG
 reassuring/Y
+rebar/S
 rebate/M
 rebel/MS
 rebellion/MS
 rebellious/YP
 rebelliousness/M
 rebid/S
 rebidding
 rebirth/M
@@ -44258,16 +44261,17 @@ seepage/M
 seer/M
 seersucker/M
 seesaw/SMDG
 seethe/DSG
 segfault/S
 segment/GSMD
 segmentation/M
 segmented/U
+segregable
 segregate/CDSGN
 segregated/U
 segregation/CM
 segregationist/MS
 segue/MGDS
 segueing
 seigneur/SM
 seignior/SM
--- a/ipc/chromium/src/base/shared_memory.h
+++ b/ipc/chromium/src/base/shared_memory.h
@@ -108,16 +108,24 @@ class SharedMemory {
   //   return ok;
   // Note that the memory is unmapped by calling this method, regardless of the
   // return value.
   bool GiveToProcess(ProcessId target_pid,
                      SharedMemoryHandle* new_handle) {
     return ShareToProcessCommon(target_pid, new_handle, true);
   }
 
+#ifdef OS_POSIX
+  // If named POSIX shm is being used, append the prefix (including
+  // the leading '/') that would be used by a process with the given
+  // pid to the given string and return true.  If not, return false.
+  // (This is public so that the Linux sandboxing code can use it.)
+  static bool AppendPosixShmPrefix(std::string* str, pid_t pid);
+#endif
+
  private:
   bool ShareToProcessCommon(ProcessId target_pid,
                             SharedMemoryHandle* new_handle,
                             bool close_self);
 
 #if defined(OS_WIN)
   HANDLE             mapped_file_;
 #elif defined(OS_POSIX)
--- a/ipc/chromium/src/base/shared_memory_posix.cc
+++ b/ipc/chromium/src/base/shared_memory_posix.cc
@@ -7,21 +7,25 @@
 #include "base/shared_memory.h"
 
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "base/file_util.h"
+#ifdef ANDROID
+#include <linux/ashmem.h>
+#endif
+
+#include "base/eintr_wrapper.h"
 #include "base/logging.h"
-#include "base/platform_thread.h"
 #include "base/string_util.h"
-#include "mozilla/UniquePtr.h"
+#include "mozilla/Atomics.h"
+#include "prenv.h"
 
 namespace base {
 
 SharedMemory::SharedMemory()
     : mapped_file_(-1),
       memory_(NULL),
       read_only_(false),
       max_size_(0) {
@@ -44,69 +48,102 @@ bool SharedMemory::IsHandleValid(const S
   return handle.fd >= 0;
 }
 
 // static
 SharedMemoryHandle SharedMemory::NULLHandle() {
   return SharedMemoryHandle();
 }
 
-namespace {
-
-// A class to handle auto-closing of FILE*'s.
-class ScopedFILEClose {
- public:
-  inline void operator()(FILE* x) const {
-    if (x) {
-      fclose(x);
-    }
+// static
+bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid)
+{
+#if defined(ANDROID) || defined(SHM_ANON)
+  return false;
+#else
+  *str += '/';
+#ifdef OS_LINUX
+  // The Snap package environment doesn't provide a private /dev/shm
+  // (it's used for communication with services like PulseAudio);
+  // instead AppArmor is used to restrict access to it.  Anything with
+  // this prefix is allowed:
+  static const char* const kSnap = PR_GetEnv("SNAP_NAME");
+  if (kSnap) {
+    StringAppendF(str, "snap.%s.", kSnap);
   }
-};
-
-typedef mozilla::UniquePtr<FILE, ScopedFILEClose> ScopedFILE;
-
+#endif // OS_LINUX
+  // Hopefully the "implementation defined" name length limit is long
+  // enough for this.
+  StringAppendF(str, "org.mozilla.ipc.%d.", static_cast<int>(pid));
+  return true;
+#endif // !ANDROID && !SHM_ANON
 }
 
 bool SharedMemory::Create(size_t size) {
   read_only_ = false;
 
   DCHECK(size > 0);
   DCHECK(mapped_file_ == -1);
 
-  ScopedFILE file_closer;
-  FILE *fp;
-
-  FilePath path;
-  fp = file_util::CreateAndOpenTemporaryShmemFile(&path);
-
-  // Deleting the file prevents anyone else from mapping it in
-  // (making it private), and prevents the need for cleanup (once
-  // the last fd is closed, it is truly freed).
-  file_util::Delete(path);
-
-  if (fp == NULL)
-    return false;
-  file_closer.reset(fp);  // close when we go out of scope
+  int fd;
+  bool needs_truncate = true;
 
-  // Set the file size.
-  //
-  // According to POSIX, (f)truncate can be used to extend files;
-  // previously this required the XSI option but as of the 2008
-  // edition it's required for everything.  (Linux documents that this
-  // may fail on non-"native" filesystems like FAT, but /dev/shm
-  // should always be tmpfs.)
-  if (ftruncate(fileno(fp), size) != 0)
+#ifdef ANDROID
+  // Android has its own shared memory facility:
+  fd = open("/" ASHMEM_NAME_DEF, O_RDWR, 0600);
+  if (fd < 0) {
+    CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
+    return false;
+  }
+  if (ioctl(fd, ASHMEM_SET_SIZE, size) != 0) {
+    CHROMIUM_LOG(WARNING) << "failed to set shm size: " << strerror(errno);
+    close(fd);
     return false;
-  // This probably isn't needed.
-  if (fseeko(fp, size, SEEK_SET) != 0)
-    return false;
+  }
+  needs_truncate = false;
+#elif defined(SHM_ANON)
+  // FreeBSD (or any other Unix that might decide to implement this
+  // nice, simple API):
+  fd = shm_open(SHM_ANON, O_RDWR, 0600);
+#else
+  // Generic Unix: shm_open + shm_unlink
+  do {
+    // The names don't need to be unique, but it saves time if they
+    // usually are.
+    static mozilla::Atomic<size_t> sNameCounter;
+    std::string name;
+    CHECK(AppendPosixShmPrefix(&name, getpid()));
+    StringAppendF(&name, "%zu", sNameCounter++);
+    // O_EXCL means the names being predictable shouldn't be a problem.
+    fd = HANDLE_EINTR(shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600));
+    if (fd >= 0) {
+      if (shm_unlink(name.c_str()) != 0) {
+        // This shouldn't happen, but if it does: assume the file is
+        // in fact leaked, and bail out now while it's still 0-length.
+        DLOG(FATAL) << "failed to unlink shm: " << strerror(errno);
+        return false;
+      }
+    }
+  } while (fd < 0 && errno == EEXIST);
+#endif
 
-  mapped_file_ = dup(fileno(fp));
-  DCHECK(mapped_file_ >= 0);
+  if (fd < 0) {
+    CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno);
+    return false;
+  }
 
+  if (needs_truncate) {
+    if (HANDLE_EINTR(ftruncate(fd, static_cast<off_t>(size))) != 0) {
+      CHROMIUM_LOG(WARNING) << "failed to set shm size: " << strerror(errno);
+      close(fd);
+      return false;
+    }
+  }
+
+  mapped_file_ = fd;
   max_size_ = size;
   return true;
 }
 
 bool SharedMemory::Map(size_t bytes) {
   if (mapped_file_ == -1)
     return false;
 
--- a/js/src/tests/lib/jittests.py
+++ b/js/src/tests/lib/jittests.py
@@ -376,17 +376,17 @@ def find_tests(substring=None):
             test = os.path.join(dirpath, filename)
             if substring is None \
                or substring in os.path.relpath(test, TEST_DIR):
                 ans.append(test)
     return ans
 
 
 def run_test_remote(test, device, prefix, options):
-    from mozdevice import ADBDevice, ADBProcessError
+    from mozdevice import ADBDevice, ADBProcessError, ADBTimeoutError
 
     if options.test_reflect_stringify:
         raise ValueError("can't run Reflect.stringify tests remotely")
     cmd = test.command(prefix,
                        posixpath.join(options.remote_test_root, 'lib/'),
                        posixpath.join(options.remote_test_root, 'modules/'),
                        posixpath.join(options.remote_test_root, 'tests'))
     if options.show_cmd:
@@ -400,16 +400,18 @@ def run_test_remote(test, device, prefix
 
     cmd = ADBDevice._escape_command_line(cmd)
     start = datetime.now()
     try:
         out = device.shell_output(cmd, env=env,
                                   cwd=options.remote_test_root,
                                   timeout=int(options.timeout))
         returncode = 0
+    except ADBTimeoutError:
+        raise
     except ADBProcessError as e:
         out = e.adb_process.stdout
         print("exception output: %s" % str(out))
         returncode = e.adb_process.exitcode
 
     elapsed = (datetime.now() - start).total_seconds()
 
     # We can't distinguish between stdout and stderr so we pass
--- a/layout/tools/reftest/remotereftest.py
+++ b/layout/tools/reftest/remotereftest.py
@@ -8,17 +8,17 @@ import psutil
 import signal
 import sys
 import tempfile
 import time
 import traceback
 import urllib2
 from contextlib import closing
 
-from mozdevice import ADBAndroid
+from mozdevice import ADBAndroid, ADBTimeoutError
 import mozinfo
 from automation import Automation
 from remoteautomation import RemoteAutomation, fennecLogcatFilters
 
 from output import OutputHandler
 from runreftest import RefTest, ReftestResolver
 import reftestcommandline
 
@@ -344,16 +344,18 @@ class RemoteReftest(RefTest):
             for category in devinfo:
                 if type(devinfo[category]) is list:
                     print "  %s:" % category
                     for item in devinfo[category]:
                         print "     %s" % item
                 else:
                     print "  %s: %s" % (category, devinfo[category])
             print "Test root: %s" % self.device.test_root
+        except ADBTimeoutError:
+            raise
         except Exception as e:
             print "WARNING: Error getting device information: %s" % str(e)
 
     def environment(self, **kwargs):
         return self.automation.environment(**kwargs)
 
     def buildBrowserEnv(self, options, profileDir):
         browserEnv = RefTest.buildBrowserEnv(self, options, profileDir)
--- a/media/mtransport/third_party/nICEr/nicer.gyp
+++ b/media/mtransport/third_party/nICEr/nicer.gyp
@@ -202,16 +202,17 @@
                  'include_dirs': [
                      '../nrappkit/src/port/win32/include'
                  ],
               }],
 
               # Windows, clang-cl build
               [ 'clang_cl == 1', {
                 'cflags_mozilla': [
+                    '-Xclang',
                     '-Wall',
                     '-Wno-parentheses',
                     '-Wno-strict-prototypes',
                     '-Wmissing-prototypes',
                     '-Wno-format',
                     '-Wno-format-security',
                  ],
               }],
--- a/media/mtransport/third_party/nrappkit/nrappkit.gyp
+++ b/media/mtransport/third_party/nrappkit/nrappkit.gyp
@@ -199,16 +199,17 @@
                  'sources': [
                       './src/port/win32/include/csi_platform.h',
                  ],
               }],
 
               # Windows, clang-cl build
               [ 'clang_cl == 1', {
                 'cflags_mozilla': [
+                    '-Xclang',
                     '-Wall',
                     '-Wno-parentheses',
                     '-Wno-strict-prototypes',
                     '-Wmissing-prototypes',
                     '-Wno-format',
                     '-Wno-format-security',
                  ],
               }],
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -21,26 +21,24 @@ from . import errors
 from . import transport
 from .decorators import do_process_check
 from .geckoinstance import GeckoInstance
 from .keys import Keys
 from .timeout import Timeouts
 
 CHROME_ELEMENT_KEY = "chromeelement-9fc5-4b51-a3c8-01716eedeb04"
 FRAME_KEY = "frame-075b-4da1-b6ba-e579c2d3230a"
-LEGACY_ELEMENT_KEY = "ELEMENT"
 WEB_ELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf"
 WINDOW_KEY = "window-fcc6-11e5-b4f8-330a88ab9d7f"
 
 
 class HTMLElement(object):
     """Represents a DOM Element."""
 
-    identifiers = (CHROME_ELEMENT_KEY, FRAME_KEY, WINDOW_KEY,
-                   LEGACY_ELEMENT_KEY, WEB_ELEMENT_KEY)
+    identifiers = (CHROME_ELEMENT_KEY, FRAME_KEY, WINDOW_KEY, WEB_ELEMENT_KEY)
 
     def __init__(self, marionette, id):
         self.marionette = marionette
         assert(id is not None)
         self.id = id
 
     def __str__(self):
         return self.id
@@ -182,18 +180,16 @@ class HTMLElement(object):
         return self.marionette._send_message("WebDriver:GetElementCSSValue",
                                              body, key="value")
 
     @classmethod
     def _from_json(cls, json, marionette):
         if isinstance(json, dict):
             if WEB_ELEMENT_KEY in json:
                 return cls(marionette, json[WEB_ELEMENT_KEY])
-            elif LEGACY_ELEMENT_KEY in json:
-                return cls(marionette, json[LEGACY_ELEMENT_KEY])
             elif CHROME_ELEMENT_KEY in json:
                 return cls(marionette, json[CHROME_ELEMENT_KEY])
             elif FRAME_KEY in json:
                 return cls(marionette, json[FRAME_KEY])
             elif WINDOW_KEY in json:
                 return cls(marionette, json[WINDOW_KEY])
         raise ValueError("Unrecognised web element")
 
@@ -1606,17 +1602,17 @@ class Marionette(object):
             for arg in args:
                 wrapped.append(self._to_json(arg))
         elif isinstance(args, dict):
             wrapped = {}
             for arg in args:
                 wrapped[arg] = self._to_json(args[arg])
         elif type(args) == HTMLElement:
             wrapped = {WEB_ELEMENT_KEY: args.id,
-                       LEGACY_ELEMENT_KEY: args.id}
+                       CHROME_ELEMENT_KEY: args.id}
         elif (isinstance(args, bool) or isinstance(args, basestring) or
               isinstance(args, int) or isinstance(args, float) or args is None):
             wrapped = args
         return wrapped
 
     def _from_json(self, value):
         if isinstance(value, list):
             unwrapped = []
--- a/testing/marionette/element.js
+++ b/testing/marionette/element.js
@@ -1438,17 +1438,16 @@ class WebElement {
    */
   static fromJSON(json) {
     assert.object(json);
     let keys = Object.keys(json);
 
     for (let key of keys) {
       switch (key) {
         case ContentWebElement.Identifier:
-        case ContentWebElement.LegacyIdentifier:
           return ContentWebElement.fromJSON(json);
 
         case ContentWebWindow.Identifier:
           return ContentWebWindow.fromJSON(json);
 
         case ContentWebFrame.Identifier:
           return ContentWebFrame.fromJSON(json);
 
@@ -1497,32 +1496,30 @@ class WebElement {
 
       default:
         throw new InvalidArgumentError("Unknown context: " + context);
     }
   }
 
   /**
    * Checks if <var>ref<var> is a {@link WebElement} reference,
-   * i.e. if it has {@link ContentWebElement.Identifier},
-   * {@link ContentWebElement.LegacyIdentifier}, or
+   * i.e. if it has {@link ContentWebElement.Identifier}, or
    * {@link ChromeWebElement.Identifier} as properties.
    *
    * @param {Object.<string, string>} obj
    *     Object that represents a reference to a {@link WebElement}.
    * @return {boolean}
    *     True if <var>obj</var> is a {@link WebElement}, false otherwise.
    */
   static isReference(obj) {
     if (Object.prototype.toString.call(obj) != "[object Object]") {
       return false;
     }
 
     if ((ContentWebElement.Identifier in obj) ||
-        (ContentWebElement.LegacyIdentifier in obj) ||
         (ContentWebWindow.Identifier in obj) ||
         (ContentWebFrame.Identifier in obj) ||
         (ChromeWebElement.Identifier in obj)) {
       return true;
     }
     return false;
   }
 
@@ -1540,49 +1537,42 @@ class WebElement {
 this.WebElement = WebElement;
 
 /**
  * DOM elements are represented as web elements when they are
  * transported over the wire protocol.
  */
 class ContentWebElement extends WebElement {
   toJSON() {
-    return {
-      [ContentWebElement.Identifier]: this.uuid,
-      [ContentWebElement.LegacyIdentifier]: this.uuid,
-    };
+    return {[ContentWebElement.Identifier]: this.uuid};
   }
 
   static fromJSON(json) {
-    const {Identifier, LegacyIdentifier} = ContentWebElement;
+    const {Identifier} = ContentWebElement;
 
-    if (!(Identifier in json) && !(LegacyIdentifier in json)) {
+    if (!(Identifier in json)) {
       throw new InvalidArgumentError(
           pprint`Expected web element reference, got: ${json}`);
     }
 
-    let uuid = json[Identifier] || json[LegacyIdentifier];
+    let uuid = json[Identifier];
     return new ContentWebElement(uuid);
   }
 }
 ContentWebElement.Identifier = "element-6066-11e4-a52e-4f735466cecf";
-ContentWebElement.LegacyIdentifier = "ELEMENT";
 this.ContentWebElement = ContentWebElement;
 
 /**
  * Top-level browsing contexts, such as <code>WindowProxy</code>
  * whose <code>opener</code> is null, are represented as web windows
  * over the wire protocol.
  */
 class ContentWebWindow extends WebElement {
   toJSON() {
-    return {
-      [ContentWebWindow.Identifier]: this.uuid,
-      [ContentWebElement.LegacyIdentifier]: this.uuid,
-    };
+    return {[ContentWebWindow.Identifier]: this.uuid};
   }
 
   static fromJSON(json) {
     if (!(ContentWebWindow.Identifier in json)) {
       throw new InvalidArgumentError(
           pprint`Expected web window reference, got: ${json}`);
     }
     let uuid = json[ContentWebWindow.Identifier];
@@ -1594,20 +1584,17 @@ this.ContentWebWindow = ContentWebWindow
 
 /**
  * Nested browsing contexts, such as the <code>WindowProxy</code>
  * associated with <tt>&lt;frame&gt;</tt> and <tt>&lt;iframe&gt;</tt>,
  * are represented as web frames over the wire protocol.
  */
 class ContentWebFrame extends WebElement {
   toJSON() {
-    return {
-      [ContentWebFrame.Identifier]: this.uuid,
-      [ContentWebElement.LegacyIdentifier]: this.uuid,
-    };
+    return {[ContentWebFrame.Identifier]: this.uuid};
   }
 
   static fromJSON(json) {
     if (!(ContentWebFrame.Identifier in json)) {
       throw new InvalidArgumentError(
           pprint`Expected web frame reference, got: ${json}`);
     }
     let uuid = json[ContentWebFrame.Identifier];
@@ -1618,20 +1605,17 @@ ContentWebFrame.Identifier = "frame-075b
 this.ContentWebFrame = ContentWebFrame;
 
 /**
  * XUL elements in chrome space are represented as chrome web elements
  * over the wire protocol.
  */
 class ChromeWebElement extends WebElement {
   toJSON() {
-    return {
-      [ChromeWebElement.Identifier]: this.uuid,
-      [ContentWebElement.LegacyIdentifier]: this.uuid,
-    };
+    return {[ChromeWebElement.Identifier]: this.uuid};
   }
 
   static fromJSON(json) {
     if (!(ChromeWebElement.Identifier in json)) {
       throw new InvalidArgumentError("Expected chrome element reference " +
           pprint`for XUL/XBL element, got: ${json}`);
     }
     let uuid = json[ChromeWebElement.Identifier];
--- a/testing/marionette/test/unit/test_element.js
+++ b/testing/marionette/test/unit/test_element.js
@@ -353,47 +353,25 @@ add_test(function test_WebElement_from()
   ok(WebElement.from(xulEl) instanceof ChromeWebElement);
 
   Assert.throws(() => WebElement.from({}), InvalidArgumentError);
 
   run_next_test();
 });
 
 add_test(function test_WebElement_fromJSON_ContentWebElement() {
-  const {Identifier, LegacyIdentifier} = ContentWebElement;
-
-  let refNew = {[Identifier]: "foo"};
-  let webElNew = WebElement.fromJSON(refNew);
-  ok(webElNew instanceof ContentWebElement);
-  equal(webElNew.uuid, "foo");
-
-  let refOld = {[LegacyIdentifier]: "foo"};
-  let webElOld = WebElement.fromJSON(refOld);
-  ok(webElOld instanceof ContentWebElement);
-  equal(webElOld.uuid, "foo");
+  const {Identifier} = ContentWebElement;
 
-  ok(webElNew.is(webElOld));
-  ok(webElOld.is(webElNew));
-
-  let refBoth = {
-    [Identifier]: "foo",
-    [LegacyIdentifier]: "foo",
-  };
-  let webElBoth = WebElement.fromJSON(refBoth);
-  ok(webElBoth instanceof ContentWebElement);
-  equal(webElBoth.uuid, "foo");
-
-  ok(webElBoth.is(webElNew));
-  ok(webElBoth.is(webElOld));
-  ok(webElNew.is(webElBoth));
-  ok(webElOld.is(webElBoth));
+  let ref = {[Identifier]: "foo"};
+  let webEl = WebElement.fromJSON(ref);
+  ok(webEl instanceof ContentWebElement);
+  equal(webEl.uuid, "foo");
 
   let identifierPrecedence = {
     [Identifier]: "identifier-uuid",
-    [LegacyIdentifier]: "legacyidentifier-uuid",
   };
   let precedenceEl = WebElement.fromJSON(identifierPrecedence);
   ok(precedenceEl instanceof ContentWebElement);
   equal(precedenceEl.uuid, "identifier-uuid");
 
   run_next_test();
 });
 
@@ -445,61 +423,46 @@ add_test(function test_WebElement_fromUU
 });
 
 add_test(function test_WebElement_isReference() {
   for (let t of [42, true, "foo", [], {}]) {
     ok(!WebElement.isReference(t));
   }
 
   ok(WebElement.isReference({[ContentWebElement.Identifier]: "foo"}));
-  ok(WebElement.isReference({[ContentWebElement.LegacyIdentifier]: "foo"}));
   ok(WebElement.isReference({[ContentWebWindow.Identifier]: "foo"}));
   ok(WebElement.isReference({[ContentWebFrame.Identifier]: "foo"}));
   ok(WebElement.isReference({[ChromeWebElement.Identifier]: "foo"}));
 
   run_next_test();
 });
 
 add_test(function test_WebElement_generateUUID() {
   equal(typeof WebElement.generateUUID(), "string");
   run_next_test();
 });
 
 add_test(function test_ContentWebElement_toJSON() {
-  const {Identifier, LegacyIdentifier} = ContentWebElement;
+  const {Identifier} = ContentWebElement;
 
   let el = new ContentWebElement("foo");
   let json = el.toJSON();
 
   ok(Identifier in json);
-  ok(LegacyIdentifier in json);
   equal(json[Identifier], "foo");
-  equal(json[LegacyIdentifier], "foo");
 
   run_next_test();
 });
 
 add_test(function test_ContentWebElement_fromJSON() {
-  const {Identifier, LegacyIdentifier} = ContentWebElement;
-
-  let newEl = ContentWebElement.fromJSON({[Identifier]: "foo"});
-  ok(newEl instanceof ContentWebElement);
-  equal(newEl.uuid, "foo");
+  const {Identifier} = ContentWebElement;
 
-  let oldEl = ContentWebElement.fromJSON({[LegacyIdentifier]: "foo"});
-  ok(oldEl instanceof ContentWebElement);
-  equal(oldEl.uuid, "foo");
-
-  let bothRef = {
-    [Identifier]: "identifier-uuid",
-    [LegacyIdentifier]: "legacyidentifier-foo",
-  };
-  let bothEl = ContentWebElement.fromJSON(bothRef);
-  ok(bothEl instanceof ContentWebElement);
-  equal(bothEl.uuid, "identifier-uuid");
+  let el = ContentWebElement.fromJSON({[Identifier]: "foo"});
+  ok(el instanceof ContentWebElement);
+  equal(el.uuid, "foo");
 
   Assert.throws(() => ContentWebElement.fromJSON({}), InvalidArgumentError);
 
   run_next_test();
 });
 
 add_test(function test_ContentWebWindow_toJSON() {
   let win = new ContentWebWindow("foo");
--- a/testing/mochitest/runrobocop.py
+++ b/testing/mochitest/runrobocop.py
@@ -17,17 +17,17 @@ sys.path.insert(
 
 from automation import Automation
 from remoteautomation import RemoteAutomation, fennecLogcatFilters
 from runtests import KeyValueParseError, MochitestDesktop, MessageLogger, parseKeyValue
 from mochitest_options import MochitestArgumentParser
 
 from manifestparser import TestManifest
 from manifestparser.filters import chunk_by_slice
-from mozdevice import ADBAndroid
+from mozdevice import ADBAndroid, ADBTimeoutError
 import mozfile
 import mozinfo
 
 SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
 
 
 class RobocopTestRunner(MochitestDesktop):
     """
@@ -336,16 +336,18 @@ class RobocopTestRunner(MochitestDesktop
             for category in devinfo:
                 if type(devinfo[category]) is list:
                     self.log.info("  %s:" % category)
                     for item in devinfo[category]:
                         self.log.info("     %s" % item)
                 else:
                     self.log.info("  %s: %s" % (category, devinfo[category]))
             self.log.info("Test root: %s" % self.device.test_root)
+        except ADBTimeoutError:
+            raise
         except Exception as e:
             self.log.warning("Error getting device information: %s" % str(e))
 
     def setupRobotiumConfig(self, browserEnv):
         """
            Create robotium.config and push it to the device.
         """
         fHandle = tempfile.NamedTemporaryFile(suffix='.config',
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -2808,20 +2808,23 @@ toolbar#nav-bar {
                     screenshotOnFail=options.screenshotOnFail,
                     bisectChunk=options.bisectChunk,
                     marionette_args=marionette_args,
                 )
                 status = ret or status
         except KeyboardInterrupt:
             self.log.info("runtests.py | Received keyboard interrupt.\n")
             status = -1
-        except Exception:
+        except Exception as e:
             traceback.print_exc()
             self.log.error(
                 "Automation Error: Received unexpected exception while running application\n")
+            if 'ADBTimeoutError' in repr(e):
+                self.log.info("runtests.py | Device disconnected. Aborting test.\n")
+                raise
             status = 1
         finally:
             self.stopServers()
 
         ignoreMissingLeaks = options.ignoreMissingLeaks
         leakThresholds = options.leakThresholds
 
         # Stop leak detection if m-bc code coverage is enabled
--- a/testing/mochitest/runtestsremote.py
+++ b/testing/mochitest/runtestsremote.py
@@ -12,17 +12,17 @@ sys.path.insert(
         os.path.realpath(
             os.path.dirname(__file__))))
 
 from automation import Automation
 from remoteautomation import RemoteAutomation, fennecLogcatFilters
 from runtests import MochitestDesktop, MessageLogger
 from mochitest_options import MochitestArgumentParser
 
-from mozdevice import ADBAndroid
+from mozdevice import ADBAndroid, ADBTimeoutError
 import mozinfo
 
 SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
 
 
 class MochiRemote(MochitestDesktop):
     localProfile = None
     logMessages = []
@@ -278,16 +278,18 @@ class MochiRemote(MochitestDesktop):
             for category in devinfo:
                 if type(devinfo[category]) is list:
                     self.log.info("  %s:" % category)
                     for item in devinfo[category]:
                         self.log.info("     %s" % item)
                 else:
                     self.log.info("  %s: %s" % (category, devinfo[category]))
             self.log.info("Test root: %s" % self.device.test_root)
+        except ADBTimeoutError:
+            raise
         except Exception as e:
             self.log.warning("Error getting device information: %s" % str(e))
 
     def getGMPPluginPath(self, options):
         # TODO: bug 1149374
         return None
 
     def buildBrowserEnv(self, options, debugger=False):
@@ -339,31 +341,36 @@ def run_test_harness(parser, options):
         options.extensionsToExclude.append('roboextender@mozilla.org')
 
     mochitest = MochiRemote(options)
 
     if options.log_mach is None and not options.verify:
         mochitest.printDeviceInfo()
 
     try:
+        device_exception = False
         if options.verify:
             retVal = mochitest.verifyTests(options)
         else:
             retVal = mochitest.runTests(options)
-    except Exception:
+    except Exception as e:
         mochitest.log.error("Automation Error: Exception caught while running tests")
         traceback.print_exc()
-        try:
-            mochitest.cleanup(options)
-        except Exception:
-            # device error cleaning up... oh well!
-            traceback.print_exc()
+        if isinstance(e, ADBTimeoutError):
+            mochitest.log.info("Device disconnected. Will not run mochitest.cleanup().")
+            device_exception = True
+        else:
+            try:
+                mochitest.cleanup(options)
+            except Exception:
+                # device error cleaning up... oh well!
+                traceback.print_exc()
         retVal = 1
 
-    if options.log_mach is None and not options.verify:
+    if not device_exception and options.log_mach is None and not options.verify:
         mochitest.printDeviceInfo(printLogcat=True)
 
     mochitest.message_logger.finish()
 
     return retVal
 
 
 def main(args=sys.argv[1:]):
--- a/testing/remotecppunittests.py
+++ b/testing/remotecppunittests.py
@@ -9,17 +9,17 @@ import sys
 import subprocess
 from zipfile import ZipFile
 import runcppunittests as cppunittests
 import mozcrash
 import mozfile
 import mozinfo
 import mozlog
 import posixpath
-from mozdevice import ADBAndroid, ADBProcessError
+from mozdevice import ADBAndroid, ADBProcessError, ADBTimeoutError
 
 try:
     from mozbuild.base import MozbuildObject
     build_obj = MozbuildObject.from_environment()
 except ImportError:
     build_obj = None
 
 
@@ -134,16 +134,18 @@ class RemoteCPPUnitTests(cppunittests.CP
         test_timeout = cppunittests.CPPUnitTests.TEST_PROC_TIMEOUT * \
             timeout_factor
 
         try:
             output = self.device.shell_output(remote_bin, env=env,
                                               cwd=self.remote_home_dir,
                                               timeout=test_timeout)
             returncode = 0
+        except ADBTimeoutError:
+            raise
         except ADBProcessError as e:
             output = e.adb_process.stdout
             returncode = e.adb_process.exitcode
 
         self.log.process_output(basename, "\n%s" % output,
                                 command=[remote_bin])
         with mozfile.TemporaryDirectory() as tempdir:
             self.device.pull(self.remote_home_dir, tempdir)
--- a/testing/xpcshell/remotexpcshelltests.py
+++ b/testing/xpcshell/remotexpcshelltests.py
@@ -11,17 +11,17 @@ import os
 import posixpath
 import shutil
 import subprocess
 import sys
 import runxpcshelltests as xpcshell
 import tempfile
 from zipfile import ZipFile
 
-from mozdevice import ADBAndroid, ADBDevice
+from mozdevice import ADBAndroid, ADBDevice, ADBTimeoutError
 import mozfile
 import mozinfo
 from mozlog import commandline
 
 from xpcshellcommandline import parser_remote
 
 here = os.path.dirname(os.path.abspath(__file__))
 
@@ -151,16 +151,18 @@ class RemoteXPCShellTestThread(xpcshell.
         cmd.insert(1, self.remoteHere)
         cmd = ADBDevice._escape_command_line(cmd)
         try:
             # env is ignored here since the environment has already been
             # set for the command via the pushWrapper method.
             adb_process = self.device.shell(cmd, timeout=timeout+10, root=True)
             output_file = adb_process.stdout_file
             self.shellReturnCode = adb_process.exitcode
+        except ADBTimeoutError:
+            raise
         except Exception as e:
             if self.timedout:
                 # If the test timed out, there is a good chance the shell
                 # call also timed out and raised this Exception.
                 # Ignore this exception to simplify the error report.
                 self.shellReturnCode = None
             else:
                 raise e
@@ -209,16 +211,18 @@ class RemoteXPCShellTestThread(xpcshell.
         if self.shellReturnCode is not None:
             return self.shellReturnCode
         else:
             return -1
 
     def removeDir(self, dirname):
         try:
             self.device.rm(dirname, recursive=True, root=True)
+        except ADBTimeoutError:
+            raise
         except Exception as e:
             self.log.warning(str(e))
 
     # TODO: consider creating a separate log dir.  We don't have the test file structure,
     # so we use filename.log.  Would rather see ./logs/filename.log
     def createLogFile(self, test, stdout):
         try:
             f = None