Merge mozilla-central to autoland. a=merge CLOSED TREE
authorBogdan Tara <btara@mozilla.com>
Wed, 19 Dec 2018 06:53:54 +0200
changeset 451294 80bd4f3c0fcfb6080048f5d859142876c05d3eb1
parent 451293 973d54f232af0074ebb4fd1a6eab4822d43e80c1 (current diff)
parent 451263 204ab379fb829827a82efd9ef72bb18acdcf42f6 (diff)
child 451295 2aeb562c9c1c1a80ce53adf5adeb546e20a8da57
push id110646
push userebalazs@mozilla.com
push dateWed, 19 Dec 2018 16:05:18 +0000
treeherdermozilla-inbound@1acd86ad823c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone66.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 mozilla-central to autoland. a=merge CLOSED TREE
build/build-clang/aarch64-vastart-checking.patch
build/build-clang/r342649-hotpatch-8-byte-nops.patch
build/build-clang/r343123-pin-asan-dll.patch
build/build-clang/r346300-compiler-rt-windows-mmap.patch
deleted file mode 100644
--- a/build/build-clang/aarch64-vastart-checking.patch
+++ /dev/null
@@ -1,16 +0,0 @@
-AArch64 Windows uses a five-argument __va_start, just like ARM.
-
-https://bugs.llvm.org/show_bug.cgi?id=39090
-
-diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
-index 22483f8..53d9cb2 100644
---- a/clang/lib/Sema/SemaChecking.cpp
-+++ b/clang/lib/Sema/SemaChecking.cpp
-@@ -917,6 +917,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
-     switch (Context.getTargetInfo().getTriple().getArch()) {
-     case llvm::Triple::arm:
-     case llvm::Triple::thumb:
-+    case llvm::Triple::aarch64:
-       if (SemaBuiltinVAStartARMMicrosoft(TheCall))
-         return ExprError();
-       break;
--- a/build/build-clang/clang-win64.json
+++ b/build/build-clang/clang-win64.json
@@ -1,26 +1,22 @@
 {
-    "llvm_revision": "342383",
+    "llvm_revision": "348970",
     "stages": "3",
     "build_libcxx": false,
     "build_type": "Release",
     "assertions": false,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_700/final",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_700/final",
-    "lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_700/final",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_700/final",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_700/final",
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
+    "lld_repo": "https://llvm.org/svn/llvm-project/lld/trunk",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "python_path": "c:/mozilla-build/python/python.exe",
     "cc": "cl.exe",
     "cxx": "cl.exe",
     "ml": "ml64.exe",
     "patches": [
       "workaround-issue38586.patch",
-      "r342649-hotpatch-8-byte-nops.patch",
       "r342652-unpoison-thread-stacks.patch",
-      "r343123-pin-asan-dll.patch",
-      "aarch64-vastart-checking.patch",
       "downgrade-mangling-error.patch",
-      "r346300-compiler-rt-windows-mmap.patch",
       "loosen-msvc-detection.patch"
     ]
 }
deleted file mode 100644
--- a/build/build-clang/r342649-hotpatch-8-byte-nops.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-[winasan] Reduce hotpatch prefix check to 8 bytes
-
-Same idea as r310419: The 8 byte nop is a suffix of the 9 byte nop, and we need at most 6 bytes.
-
-Differential Revision: https://reviews.llvm.org/D51788
-
---- a/compiler-rt/lib/interception/interception_win.cc	(revision 342648)
-+++ b/compiler-rt/lib/interception/interception_win.cc	(revision 342649)
-@@ -223,8 +223,8 @@
-   return true;
- }
- 
--static const u8 kHintNop9Bytes[] = {
--  0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00
-+static const u8 kHintNop8Bytes[] = {
-+  0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00
- };
- 
- template<class T>
-@@ -239,8 +239,8 @@
- static bool FunctionHasPadding(uptr address, uptr size) {
-   if (IsMemoryPadding(address - size, size))
-     return true;
--  if (size <= sizeof(kHintNop9Bytes) &&
--      FunctionHasPrefix(address, kHintNop9Bytes))
-+  if (size <= sizeof(kHintNop8Bytes) &&
-+      FunctionHasPrefix(address, kHintNop8Bytes))
-     return true;
-   return false;
- }
--- a/build/build-clang/r342652-unpoison-thread-stacks.patch
+++ b/build/build-clang/r342652-unpoison-thread-stacks.patch
@@ -16,18 +16,18 @@ Differential Revision: https://reviews.l
 +  uptr stackSize = (uptr)tib->StackBase - (uptr)tib->StackLimit;
 +  __asan_unpoison_memory_region(tib->StackLimit, stackSize);
 +  return REAL(NtTerminateThread(rcx));
 +}
 +
  // }}}
  
  namespace __asan {
-@@ -161,7 +169,9 @@
- void InitializePlatformInterceptors() {
+@@ -169,7 +177,9 @@
+
    ASAN_INTERCEPT_FUNC(CreateThread);
    ASAN_INTERCEPT_FUNC(SetUnhandledExceptionFilter);
 -
 +  CHECK(::__interception::OverrideFunction("NtTerminateThread",
 +                                           (uptr)WRAP(NtTerminateThread),
 +                                           (uptr *)&REAL(NtTerminateThread)));
  #ifdef _WIN64
    ASAN_INTERCEPT_FUNC(__C_specific_handler);
deleted file mode 100644
--- a/build/build-clang/r343123-pin-asan-dll.patch
+++ /dev/null
@@ -1,27 +0,0 @@
-------------------------------------------------------------------------
-r343123 | dmajor | 2018-09-26 12:28:39 -0400 (Wed, 26 Sep 2018) | 5 lines
-
-[winasan] Pin the ASan DLL to prevent unloading
-
-Differential Revision: https://reviews.llvm.org/D52505
-
-===================================================================
---- a/compiler-rt/lib/asan/asan_win.cc	(revision 343122)
-+++ b/compiler-rt/lib/asan/asan_win.cc	(revision 343123)
-@@ -167,6 +167,14 @@
- namespace __asan {
- 
- void InitializePlatformInterceptors() {
-+  // The interceptors were not designed to be removable, so we have to keep this
-+  // module alive for the life of the process.
-+  HMODULE pinned;
-+  CHECK(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
-+                           GET_MODULE_HANDLE_EX_FLAG_PIN,
-+                           (LPCWSTR)&InitializePlatformInterceptors,
-+                           &pinned));
-+
-   ASAN_INTERCEPT_FUNC(CreateThread);
-   ASAN_INTERCEPT_FUNC(SetUnhandledExceptionFilter);
-   CHECK(::__interception::OverrideFunction("NtTerminateThread",
-
-------------------------------------------------------------------------
deleted file mode 100644
--- a/build/build-clang/r346300-compiler-rt-windows-mmap.patch
+++ /dev/null
@@ -1,149 +0,0 @@
-From 0b881f23c6f2a637ab97d71e964cc3743fef98b8 Mon Sep 17 00:00:00 2001
-From: Marco Castelluccio <mcastelluccio@mozilla.com>
-Date: Wed, 7 Nov 2018 09:38:26 +0000
-Subject: [PATCH] [GCOV] Close file mapping handle on Windows, so flushed gcda
- files can be removed while the process is in execution
-
-git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@346300 91177308-0d34-0410-b5e6-96231b3b80d8
----
- lib/profile/GCDAProfiling.c                   | 49 +++++++++++++++++++
- lib/profile/WindowsMMap.c                     |  8 ---
- lib/profile/WindowsMMap.h                     |  8 +++
- .../instrprof-gcov-__gcov_flush-multiple.c    | 16 ++++++
- ...nstrprof-gcov-__gcov_flush-multiple.c.gcov | 21 ++++++++
- .../instrprof-gcov-__gcov_flush-multiple.test | 10 ++++
- 6 files changed, 104 insertions(+), 8 deletions(-)
- create mode 100644 test/profile/Inputs/instrprof-gcov-__gcov_flush-multiple.c
- create mode 100644 test/profile/Inputs/instrprof-gcov-__gcov_flush-multiple.c.gcov
- create mode 100644 test/profile/instrprof-gcov-__gcov_flush-multiple.test
-
-diff --git a/compiler-rt/lib/profile/GCDAProfiling.c b/compiler-rt/lib/profile/GCDAProfiling.c
-index cbca36551..0665a680c 100644
---- a/compiler-rt/lib/profile/GCDAProfiling.c
-+++ b/compiler-rt/lib/profile/GCDAProfiling.c
-@@ -29,6 +29,8 @@
- #include <string.h>
- 
- #if defined(_WIN32)
-+#define WIN32_LEAN_AND_MEAN
-+#include <windows.h>
- #include "WindowsMMap.h"
- #else
- #include <sys/mman.h>
-@@ -86,6 +88,9 @@ static uint64_t cur_buffer_size = 0;
- static uint64_t cur_pos = 0;
- static uint64_t file_size = 0;
- static int new_file = 0;
-+#if defined(_WIN32)
-+static HANDLE mmap_handle = NULL;
-+#endif
- static int fd = -1;
- 
- typedef void (*fn_ptr)();
-@@ -255,6 +260,28 @@ static int map_file() {
-   if (file_size == 0)
-     return -1;
- 
-+#if defined(_WIN32)
-+  HANDLE mmap_fd;
-+  if (fd == -1)
-+    mmap_fd = INVALID_HANDLE_VALUE;
-+  else
-+    mmap_fd = (HANDLE)_get_osfhandle(fd);
-+
-+  mmap_handle = CreateFileMapping(mmap_fd, NULL, PAGE_READWRITE, DWORD_HI(file_size), DWORD_LO(file_size), NULL);
-+  if (mmap_handle == NULL) {
-+    fprintf(stderr, "profiling: %s: cannot create file mapping: %d\n", filename,
-+            GetLastError());
-+    return -1;
-+  }
-+
-+  write_buffer = MapViewOfFile(mmap_handle, FILE_MAP_WRITE, 0, 0, file_size);
-+  if (write_buffer == NULL) {
-+    fprintf(stderr, "profiling: %s: cannot map: %d\n", filename,
-+            GetLastError());
-+    CloseHandle(mmap_handle);
-+    return -1;
-+  }
-+#else
-   write_buffer = mmap(0, file_size, PROT_READ | PROT_WRITE,
-                       MAP_FILE | MAP_SHARED, fd, 0);
-   if (write_buffer == (void *)-1) {
-@@ -263,10 +290,30 @@ static int map_file() {
-             strerror(errnum));
-     return -1;
-   }
-+#endif
-+
-   return 0;
- }
- 
- static void unmap_file() {
-+#if defined(_WIN32)
-+  if (!FlushViewOfFile(write_buffer, file_size)) {
-+    fprintf(stderr, "profiling: %s: cannot flush mapped view: %d\n", filename,
-+            GetLastError());
-+  }
-+
-+  if (!UnmapViewOfFile(write_buffer)) {
-+    fprintf(stderr, "profiling: %s: cannot unmap mapped view: %d\n", filename,
-+            GetLastError());
-+  }
-+
-+  if (!CloseHandle(mmap_handle)) {
-+    fprintf(stderr, "profiling: %s: cannot close file mapping handle: %d\n", filename,
-+            GetLastError());
-+  }
-+
-+  mmap_handle = NULL;
-+#else
-   if (msync(write_buffer, file_size, MS_SYNC) == -1) {
-     int errnum = errno;
-     fprintf(stderr, "profiling: %s: cannot msync: %s\n", filename,
-@@ -277,6 +324,8 @@ static void unmap_file() {
-    * is written and we don't care.
-    */
-   (void)munmap(write_buffer, file_size);
-+#endif
-+
-   write_buffer = NULL;
-   file_size = 0;
- }
-diff --git a/compiler-rt/lib/profile/WindowsMMap.c b/compiler-rt/lib/profile/WindowsMMap.c
-index dc87a888a..41cc67f41 100644
---- a/compiler-rt/lib/profile/WindowsMMap.c
-+++ b/compiler-rt/lib/profile/WindowsMMap.c
-@@ -24,14 +24,6 @@
- 
- #include "InstrProfiling.h"
- 
--#ifdef __USE_FILE_OFFSET64
--# define DWORD_HI(x) (x >> 32)
--# define DWORD_LO(x) ((x) & 0xffffffff)
--#else
--# define DWORD_HI(x) (0)
--# define DWORD_LO(x) (x)
--#endif
--
- COMPILER_RT_VISIBILITY
- void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
- {
-diff --git a/compiler-rt/lib/profile/WindowsMMap.h b/compiler-rt/lib/profile/WindowsMMap.h
-index ac2c911c8..51a130b31 100644
---- a/compiler-rt/lib/profile/WindowsMMap.h
-+++ b/compiler-rt/lib/profile/WindowsMMap.h
-@@ -45,6 +45,14 @@
- #define   LOCK_NB   4    /* don't block when locking */
- #define   LOCK_UN   8    /* unlock */
- 
-+#ifdef __USE_FILE_OFFSET64
-+# define DWORD_HI(x) (x >> 32)
-+# define DWORD_LO(x) ((x) & 0xffffffff)
-+#else
-+# define DWORD_HI(x) (0)
-+# define DWORD_LO(x) (x)
-+#endif
-+
- void *mmap(void *start, size_t length, int prot, int flags, int fd,
-            off_t offset);
- 
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 108
+Version 109
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-107...release-108
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-108...release-109
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.2
 - babel-preset-react @6.24.1
 - react @16.4.1
 - react-dom @16.4.1
 - webpack @3.12.0
--- a/devtools/client/debugger/new/dist/debugger.css
+++ b/devtools/client/debugger/new/dist/debugger.css
@@ -1933,16 +1933,20 @@ html .toggle-button.end.vertical svg {
 }
 
 /* Removes start margin when a custom root is used */
 .sources-list-custom-root
   .tree
   > .tree-node[data-expandable="false"][aria-level="0"] {
   padding-inline-start: 4px;
 }
+
+.sources-list .tree-node[data-expandable="false"] .tree-indent:last-of-type {
+  margin-inline-end: 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/. */
 
 menu {
   display: inline;
   padding: 0;
 }
@@ -2047,16 +2051,20 @@ menuseparator {
 .outline-filter-input.focused {
   border: 1px solid var(--theme-highlight-blue);
 }
 
 .outline-filter-input::placeholder {
   font-style: italic;
   color: var(--theme-comment);
 }
+
+.theme-dark .outline-filter-input.focused {
+  border: 1px solid var(--blue-50);
+}
 /* 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/>. */
 
 .outline {
   overflow-y: hidden;
 }
 
@@ -2155,16 +2163,21 @@ menuseparator {
 .theme-dark .outline-footer button {
   color: var(--theme-body-color);
 }
 
 .outline-footer button.active {
   background: var(--theme-highlight-blue);
   color: #ffffff;
 }
+
+.theme-dark .outline-footer button.active {
+  background: var(--theme-selection-background);
+  color: #ffffff;
+}
 /* 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/>. */
 
 .function-signature {
   align-self: center;
 }
 
@@ -2826,22 +2839,22 @@ html[dir="rtl"] .editor-mount {
 .theme-dark {
   --gutter-hover-background-color: #414141;
   --breakpoint-fill: var(--blue-55);
   --breakpoint-stroke: var(--blue-40);
   --breakpoint-fill-disabled: var(--blue-50);
   --breakpoint-stroke-disabled: var(--blue-60);
 }
 
-.empty-line .CodeMirror-linenumber {
-  opacity: 0.5;
-}
-
-.theme-dark .empty-line .CodeMirror-linenumber {
-  opacity: 0.6;
+.theme-light .cm-s-mozilla .empty-line .CodeMirror-linenumber {
+  color: var(--grey-40);
+}
+
+.theme-dark .cm-s-mozilla .empty-line .CodeMirror-linenumber {
+  color: var(--grey-60);
 }
 
 :not(.empty-line):not(.new-breakpoint)
   > .CodeMirror-gutter-wrapper:hover
   > .CodeMirror-linenumber {
   height: 13px;
   color: var(--theme-body-color);
   /* Add 1px offset to the background to match it
--- a/devtools/client/debugger/new/dist/parser-worker.js
+++ b/devtools/client/debugger/new/dist/parser-worker.js
@@ -1336,22 +1336,27 @@ function getFunctionParameterNames(path)
     });
   }
   return [];
 }
 
 /* eslint-disable complexity */
 function extractSymbol(path, symbols) {
   if ((0, _helpers.isFunction)(path)) {
+    const name = (0, _getFunctionName2.default)(path.node, path.parent);
     symbols.functions.push({
-      name: (0, _getFunctionName2.default)(path.node, path.parent),
+      name,
       klass: (0, _inferClassName.inferClassName)(path),
       location: path.node.loc,
       parameterNames: getFunctionParameterNames(path),
-      identifier: path.node.id
+      identifier: path.node.id,
+      // indicates the occurence of the function in a file
+      // e.g { name: foo, ... index: 4 } is the 4th foo function
+      // in the file
+      index: symbols.functions.filter(f => f.name === name).length
     });
   }
 
   if (t.isJSXElement(path)) {
     symbols.hasJsx = true;
   }
 
   if (t.isGenericTypeAnnotation(path)) {
--- a/devtools/client/debugger/new/dist/search-worker.js
+++ b/devtools/client/debugger/new/dist/search-worker.js
@@ -630,18 +630,17 @@ function findSourceMatches(source, query
     const indices = [];
 
     while (result = query.exec(_text)) {
       indices.push({
         sourceId: id,
         line: line + 1,
         column: result.index,
         match: result[0],
-        value: _text,
-        text: result.input
+        value: _text
       });
     }
     return indices;
   }).filter(_matches => _matches.length > 0);
 
   return [].concat(...matches);
 }
 
--- a/devtools/client/debugger/new/panel.js
+++ b/devtools/client/debugger/new/panel.js
@@ -30,17 +30,32 @@ DebuggerPanel.prototype = {
     } = await this.panelWin.Debugger.bootstrap({
       threadClient: this.toolbox.threadClient,
       tabTarget: this.toolbox.target,
       debuggerClient: this.toolbox.target.client,
       sourceMaps: this.toolbox.sourceMapService,
       toolboxActions: {
         // Open a link in a new browser tab.
         openLink: this.openLink.bind(this),
-        openWorkerToolbox: this.openWorkerToolbox.bind(this)
+        openWorkerToolbox: this.openWorkerToolbox.bind(this),
+        openElementInInspector: async function(grip) {
+          await this.toolbox.initInspector();
+          const onSelectInspector = this.toolbox.selectTool("inspector");
+          const onGripNodeToFront = this.toolbox.walker.gripToNodeFront(grip);
+          const [
+            front,
+            inspector,
+          ] = await Promise.all([onGripNodeToFront, onSelectInspector]);
+
+          const onInspectorUpdated = inspector.once("inspector-updated");
+          const onNodeFrontSet = this.toolbox.selection
+            .setNodeFront(front, { reason: "debugger" });
+
+          return Promise.all([onNodeFrontSet, onInspectorUpdated]);
+        }.bind(this)
       }
     });
 
     this._actions = actions;
     this._store = store;
     this._selectors = selectors;
     this._client = client;
     this.isReady = true;
--- a/devtools/client/debugger/new/src/actions/breakpoints/index.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/index.js
@@ -151,21 +151,20 @@ export function toggleAllBreakpoints(sho
  * @memberof actions/breakpoints
  * @static
  */
 export function toggleBreakpoints(
   shouldDisableBreakpoints: boolean,
   breakpoints: Breakpoint[]
 ) {
   return async ({ dispatch }: ThunkArgs) => {
-    const promises = breakpoints.map(
-      breakpoint =>
-        shouldDisableBreakpoints
-          ? dispatch(disableBreakpoint(breakpoint.location))
-          : dispatch(enableBreakpoint(breakpoint.location))
+    const promises = breakpoints.map(breakpoint =>
+      shouldDisableBreakpoints
+        ? dispatch(disableBreakpoint(breakpoint.location))
+        : dispatch(enableBreakpoint(breakpoint.location))
     );
 
     await Promise.all(promises);
   };
 }
 
 /**
  * Removes all breakpoints
@@ -373,17 +372,17 @@ export function toggleDisabledBreakpoint
     }
     return dispatch(enableBreakpoint(bp.location));
   };
 }
 
 export function enableXHRBreakpoint(index: number, bp: XHRBreakpoint) {
   return ({ dispatch, getState, client }: ThunkArgs) => {
     const xhrBreakpoints = getXHRBreakpoints(getState());
-    const breakpoint = bp || xhrBreakpoints.get(index);
+    const breakpoint = bp || xhrBreakpoints[index];
     const enabledBreakpoint = {
       ...breakpoint,
       disabled: false
     };
 
     return dispatch({
       type: "ENABLE_XHR_BREAKPOINT",
       breakpoint: enabledBreakpoint,
@@ -391,17 +390,17 @@ export function enableXHRBreakpoint(inde
       [PROMISE]: client.setXHRBreakpoint(breakpoint.path, breakpoint.method)
     });
   };
 }
 
 export function disableXHRBreakpoint(index: number, bp: XHRBreakpoint) {
   return ({ dispatch, getState, client }: ThunkArgs) => {
     const xhrBreakpoints = getXHRBreakpoints(getState());
-    const breakpoint = bp || xhrBreakpoints.get(index);
+    const breakpoint = bp || xhrBreakpoints[index];
     const disabledBreakpoint = {
       ...breakpoint,
       disabled: true
     };
 
     return dispatch({
       type: "DISABLE_XHR_BREAKPOINT",
       breakpoint: disabledBreakpoint,
@@ -413,17 +412,17 @@ export function disableXHRBreakpoint(ind
 
 export function updateXHRBreakpoint(
   index: number,
   path: string,
   method: string
 ) {
   return ({ dispatch, getState, client }: ThunkArgs) => {
     const xhrBreakpoints = getXHRBreakpoints(getState());
-    const breakpoint = xhrBreakpoints.get(index);
+    const breakpoint = xhrBreakpoints[index];
 
     const updatedBreakpoint = {
       ...breakpoint,
       path,
       method,
       text: `URL contains "${path}"`
     };
 
@@ -441,17 +440,17 @@ export function updateXHRBreakpoint(
 export function togglePauseOnAny() {
   return ({ dispatch, getState }: ThunkArgs) => {
     const xhrBreakpoints = getXHRBreakpoints(getState());
     const index = xhrBreakpoints.findIndex(({ path }) => path.length === 0);
     if (index < 0) {
       return dispatch(setXHRBreakpoint("", "ANY"));
     }
 
-    const bp = xhrBreakpoints.get(index);
+    const bp = xhrBreakpoints[index];
     if (bp.disabled) {
       return dispatch(enableXHRBreakpoint(index, bp));
     }
 
     return dispatch(disableXHRBreakpoint(index, bp));
   };
 }
 
@@ -465,17 +464,17 @@ export function setXHRBreakpoint(path: s
       [PROMISE]: client.setXHRBreakpoint(path, method)
     });
   };
 }
 
 export function removeXHRBreakpoint(index: number) {
   return ({ dispatch, getState, client }: ThunkArgs) => {
     const xhrBreakpoints = getXHRBreakpoints(getState());
-    const breakpoint = xhrBreakpoints.get(index);
+    const breakpoint = xhrBreakpoints[index];
     return dispatch({
       type: "REMOVE_XHR_BREAKPOINT",
       breakpoint,
       index,
       [PROMISE]: client.removeXHRBreakpoint(breakpoint.path, breakpoint.method)
     });
   };
 }
--- a/devtools/client/debugger/new/src/actions/breakpoints/syncBreakpoint.js
+++ b/devtools/client/debugger/new/src/actions/breakpoints/syncBreakpoint.js
@@ -26,21 +26,21 @@ import type {
 } from "../../types";
 
 type BreakpointSyncData = {
   previousLocation: SourceLocation,
   breakpoint: ?Breakpoint
 };
 
 async function makeScopedLocation(
-  { name, offset }: ASTLocation,
+  { name, offset, index }: ASTLocation,
   location: SourceLocation,
   source
 ) {
-  const scope = await findScopeByName(source, name);
+  const scope = await findScopeByName(source, name, index);
   // fallback onto the location line, if the scope is not found
   // note: we may at some point want to delete the breakpoint if the scope
   // disappears
   const line = scope ? scope.location.start.line + offset.line : location.line;
   return {
     line,
     column: location.column,
     sourceUrl: source.url,
--- a/devtools/client/debugger/new/src/actions/pause/continueToHere.js
+++ b/devtools/client/debugger/new/src/actions/pause/continueToHere.js
@@ -9,17 +9,17 @@ import {
   getSelectedFrame,
   getCanRewind
 } from "../../selectors";
 import { addHiddenBreakpoint } from "../breakpoints";
 import { resume, rewind } from "./commands";
 
 import type { ThunkArgs } from "../types";
 
-export function continueToHere(line: number) {
+export function continueToHere(line: number, column?: number) {
   return async function({ dispatch, getState }: ThunkArgs) {
     const selectedSource = getSelectedSource(getState());
     const selectedFrame = getSelectedFrame(getState());
 
     if (!selectedFrame || !selectedSource) {
       return;
     }
 
@@ -29,16 +29,16 @@ export function continueToHere(line: num
     }
 
     const action =
       getCanRewind(getState()) && line < debugLine ? rewind : resume;
 
     await dispatch(
       addHiddenBreakpoint({
         line,
-        column: undefined,
+        column: column,
         sourceId: selectedSource.id
       })
     );
 
     dispatch(action());
   };
 }
--- a/devtools/client/debugger/new/src/actions/pause/mapFrames.js
+++ b/devtools/client/debugger/new/src/actions/pause/mapFrames.js
@@ -22,21 +22,22 @@ import { features } from "../../utils/pr
 import { isGeneratedId } from "devtools-source-map";
 
 function getSelectedFrameId(state, frames) {
   if (!features.originalBlackbox) {
     const selectedFrame = getSelectedFrame(state);
     return selectedFrame && selectedFrame.id;
   }
 
-  const selectedFrame =  frames.find(frame =>
-    !getSource(state, frame.location.sourceId).isBlackBoxed
-  )
+  const selectedFrame = frames.find(frame => {
+    const source = getSource(state, frame.location.sourceId);
+    return source && !source.isBlackBoxed;
+  });
 
-  return selectedFrame && selectedFrame.id
+  return selectedFrame && selectedFrame.id;
 }
 
 export function updateFrameLocation(frame: Frame, sourceMaps: any) {
   if (frame.isOriginal) {
     return Promise.resolve(frame);
   }
   return sourceMaps.getOriginalLocation(frame.location).then(loc => ({
     ...frame,
@@ -164,16 +165,16 @@ export function mapFrames() {
     if (!frames) {
       return;
     }
 
     let mappedFrames = await updateFrameLocations(frames, sourceMaps);
     mappedFrames = await expandFrames(mappedFrames, sourceMaps, getState);
     mappedFrames = mapDisplayNames(mappedFrames, getState);
 
-    const selectedFrameId = getSelectedFrameId(getState(), mappedFrames)
+    const selectedFrameId = getSelectedFrameId(getState(), mappedFrames);
     dispatch({
       type: "MAP_FRAMES",
       frames: mappedFrames,
       selectedFrameId
     });
   };
 }
--- a/devtools/client/debugger/new/src/actions/sources/blackbox.js
+++ b/devtools/client/debugger/new/src/actions/sources/blackbox.js
@@ -22,19 +22,19 @@ export function toggleBlackBox(source: S
     const { isBlackBoxed, id } = source;
 
     if (!isBlackBoxed) {
       recordEvent("blackbox");
     }
 
     let promise;
     if (features.originalBlackbox && isOriginalId(id)) {
-      promise = Promise.resolve({isBlackBoxed: !isBlackBoxed})
+      promise = Promise.resolve({ isBlackBoxed: !isBlackBoxed });
     } else {
-      promise = client.blackBox(id, isBlackBoxed)
+      promise = client.blackBox(id, isBlackBoxed);
     }
 
     return dispatch({
       type: "BLACKBOX",
       source,
       [PROMISE]: promise
     });
   };
--- a/devtools/client/debugger/new/src/actions/toolbox.js
+++ b/devtools/client/debugger/new/src/actions/toolbox.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 const { isDevelopment } = require("devtools-environment");
 const { getSelectedFrameId } = require("../selectors");
 
 import type { ThunkArgs } from "./types";
-import type { Worker } from "../types";
+import type { Worker, Grip } from "../types";
 
 /**
  * @memberof actions/toolbox
  * @static
  */
 export function openLink(url: string) {
   return async function({ openLink: openLinkCommand }: ThunkArgs) {
     if (isDevelopment()) {
@@ -42,8 +42,18 @@ export function evaluateInConsole(inputS
   return async ({ client, getState }: ThunkArgs) => {
     const frameId = getSelectedFrameId(getState());
     client.evaluate(
       `console.log("${inputString}"); console.log(${inputString})`,
       { frameId }
     );
   };
 }
+
+export function openElementInInspectorCommand(grip: Grip) {
+  return async ({ openElementInInspector }: ThunkArgs) => {
+    if (isDevelopment()) {
+      alert(`Opening node in Inspector: ${grip.class}`);
+    } else {
+      return openElementInInspector(grip);
+    }
+  };
+}
--- a/devtools/client/debugger/new/src/components/App.js
+++ b/devtools/client/debugger/new/src/components/App.js
@@ -63,17 +63,18 @@ type Props = {
   endPanelCollapsed: boolean,
   activeSearch: string,
   quickOpenEnabled: boolean,
   setActiveSearch: string => void,
   closeActiveSearch: () => void,
   closeProjectSearch: () => void,
   openQuickOpen: (query?: string) => void,
   closeQuickOpen: () => void,
-  setOrientation: OrientationType => void
+  setOrientation: OrientationType => void,
+  canRewind: boolean
 };
 
 type State = {
   shortcutsModalEnabled: boolean,
   startPanelSize: number,
   endPanelSize: number
 };
 
--- a/devtools/client/debugger/new/src/components/Editor/Breakpoints.js
+++ b/devtools/client/debugger/new/src/components/Editor/Breakpoints.js
@@ -1,19 +1,18 @@
 /* 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/>. */
 
 // @flow
 import { connect } from "react-redux";
 import React, { Component } from "react";
-
 import Breakpoint from "./Breakpoint";
 
-import { getSelectedSource, getVisibleBreakpoints } from "../../selectors";
+import { getSelectedSource, getFirstVisibleBreakpoints } from "../../selectors";
 import { makeLocationId } from "../../utils/breakpoint";
 import { isLoaded } from "../../utils/source";
 
 import type { Breakpoint as BreakpointType, Source } from "../../types";
 
 type Props = {
   selectedSource: Source,
   breakpoints: BreakpointType[],
@@ -49,11 +48,13 @@ class Breakpoints extends Component<Prop
           );
         })}
       </div>
     );
   }
 }
 
 export default connect(state => ({
-  breakpoints: getVisibleBreakpoints(state),
+  // Retrieves only the first breakpoint per line so that the
+  // breakpoint marker represents only the first breakpoint
+  breakpoints: getFirstVisibleBreakpoints(state),
   selectedSource: getSelectedSource(state)
 }))(Breakpoints);
--- a/devtools/client/debugger/new/src/components/Editor/Footer.js
+++ b/devtools/client/debugger/new/src/components/Editor/Footer.js
@@ -210,17 +210,26 @@ class SourceFooter extends PureComponent
   renderCursorPosition() {
     const { cursorPosition } = this.state;
 
     const text = L10N.getFormatStr(
       "sourceFooter.currentCursorPosition",
       cursorPosition.line + 1,
       cursorPosition.column + 1
     );
-    return <span className="cursor-position">{text}</span>;
+    const title = L10N.getFormatStr(
+      "sourceFooter.currentCursorPosition.tooltip",
+      cursorPosition.line + 1,
+      cursorPosition.column + 1
+    );
+    return (
+      <span className="cursor-position" title={title}>
+        {text}
+      </span>
+    );
   }
 
   render() {
     const { selectedSource, horizontal } = this.props;
 
     if (!shouldShowFooter(selectedSource, horizontal)) {
       return null;
     }
--- a/devtools/client/debugger/new/src/components/Editor/GutterMenu.js
+++ b/devtools/client/debugger/new/src/components/Editor/GutterMenu.js
@@ -20,16 +20,17 @@ import actions from "../../actions";
 type Props = {
   setContextMenu: Function,
   contextMenu: Object
 };
 
 export function gutterMenu({
   breakpoint,
   line,
+  column,
   event,
   isPaused,
   toggleBreakpoint,
   openConditionalPanel,
   toggleDisabledBreakpoint,
   isCbPanelOpen,
   closeConditionalPanel,
   continueToHere,
@@ -68,56 +69,56 @@ export function gutterMenu({
       label: L10N.getStr("editor.continueToHere.label")
     }
   };
 
   const toggleBreakpointItem = {
     accesskey: L10N.getStr("shortcuts.toggleBreakpoint.accesskey"),
     disabled: false,
     click: () => {
-      toggleBreakpoint(line);
+      toggleBreakpoint(line, column);
       if (isCbPanelOpen) {
         closeConditionalPanel();
       }
     },
     accelerator: L10N.getStr("toggleBreakpoint.key"),
     ...(breakpoint ? gutterItems.removeBreakpoint : gutterItems.addBreakpoint)
   };
 
   const conditionalBreakpoint = {
     accesskey: L10N.getStr("editor.addConditionalBreakpoint.accesskey"),
     disabled: false,
     // Leaving column undefined so pause points can be detected
     click: () =>
       openConditionalPanel(
-        breakpoint ? breakpoint.location : { line, sourceId }
+        breakpoint ? breakpoint.location : { line, column, sourceId }
       ),
     accelerator: L10N.getStr("toggleCondPanel.key"),
     ...(breakpoint && breakpoint.condition
       ? gutterItems.editConditional
       : gutterItems.addConditional)
   };
 
   const items = [toggleBreakpointItem, conditionalBreakpoint];
 
   if (isPaused) {
     const continueToHereItem = {
       accesskey: L10N.getStr("editor.continueToHere.accesskey"),
       disabled: false,
-      click: () => continueToHere(line),
+      click: () => continueToHere(line, column),
       ...gutterItems.continueToHere
     };
     items.push(continueToHereItem);
   }
 
   if (breakpoint) {
     const disableBreakpoint = {
       accesskey: L10N.getStr("editor.disableBreakpoint.accesskey"),
       disabled: false,
-      click: () => toggleDisabledBreakpoint(line),
+      click: () => toggleDisabledBreakpoint(line, column),
       ...(breakpoint.disabled
         ? gutterItems.enableBreakpoint
         : gutterItems.disableBreakpoint)
     };
     items.push(disableBreakpoint);
   }
 
   showMenu(event, items);
@@ -134,27 +135,32 @@ class GutterContextMenuComponent extends
     // clear the context menu since it is open
     this.props.setContextMenu("", null);
     return this.showMenu(nextProps);
   }
 
   showMenu(nextProps) {
     const { contextMenu, ...props } = nextProps;
     const { event } = contextMenu;
+
     const sourceId = props.selectedSource ? props.selectedSource.id : "";
     const line = lineAtHeight(props.editor, sourceId, event);
+    const column = props.editor.codeMirror.coordsChar({
+      left: event.x,
+      top: event.y
+    }).ch;
     const breakpoint = nextProps.breakpoints.find(
-      bp => bp.location.line === line
+      bp => bp.location.line === line && bp.location.column === column
     );
 
     if (props.emptyLines && props.emptyLines.includes(line)) {
       return;
     }
 
-    gutterMenu({ event, sourceId, line, breakpoint, ...props });
+    gutterMenu({ event, sourceId, line, column, breakpoint, ...props });
   }
 
   render() {
     return null;
   }
 }
 
 const mapStateToProps = state => {
--- a/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js
+++ b/devtools/client/debugger/new/src/components/Editor/Preview/Popup.js
@@ -29,32 +29,34 @@ import { isReactComponent, isImmutablePr
 
 import Svg from "../../shared/Svg";
 import { createObjectClient } from "../../../client/firefox";
 
 import "./Popup.css";
 
 import type { EditorRange } from "../../../utils/editor/types";
 import type { Coords } from "../../shared/Popover";
+import type { Grip } from "../../../types";
 
 type PopupValue = Object | null;
 type Props = {
   setPopupObjectProperties: (Object, Object) => void,
   addExpression: (string, ?Object) => void,
   popupObjectProperties: Object,
   popoverPos: Object,
   value: PopupValue,
   expression: string,
   onClose: () => void,
   range: EditorRange,
   editor: any,
   editorRef: ?HTMLDivElement,
   selectSourceURL: (string, Object) => void,
   openLink: string => void,
-  extra: Object
+  extra: Object,
+  openElementInInspector: (grip: Grip) => void
 };
 
 type State = {
   top: number
 };
 
 function inPreview(event) {
   const relatedTarget: Element = (event.relatedTarget: any);
@@ -263,26 +265,28 @@ export class Popup extends Component<Pro
           mode: MODE.LONG,
           openLink
         })}
       </div>
     );
   }
 
   renderObjectInspector(roots: Array<Object>) {
-    const { openLink } = this.props;
+    const { openLink, openElementInInspector } = this.props;
 
     return (
       <ObjectInspector
         roots={roots}
         autoExpandDepth={0}
         disableWrap={true}
         focusable={false}
         openLink={openLink}
         createObjectClient={grip => createObjectClient(grip)}
+        onDOMNodeClick={grip => openElementInInspector(grip)}
+        onInspectIconClick={grip => openElementInInspector(grip)}
       />
     );
   }
 
   renderPreview() {
     // We don't have to check and
     // return on `false`, `""`, `0`, `undefined` etc,
     // these falsy simple typed value because we want to
@@ -339,17 +343,18 @@ export class Popup extends Component<Pro
       >
         {this.renderPreview()}
       </Popover>
     );
   }
 }
 
 const mapStateToProps = state => ({
-  popupObjectProperties: getAllPopupObjectProperties(state)
+  popupObjectProperties: getAllPopupObjectProperties(state),
+  openElementInInspector: actions.openElementInInspectorCommand
 });
 
 const {
   addExpression,
   selectSourceURL,
   selectLocation,
   setPopupObjectProperties,
   openLink
--- a/devtools/client/debugger/new/src/components/Editor/SearchBar.js
+++ b/devtools/client/debugger/new/src/components/Editor/SearchBar.js
@@ -286,16 +286,17 @@ class SearchBar extends Component<Props,
         >
           <Svg name={svgName} />
         </button>
       );
     }
 
     return (
       <div className="search-modifiers">
+        <span className="pipe-divider" />
         <span className="search-type-name">
           {L10N.getStr("symbolSearch.searchModifier.modifiersLabel")}
         </span>
         <SearchModBtn
           modVal="regexMatch"
           className="regex-match-btn"
           svgName="regex-match"
           tooltip={L10N.getStr("symbolSearch.searchModifier.regex")}
--- a/devtools/client/debugger/new/src/components/Editor/Tab.js
+++ b/devtools/client/debugger/new/src/components/Editor/Tab.js
@@ -13,23 +13,24 @@ import SourceIcon from "../shared/Source
 import { CloseButton } from "../shared/Button";
 
 import type { List } from "immutable";
 import type { Source } from "../../types";
 
 import actions from "../../actions";
 
 import {
+  getDisplayPath,
   getFileURL,
   getRawSourceURL,
+  getSourceQueryString,
   getTruncatedFileName,
-  getDisplayPath,
-  isPretty,
-  getSourceQueryString
+  isPretty
 } from "../../utils/source";
+import { shouldShowPrettyPrint } from "../../utils/editor";
 import { copyToTheClipboard } from "../../utils/clipboard";
 import { getTabMenuItems } from "../../utils/tabs";
 
 import {
   getSelectedSource,
   getActiveSearch,
   getSourcesForTabs,
   getHasSiblingOfSameName
@@ -60,53 +61,53 @@ class Tab extends PureComponent<Props> {
 
   showContextMenu(e, tab: string) {
     const {
       closeTab,
       closeTabs,
       tabSources,
       showSource,
       togglePrettyPrint,
-      selectedSource
+      selectedSource,
+      source
     } = this.props;
 
     const tabCount = tabSources.length;
     const otherTabs = tabSources.filter(t => t.id !== tab);
     const sourceTab = tabSources.find(t => t.id == tab);
     const tabURLs = tabSources.map(t => t.url);
     const otherTabURLs = otherTabs.map(t => t.url);
 
     if (!sourceTab) {
       return;
     }
 
-    const isPrettySource = isPretty(sourceTab);
     const tabMenuItems = getTabMenuItems();
     const items = [
       {
         item: {
           ...tabMenuItems.closeTab,
           click: () => closeTab(sourceTab)
         }
       },
       {
         item: {
           ...tabMenuItems.closeOtherTabs,
           click: () => closeTabs(otherTabURLs),
-          disabled: () => tabCount === 1
+          disabled: otherTabURLs.length === 0
         }
       },
       {
         item: {
           ...tabMenuItems.closeTabsToEnd,
           click: () => {
             const tabIndex = tabSources.findIndex(t => t.id == tab);
             closeTabs(tabURLs.filter((t, i) => i > tabIndex));
           },
-          disabled: () =>
+          disabled:
             tabCount === 1 ||
             tabSources.some((t, i) => t === tab && tabCount - 1 === i)
         }
       },
       {
         item: { ...tabMenuItems.closeAllTabs, click: () => closeTabs(tabURLs) }
       },
       { item: { type: "separator" } },
@@ -128,24 +129,23 @@ class Tab extends PureComponent<Props> {
         item: {
           ...tabMenuItems.showSource,
           disabled: !selectedSource.url,
           click: () => showSource(tab)
         }
       }
     ];
 
-    if (!isPrettySource) {
-      items.push({
-        item: {
-          ...tabMenuItems.prettyPrint,
-          click: () => togglePrettyPrint(tab)
-        }
-      });
-    }
+    items.push({
+      item: {
+        ...tabMenuItems.prettyPrint,
+        click: () => togglePrettyPrint(tab),
+        disabled: !shouldShowPrettyPrint(source)
+      }
+    });
 
     showMenu(e, buildMenu(items));
   }
 
   isProjectSearchEnabled() {
     return this.props.activeSearch === "project";
   }
 
--- a/devtools/client/debugger/new/src/components/Editor/index.js
+++ b/devtools/client/debugger/new/src/components/Editor/index.js
@@ -345,17 +345,21 @@ class Editor extends PureComponent<Props
   };
 
   openMenu(event: MouseEvent) {
     event.stopPropagation();
     event.preventDefault();
 
     const { setContextMenu } = this.props;
     const target: Element = (event.target: any);
-    if (target.classList.contains("CodeMirror-linenumber")) {
+
+    if (
+      target.classList.contains("CodeMirror-linenumber") ||
+      target.getAttribute("id") === "columnmarker"
+    ) {
       return setContextMenu("Gutter", event);
     }
 
     return setContextMenu("Editor", event);
   }
 
   onGutterClick = (
     cm: Object,
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/Outline.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/Outline.js
@@ -71,19 +71,22 @@ export class Outline extends Component<P
     this.state = { filter: "" };
   }
 
   selectItem(location: AstLocation) {
     const { selectedSource, selectLocation } = this.props;
     if (!selectedSource) {
       return;
     }
-    const selectedSourceId = selectedSource.id;
-    const startLine = location.start.line;
-    selectLocation({ sourceId: selectedSourceId, line: startLine });
+
+    selectLocation({
+      sourceId: selectedSource.id,
+      line: location.start.line,
+      column: location.start.column
+    });
   }
 
   onContextMenu(event: SyntheticEvent<HTMLElement>, func: SymbolDeclaration) {
     event.stopPropagation();
     event.preventDefault();
 
     const {
       selectedSource,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/index.js
@@ -10,17 +10,20 @@ import { connect } from "react-redux";
 
 import ExceptionOption from "./ExceptionOption";
 
 import Breakpoint from "./Breakpoint";
 import BreakpointHeading from "./BreakpointHeading";
 
 import actions from "../../../actions";
 import { getDisplayPath } from "../../../utils/source";
-import { makeLocationId, sortBreakpoints } from "../../../utils/breakpoint";
+import {
+  makeLocationId,
+  sortFormattedBreakpoints
+} from "../../../utils/breakpoint";
 
 import { getSelectedSource, getBreakpointSources } from "../../../selectors";
 
 import type { Source } from "../../../types";
 import type { BreakpointSources } from "../../../selectors/breakpointSources";
 
 import "./Breakpoints.css";
 
@@ -74,17 +77,17 @@ class Breakpoints extends Component<Prop
     const { breakpointSources } = this.props;
     const sources = [
       ...breakpointSources.map(({ source, breakpoints }) => source)
     ];
 
     return [
       ...breakpointSources.map(({ source, breakpoints, i }) => {
         const path = getDisplayPath(source, sources);
-        const sortedBreakpoints = sortBreakpoints(breakpoints);
+        const sortedBreakpoints = sortFormattedBreakpoints(breakpoints);
 
         return [
           <BreakpointHeading
             source={source}
             sources={sources}
             path={path}
             key={source.url}
           />,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Expressions.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Expressions.js
@@ -17,17 +17,17 @@ import {
 } from "../../selectors";
 import { getValue } from "../../utils/expressions";
 import { createObjectClient } from "../../client/firefox";
 
 import { CloseButton } from "../shared/Button";
 import { debounce } from "lodash";
 
 import type { List } from "immutable";
-import type { Expression } from "../../types";
+import type { Expression, Grip } from "../../types";
 
 import "./Expressions.css";
 
 const { ObjectInspector } = objectInspector;
 
 type State = {
   editing: boolean,
   editIndex: number,
@@ -43,17 +43,18 @@ type Props = {
   autocomplete: (input: string, cursor: number) => Promise<any>,
   clearAutocomplete: () => void,
   onExpressionAdded: () => void,
   addExpression: (input: string) => void,
   clearExpressionError: () => void,
   evaluateExpressions: () => void,
   updateExpression: (input: string, expression: Expression) => void,
   deleteExpression: (expression: Expression) => void,
-  openLink: (url: string) => void
+  openLink: (url: string) => void,
+  openElementInInspector: (grip: Grip) => void
 };
 
 class Expressions extends Component<Props, State> {
   _input: ?HTMLInputElement;
   renderExpression: (
     expression: Expression,
     index: number
   ) => React$Element<"li">;
@@ -201,17 +202,17 @@ class Expressions extends Component<Prop
     if (!this.props.expressionError) {
       this.hideInput();
     }
 
     this.props.clearAutocomplete();
   };
 
   renderExpression = (expression: Expression, index: number) => {
-    const { expressionError, openLink } = this.props;
+    const { expressionError, openLink, openElementInInspector } = this.props;
     const { editing, editIndex } = this.state;
     const { input, updating } = expression;
     const isEditingExpr = editing && editIndex === index;
     if (isEditingExpr || (isEditingExpr && expressionError)) {
       return this.renderExpressionEditInput(expression);
     }
 
     if (updating) {
@@ -238,16 +239,18 @@ class Expressions extends Component<Prop
         <div className="expression-content">
           <ObjectInspector
             roots={[root]}
             autoExpandDepth={0}
             disableWrap={true}
             focusable={false}
             openLink={openLink}
             createObjectClient={grip => createObjectClient(grip)}
+            onDOMNodeClick={grip => openElementInInspector(grip)}
+            onInspectIconClick={grip => openElementInInspector(grip)}
           />
           <div className="expression-container__close-btn">
             <CloseButton
               handleClick={e => this.deleteExpression(e, expression)}
               tooltip={L10N.getStr("expressions.remove.tooltip")}
             />
           </div>
         </div>
@@ -369,11 +372,12 @@ export default connect(
     autocomplete: actions.autocomplete,
     clearAutocomplete: actions.clearAutocomplete,
     onExpressionAdded: actions.onExpressionAdded,
     addExpression: actions.addExpression,
     clearExpressionError: actions.clearExpressionError,
     evaluateExpressions: actions.evaluateExpressions,
     updateExpression: actions.updateExpression,
     deleteExpression: actions.deleteExpression,
-    openLink: actions.openLink
+    openLink: actions.openLink,
+    openElementInInspector: actions.openElementInInspectorCommand
   }
 )(Expressions);
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
@@ -74,17 +74,17 @@ export default class Group extends Compo
     FrameMenu(
       frame,
       frameworkGroupingOn,
       { copyStackTrace, toggleFrameworkGrouping, toggleBlackBox },
       event
     );
   }
 
-  toggleFrames = event => {
+  toggleFrames = (event: SyntheticMouseEvent<HTMLElement>) => {
     event.stopPropagation();
     this.setState(prevState => ({ expanded: !prevState.expanded }));
   };
 
   renderFrames() {
     const {
       group,
       selectFrame,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/FrameworkComponent.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/FrameworkComponent.js
@@ -8,48 +8,53 @@ import { connect } from "react-redux";
 import actions from "../../actions";
 
 import { createObjectClient } from "../../client/firefox";
 import { getSelectedFrame, getAllPopupObjectProperties } from "../../selectors";
 
 import { objectInspector } from "devtools-reps";
 import { isReactComponent } from "../../utils/preview";
 
-import type { Frame } from "../../types";
+import type { Frame, Grip } from "../../types";
 
 const {
   component: ObjectInspector,
   utils: {
     createNode,
     getChildren,
     loadProperties: { loadItemProperties }
   }
 } = objectInspector;
 
 type Props = {
   selectedFrame: Frame,
   popupObjectProperties: Object,
-  setPopupObjectProperties: (Object, Object) => void
+  setPopupObjectProperties: (Object, Object) => void,
+  openElementInInspector: (grip: Grip) => void
 };
 
 class FrameworkComponent extends PureComponent<Props> {
   async componentWillMount() {
     const expression = "this;";
     const { selectedFrame, setPopupObjectProperties } = this.props;
     const value = selectedFrame.this;
 
     const root = createNode({ name: expression, contents: { value } });
     const properties = await loadItemProperties(root, createObjectClient);
     if (properties) {
       setPopupObjectProperties(value, properties);
     }
   }
 
   renderReactComponent() {
-    const { selectedFrame, popupObjectProperties } = this.props;
+    const {
+      selectedFrame,
+      popupObjectProperties,
+      openElementInInspector
+    } = this.props;
     const expression = "this;";
     const value = selectedFrame.this;
     const root = {
       name: expression,
       path: expression,
       contents: { value }
     };
 
@@ -70,16 +75,18 @@ class FrameworkComponent extends PureCom
         <ObjectInspector
           roots={roots}
           autoExpandAll={false}
           autoExpandDepth={0}
           disableWrap={true}
           focusable={false}
           dimTopLevelWindow={true}
           createObjectClient={grip => createObjectClient(grip)}
+          onDOMNodeClick={grip => openElementInInspector(grip)}
+          onInspectIconClick={grip => openElementInInspector(grip)}
         />
       </div>
     );
   }
 
   render() {
     const { selectedFrame } = this.props;
     if (selectedFrame && isReactComponent(selectedFrame.this)) {
@@ -87,17 +94,18 @@ class FrameworkComponent extends PureCom
     }
 
     return null;
   }
 }
 
 const mapStateToProps = state => ({
   selectedFrame: getSelectedFrame(state),
-  popupObjectProperties: getAllPopupObjectProperties(state)
+  popupObjectProperties: getAllPopupObjectProperties(state),
+  openElementInInspector: actions.openElementInInspectorCommand
 });
 
 export default connect(
   mapStateToProps,
   {
     setPopupObjectProperties: actions.setPopupObjectProperties
   }
 )(FrameworkComponent);
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Scopes.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Scopes.js
@@ -14,31 +14,32 @@ import {
   getGeneratedFrameScope,
   getOriginalFrameScope,
   isPaused as getIsPaused,
   getPauseReason
 } from "../../selectors";
 import { getScopes } from "../../utils/pause/scopes";
 
 import { objectInspector } from "devtools-reps";
-import type { Pause, Why } from "../../types";
+import type { Pause, Why, Grip } from "../../types";
 import type { NamedValue } from "../../utils/pause/scopes/types";
 
 import "./Scopes.css";
 
 const { ObjectInspector } = objectInspector;
 
 type Props = {
   isPaused: Pause,
   selectedFrame: Object,
   generatedFrameScopes: Object,
   originalFrameScopes: Object | null,
   isLoading: boolean,
   why: Why,
-  openLink: string => void
+  openLink: string => void,
+  openElementInInspector: (grip: Grip) => void
 };
 
 type State = {
   originalScopes: ?(NamedValue[]),
   generatedScopes: ?(NamedValue[]),
   showOriginal: boolean
 };
 
@@ -91,33 +92,40 @@ class Scopes extends PureComponent<Props
           nextProps.selectedFrame,
           nextProps.generatedFrameScopes
         )
       });
     }
   }
 
   render() {
-    const { isPaused, isLoading, openLink } = this.props;
+    const {
+      isPaused,
+      isLoading,
+      openLink,
+      openElementInInspector
+    } = this.props;
     const { originalScopes, generatedScopes, showOriginal } = this.state;
 
     const scopes = (showOriginal && originalScopes) || generatedScopes;
 
     if (scopes && !isLoading) {
       return (
         <div className="pane scopes-list">
           <ObjectInspector
             roots={scopes}
             autoExpandAll={false}
             autoExpandDepth={1}
             disableWrap={true}
             focusable={false}
             dimTopLevelWindow={true}
             openLink={openLink}
             createObjectClient={grip => createObjectClient(grip)}
+            onDOMNodeClick={grip => openElementInInspector(grip)}
+            onInspectIconClick={grip => openElementInInspector(grip)}
           />
           {originalScopes ? (
             <div className="scope-type-toggle">
               <a
                 href=""
                 onClick={e => {
                   e.preventDefault();
                   this.setState({ showOriginal: !showOriginal });
@@ -179,11 +187,12 @@ const mapStateToProps = state => {
     originalFrameScopes,
     generatedFrameScopes
   };
 };
 
 export default connect(
   mapStateToProps,
   {
-    openLink: actions.openLink
+    openLink: actions.openLink,
+    openElementInInspector: actions.openElementInInspectorCommand
   }
 )(Scopes);
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/XHRBreakpoints.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/XHRBreakpoints.js
@@ -84,17 +84,17 @@ class XHRBreakpoints extends Component<P
   };
 
   handleExistingSubmit = (e: SyntheticEvent<HTMLFormElement>) => {
     e.preventDefault();
     e.stopPropagation();
 
     const { editIndex, inputValue, inputMethod } = this.state;
     const { xhrBreakpoints } = this.props;
-    const { path, method } = xhrBreakpoints.get(editIndex);
+    const { path, method } = xhrBreakpoints[editIndex];
 
     if (path !== inputValue || method != inputMethod) {
       this.props.updateXHRBreakpoint(editIndex, inputValue, inputMethod);
     }
 
     this.hideInput();
   };
 
@@ -115,17 +115,17 @@ class XHRBreakpoints extends Component<P
   };
 
   onFocus = () => {
     this.setState({ focused: true });
   };
 
   editExpression = index => {
     const { xhrBreakpoints } = this.props;
-    const { path, method } = xhrBreakpoints.get(index);
+    const { path, method } = xhrBreakpoints[index];
     this.setState({
       inputValue: path,
       inputMethod: method,
       editing: true,
       editIndex: index
     });
   };
 
@@ -157,17 +157,17 @@ class XHRBreakpoints extends Component<P
     );
   }
   handleCheckbox = index => {
     const {
       xhrBreakpoints,
       enableXHRBreakpoint,
       disableXHRBreakpoint
     } = this.props;
-    const breakpoint = xhrBreakpoints.get(index);
+    const breakpoint = xhrBreakpoints[index];
     if (breakpoint.disabled) {
       enableXHRBreakpoint(index);
     } else {
       disableXHRBreakpoint(index);
     }
   };
 
   renderBreakpoint = breakpoint => {
@@ -213,30 +213,30 @@ class XHRBreakpoints extends Component<P
 
   renderBreakpoints = () => {
     const { showInput, xhrBreakpoints } = this.props;
     const explicitXhrBreakpoints = getExplicitXHRBreakpoints(xhrBreakpoints);
 
     return (
       <ul className="pane expressions-list">
         {explicitXhrBreakpoints.map(this.renderBreakpoint)}
-        {(showInput || explicitXhrBreakpoints.size === 0) &&
+        {(showInput || explicitXhrBreakpoints.length === 0) &&
           this.renderXHRInput(this.handleNewSubmit)}
       </ul>
     );
   };
 
   renderCheckbox = () => {
     const { shouldPauseOnAny, togglePauseOnAny, xhrBreakpoints } = this.props;
     const explicitXhrBreakpoints = getExplicitXHRBreakpoints(xhrBreakpoints);
 
     return (
       <div
         className={classnames("breakpoints-exceptions-options", {
-          empty: explicitXhrBreakpoints.size === 0
+          empty: explicitXhrBreakpoints.length === 0
         })}
       >
         <ExceptionOption
           className="breakpoints-exceptions"
           label={L10N.getStr("pauseOnAnyXHR")}
           isChecked={shouldPauseOnAny}
           onChange={() => togglePauseOnAny()}
         />
--- a/devtools/client/debugger/new/src/reducers/breakpoints.js
+++ b/devtools/client/debugger/new/src/reducers/breakpoints.js
@@ -4,40 +4,38 @@
 
 // @flow
 
 /**
  * Breakpoints reducer
  * @module reducers/breakpoints
  */
 
-import * as I from "immutable";
-
 import { isGeneratedId } from "devtools-source-map";
 import { isEqual } from "lodash";
 
 import { makeLocationId } from "../utils/breakpoint";
 
 import type { XHRBreakpoint, Breakpoint, SourceLocation } from "../types";
 import type { Action, DonePromiseAction } from "../actions/types";
 
 export type BreakpointsMap = { [string]: Breakpoint };
-export type XHRBreakpointsList = I.List<XHRBreakpoint>;
+export type XHRBreakpointsList = $ReadOnlyArray<XHRBreakpoint>;
 
 export type BreakpointsState = {
   breakpoints: BreakpointsMap,
   xhrBreakpoints: XHRBreakpointsList
 };
 
 export function initialBreakpointsState(
-  xhrBreakpoints?: any[] = []
+  xhrBreakpoints?: XHRBreakpointsList = []
 ): BreakpointsState {
   return {
     breakpoints: {},
-    xhrBreakpoints: I.List(xhrBreakpoints),
+    xhrBreakpoints: xhrBreakpoints,
     breakpointsDisabled: false
   };
 }
 
 function update(
   state: BreakpointsState = initialBreakpointsState(),
   action: Action
 ): BreakpointsState {
@@ -113,54 +111,52 @@ function addXHRBreakpoint(state, action)
 
   const existingBreakpointIndex = state.xhrBreakpoints.findIndex(
     bp => bp.path === path && bp.method === method
   );
 
   if (existingBreakpointIndex === -1) {
     return {
       ...state,
-      xhrBreakpoints: xhrBreakpoints.push(breakpoint)
+      xhrBreakpoints: [...xhrBreakpoints, breakpoint]
     };
-  } else if (xhrBreakpoints.get(existingBreakpointIndex) !== breakpoint) {
+  } else if (xhrBreakpoints[existingBreakpointIndex] !== breakpoint) {
+    const newXhrBreakpoints = [...xhrBreakpoints];
+    newXhrBreakpoints[existingBreakpointIndex] = breakpoint;
     return {
       ...state,
-      xhrBreakpoints: xhrBreakpoints.set(existingBreakpointIndex, breakpoint)
+      xhrBreakpoints: newXhrBreakpoints
     };
   }
 
   return state;
 }
 
 function removeXHRBreakpoint(state, action) {
-  const {
-    breakpoint: { path, method }
-  } = action;
+  const { breakpoint } = action;
   const { xhrBreakpoints } = state;
 
   if (action.status === "start") {
     return state;
   }
 
-  const index = xhrBreakpoints.findIndex(
-    bp => bp.path === path && bp.method === method
-  );
-
   return {
     ...state,
-    xhrBreakpoints: xhrBreakpoints.delete(index)
+    xhrBreakpoints: xhrBreakpoints.filter(bp => !isEqual(bp, breakpoint))
   };
 }
 
 function updateXHRBreakpoint(state, action) {
   const { breakpoint, index } = action;
   const { xhrBreakpoints } = state;
+  const newXhrBreakpoints = [...xhrBreakpoints];
+  newXhrBreakpoints[index] = breakpoint;
   return {
     ...state,
-    xhrBreakpoints: xhrBreakpoints.set(index, breakpoint)
+    xhrBreakpoints: newXhrBreakpoints
   };
 }
 
 function setBreakpoint(state, locationId, breakpoint) {
   return {
     ...state,
     breakpoints: { ...state.breakpoints, [locationId]: breakpoint }
   };
--- a/devtools/client/debugger/new/src/selectors/breakpoints.js
+++ b/devtools/client/debugger/new/src/selectors/breakpoints.js
@@ -1,21 +1,24 @@
 /* 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/>. */
 
 // @flow
 
 import { createSelector } from "reselect";
 
-import type { BreakpointsState } from "../reducers/breakpoints";
+import type {
+  BreakpointsState,
+  XHRBreakpointsList
+} from "../reducers/breakpoints";
 
 type OuterState = { breakpoints: BreakpointsState };
 
-export function getXHRBreakpoints(state: OuterState) {
+export function getXHRBreakpoints(state: OuterState): XHRBreakpointsList {
   return state.breakpoints.xhrBreakpoints;
 }
 
 export const shouldPauseOnAnyXHR = createSelector(
   getXHRBreakpoints,
   xhrBreakpoints => {
     const emptyBp = xhrBreakpoints.find(({ path }) => path.length === 0);
     if (!emptyBp) {
--- a/devtools/client/debugger/new/src/selectors/index.js
+++ b/devtools/client/debugger/new/src/selectors/index.js
@@ -22,17 +22,20 @@ export {
   getQuickOpenQuery,
   getQuickOpenType
 } from "../reducers/quick-open";
 
 export {
   getBreakpointAtLocation,
   getBreakpointsAtLine
 } from "./breakpointAtLocation";
-export { getVisibleBreakpoints } from "./visibleBreakpoints";
+export {
+  getVisibleBreakpoints,
+  getFirstVisibleBreakpoints
+} from "./visibleBreakpoints";
 export { inComponent } from "./inComponent";
 export { isSelectedFrameVisible } from "./isSelectedFrameVisible";
 export { getCallStackFrames } from "./getCallStackFrames";
 export { getVisibleSelectedFrame } from "./visibleSelectedFrame";
 export { getBreakpointSources } from "./breakpointSources";
 export { getXHRBreakpoints, shouldPauseOnAnyXHR } from "./breakpoints";
 export { visibleColumnBreakpoints } from "./visibleColumnBreakpoints";
 export { getVisiblePausePoints } from "./visiblePausePoints";
--- a/devtools/client/debugger/new/src/selectors/visibleBreakpoints.js
+++ b/devtools/client/debugger/new/src/selectors/visibleBreakpoints.js
@@ -1,19 +1,23 @@
 /* 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/>. */
 
 // @flow
 
+import { isGeneratedId } from "devtools-source-map";
+import { createSelector } from "reselect";
+import { uniqBy } from "lodash";
+
 import { getBreakpointsList } from "../reducers/breakpoints";
 import { getSelectedSource } from "../reducers/sources";
-import { isGeneratedId } from "devtools-source-map";
-import { createSelector } from "reselect";
+
 import memoize from "../utils/memoize";
+import { sortBreakpoints } from "../utils/breakpoint";
 
 import type { Breakpoint, Source } from "../types";
 
 function getLocation(breakpoint, isGeneratedSource) {
   return isGeneratedSource
     ? breakpoint.generatedLocation || breakpoint.location
     : breakpoint.location;
 }
@@ -51,8 +55,22 @@ export const getVisibleBreakpoints = cre
       return null;
     }
 
     return breakpoints
       .filter(bp => isVisible(bp, selectedSource))
       .map(bp => formatBreakpoint(bp, selectedSource));
   }
 );
+
+/*
+ * Finds the first breakpoint per line, which appear in the selected source.
+  */
+export const getFirstVisibleBreakpoints = createSelector(
+  getVisibleBreakpoints,
+  breakpoints => {
+    if (!breakpoints) {
+      return null;
+    }
+
+    return uniqBy(sortBreakpoints(breakpoints), bp => bp.location.line);
+  }
+);
--- a/devtools/client/debugger/new/src/selectors/visibleColumnBreakpoints.js
+++ b/devtools/client/debugger/new/src/selectors/visibleColumnBreakpoints.js
@@ -107,17 +107,17 @@ export function getColumnBreakpoints(pau
   );
 
   return columnBreakpoints.map(({ location }) => {
     // Find the breakpoint so if we know it's enabled and has condition
     const foundBreakpoint = findBreakpoint(location, breakpointMap);
 
     return {
       location,
-      enabled: !!foundBreakpoint,
+      enabled: !!foundBreakpoint && !foundBreakpoint.disabled,
       condition: foundBreakpoint ? foundBreakpoint.condition : null
     };
   });
 }
 
 export const visibleColumnBreakpoints = createSelector(
   getVisiblePausePoints,
   getVisibleBreakpoints,
--- a/devtools/client/debugger/new/src/utils/bootstrap.js
+++ b/devtools/client/debugger/new/src/utils/bootstrap.js
@@ -111,11 +111,11 @@ function updatePrefs(state: any) {
   if (
     previousPendingBreakpoints &&
     currentPendingBreakpoints !== previousPendingBreakpoints
   ) {
     asyncStore.pendingBreakpoints = currentPendingBreakpoints;
   }
 
   if (currentXHRBreakpoints !== previousXHRBreakpoints) {
-    asyncStore.xhrBreakpoints = currentXHRBreakpoints.toJS();
+    asyncStore.xhrBreakpoints = currentXHRBreakpoints;
   }
 }
--- a/devtools/client/debugger/new/src/utils/breakpoint/astBreakpointLocation.js
+++ b/devtools/client/debugger/new/src/utils/breakpoint/astBreakpointLocation.js
@@ -11,30 +11,35 @@ import type { SourceLocation, Source, AS
 import type { Symbols } from "../../reducers/ast";
 
 export function getASTLocation(
   source: Source,
   symbols: ?Symbols,
   location: SourceLocation
 ): ASTLocation {
   if (source.isWasm || !symbols || symbols.loading) {
-    return { name: undefined, offset: location };
+    return { name: undefined, offset: location, index: 0 };
   }
 
   const scope = findClosestFunction(symbols, location);
   if (scope) {
     // we only record the line, but at some point we may
     // also do column offsets
     const line = location.line - scope.location.start.line;
     return {
       name: scope.name,
-      offset: { line, column: undefined }
+      offset: { line, column: undefined },
+      index: scope.index
     };
   }
-  return { name: undefined, offset: location };
+  return { name: undefined, offset: location, index: 0 };
 }
 
-export async function findScopeByName(source: Source, name: ?string) {
+export async function findScopeByName(
+  source: Source,
+  name: ?string,
+  index: number
+) {
   const symbols = await getSymbols(source.id);
   const functions = symbols.functions;
 
-  return functions.find(node => node.name === name);
+  return functions.find(node => node.name === name && node.index === index);
 }
--- a/devtools/client/debugger/new/src/utils/breakpoint/index.js
+++ b/devtools/client/debugger/new/src/utils/breakpoint/index.js
@@ -128,17 +128,21 @@ export function createBreakpoint(
     hidden,
     generatedLocation,
     astLocation,
     id,
     text,
     originalText
   } = overrides;
 
-  const defaultASTLocation = { name: undefined, offset: location };
+  const defaultASTLocation = {
+    name: undefined,
+    offset: location,
+    index: 0
+  };
   const properties = {
     id,
     condition: condition || null,
     disabled: disabled || false,
     hidden: hidden || false,
     loading: false,
     astLocation: astLocation || defaultASTLocation,
     generatedLocation: generatedLocation || location,
@@ -181,15 +185,25 @@ export function createPendingBreakpoint(
     condition: bp.condition,
     disabled: bp.disabled,
     location: pendingLocation,
     astLocation: bp.astLocation,
     generatedLocation: pendingGeneratedLocation
   };
 }
 
-export function sortBreakpoints(breakpoints: FormattedBreakpoint[]) {
+export function sortFormattedBreakpoints(breakpoints: FormattedBreakpoint[]) {
+  return _sortBreakpoints(breakpoints, "selectedLocation");
+}
+
+export function sortBreakpoints(breakpoints: Breakpoint[]) {
+  return _sortBreakpoints(breakpoints, "location");
+}
+
+function _sortBreakpoints(breakpoints: Array<Object>, property: string) {
   return sortBy(breakpoints, [
-    "selectedLocation.line",
-    ({ selectedLocation }) =>
-      selectedLocation.column === undefined || selectedLocation.column
+    // Priority: line number, undefined column, column number
+    `${property}.line`,
+    bp => {
+      return bp[property].column === undefined || bp[property].column;
+    }
   ]);
 }
--- a/devtools/client/debugger/new/src/utils/editor/index.js
+++ b/devtools/client/debugger/new/src/utils/editor/index.js
@@ -53,32 +53,28 @@ export function endOperation() {
   const codeMirror = getCodeMirror();
   if (!codeMirror) {
     return;
   }
 
   codeMirror.endOperation();
 }
 
-export function shouldShowPrettyPrint(selectedSource) {
-  if (!selectedSource) {
-    return false;
-  }
-
-  return shouldPrettyPrint(selectedSource);
+export function shouldShowPrettyPrint(source) {
+  return shouldPrettyPrint(source);
 }
 
-export function shouldShowFooter(selectedSource, horizontal) {
+export function shouldShowFooter(source, horizontal) {
   if (!horizontal) {
     return true;
   }
-  if (!selectedSource) {
+  if (!source) {
     return false;
   }
-  return shouldShowPrettyPrint(selectedSource) || isOriginal(selectedSource);
+  return shouldShowPrettyPrint(source) || isOriginal(source);
 }
 
 export function traverseResults(e, ctx, query, dir, modifiers) {
   e.stopPropagation();
   e.preventDefault();
 
   if (dir == "prev") {
     findPrev(ctx, query, true, modifiers);
--- a/devtools/client/debugger/new/src/utils/source.js
+++ b/devtools/client/debugger/new/src/utils/source.js
@@ -431,28 +431,28 @@ export function getSourceClassnames(
 ) {
   // Conditionals should be ordered by priority of icon!
   const defaultClassName = "file";
 
   if (!source || !source.url) {
     return defaultClassName;
   }
 
-  if (sourceMetaData && sourceMetaData.framework) {
-    return sourceMetaData.framework.toLowerCase();
-  }
-
   if (isPretty(source)) {
     return "prettyPrint";
   }
 
   if (source.isBlackBoxed) {
     return "blackBox";
   }
 
+  if (sourceMetaData && sourceMetaData.framework) {
+    return sourceMetaData.framework.toLowerCase();
+  }
+
   return sourceTypes[getFileExtension(source)] || defaultClassName;
 }
 
 export function getRelativeUrl(source: Source, root: string) {
   const { group, path } = getURL(source);
   if (!root) {
     return path;
   }
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -598,16 +598,17 @@ support-files =
   examples/big-sourcemap_files/bundle.js
   examples/big-sourcemap_files/bundle.js.map
   examples/reload/code_reload_1.js
   examples/reload/code_reload_2.js
   examples/reload/doc-reload.html
   examples/reload/sjs_code_reload.sjs
   examples/doc-async.html
   examples/doc-asm.html
+  examples/doc-duplicate-functions.html
   examples/doc-sourcemapped.html
   examples/doc-content-script-sources.html
   examples/doc-scripts.html
   examples/doc-scripts-debugger.html
   examples/doc-script-mutate.html
   examples/doc-script-switching.html
   examples/doc-exceptions.html
   examples/doc-iframes.html
@@ -666,17 +667,19 @@ skip-if = ccov || (verify && debug && (o
 [browser_dbg-sourcemapped-stepping.js]
 skip-if = (os == 'win' && os_version == '10.0' && ccov) # Bug 1480680
 [browser_dbg-sourcemapped-preview.js]
 skip-if = os == "win" # Bug 1448523, Bug 1448450
 [browser_dbg-breaking.js]
 [browser_dbg-breaking-from-console.js]
 [browser_dbg-breakpoints.js]
 [browser_dbg-breakpoints-actions.js]
+[browser_dbg-breakpoints-columns.js]
 [browser_dbg-breakpoints-cond.js]
+[browser_dbg-breakpoints-duplicate-functions.js]
 [browser_dbg-browser-content-toolbox.js]
 skip-if = !e10s || verify # This test is only valid in e10s
 [browser_dbg-breakpoints-reloading.js]
 [browser_dbg-breakpoint-skipping.js]
 [browser_dbg-call-stack.js]
 [browser_dbg-scopes.js]
 [browser_dbg-chrome-create.js]
 skip-if = (verify && !debug && (os == 'linux'))
@@ -691,16 +694,17 @@ skip-if = (os == "win" && ccov) # Bug 14
 [browser_dbg-editor-gutter.js]
 [browser_dbg-editor-select.js]
 [browser_dbg-editor-highlight.js]
 [browser_dbg-ember-quickstart.js]
 [browser_dbg-expressions.js]
 [browser_dbg-expressions-error.js]
 [browser_dbg-iframes.js]
 [browser_dbg-inline-cache.js]
+[browser_dbg-inspector-integration.js]
 [browser_dbg-keyboard-navigation.js]
 [browser_dbg-keyboard-shortcuts.js]
 skip-if = os == "linux" # bug 1351952
 [browser_dbg-layout-changes.js]
 [browser_dbg-outline.js]
 skip-if = verify
 [browser_dbg-outline-pretty.js]
 [browser_dbg-outline-filter.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-columns.js
@@ -0,0 +1,106 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function getColumnBreakpointElements(dbg) {
+  return findAllElementsWithSelector(dbg, ".column-breakpoint");
+}
+
+async function assertConditionalBreakpointIsFocused(dbg) {
+  const input = findElement(dbg, "conditionalPanelInput");
+  await waitForElementFocus(dbg, input);
+}
+
+function waitForElementFocus(dbg, el) {
+  const doc = dbg.win.document;
+  return waitFor(() => doc.activeElement == el && doc.hasFocus());
+}
+
+function hasCondition(marker) {
+  return marker.classList.contains("has-condition");
+}
+
+async function setConditionalBreakpoint(dbg, index, condition) {
+  const {
+      addConditionalBreakpoint,
+      editBreakpoint
+  } = selectors.gutterContextMenu;
+  // Make this work with either add or edit menu items
+  const selector = `${addConditionalBreakpoint},${editBreakpoint}`;
+
+  rightClickElement(dbg, "breakpointItem", index);
+  selectContextMenuItem(dbg, selector);
+  await waitForElement(dbg, "conditionalPanelInput");
+  await assertConditionalBreakpointIsFocused(dbg);
+
+  // Position cursor reliably at the end of the text.
+  pressKey(dbg, "End");
+  type(dbg, condition);
+  pressKey(dbg, "Enter");
+}
+
+function removeBreakpointViaContext(dbg, index) {
+  rightClickElement(dbg, "breakpointItem", index);
+  selectContextMenuItem(dbg, "#node-menu-delete-self");
+}
+
+// Test enabling and disabling a breakpoint using the check boxes
+add_task(async function() {
+  const dbg = await initDebugger("doc-scripts.html", "simple1");
+  await pushPref("devtools.debugger.features.column-breakpoints", true);
+
+  await selectSource(dbg, "simple1");
+
+  // Scroll down to desired line so that column breakpoints render
+  getCM(dbg).setCursor({ line: 15, ch: 0 });
+
+  // Create a breakpoint at 15:undefined
+  await addBreakpoint(dbg, "simple1", 15);
+  
+  // Wait for column breakpoint markers
+  await waitForElementWithSelector(dbg, ".column-breakpoint");
+
+  let columnBreakpointMarkers = getColumnBreakpointElements(dbg);
+  ok(
+    columnBreakpointMarkers.length === 2, 
+      "2 column breakpoint markers display"
+  );
+
+  // Create a breakpoint at 15:8
+  columnBreakpointMarkers[0].click();
+
+  // Create a breakpoint at 15:28
+  columnBreakpointMarkers[1].click();
+
+  // Wait for breakpoints in right panel to render
+  await waitForState(dbg, state => {
+    return dbg.win.document.querySelectorAll(".breakpoints-list .breakpoint").length === 3;
+  })
+
+  // Scroll down in secondary pane so element we want to right-click is showing
+  dbg.win.document.querySelector(".secondary-panes").scrollTop = 100;
+
+  // Set a condition at 15:8
+  await setConditionalBreakpoint(dbg, 4, "Eight");
+
+  // Ensure column breakpoint is yellow
+  await waitForElementWithSelector(dbg, ".column-breakpoint.has-condition");
+  
+  // Remove the breakpoint from 15:undefined via the secondary pane context menu
+  removeBreakpointViaContext(dbg, 3);
+
+  // Ensure that there's still a marker on line 15
+  await waitForState(dbg, state => dbg.selectors.getBreakpointCount(state) == 2);
+  await waitForElementWithSelector(dbg, ".column-breakpoint.has-condition");
+  columnBreakpointMarkers = getColumnBreakpointElements(dbg);
+  ok(hasCondition(columnBreakpointMarkers[0]), "First column breakpoint has conditional style");
+
+  // Remove the breakpoint from 15:8
+  removeBreakpointViaContext(dbg, 3);
+
+  // Ensure there's still a marker and it has no condition
+  await waitForState(dbg, state => dbg.selectors.getBreakpointCount(state) == 1);
+  await waitForElementWithSelector(dbg, ".column-breakpoint");
+
+  // Ensure the first column breakpoint has no conditional style
+  await waitFor(() => !hasCondition(getColumnBreakpointElements(dbg)[0]));
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-breakpoints-duplicate-functions.js
@@ -0,0 +1,20 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function firstBreakpoint(dbg) {
+  return dbg.selectors.getBreakpointsList(dbg.getState())[0];
+}
+
+// tests to make sure we do not accidentally slide the breakpoint up to the first
+// function with the same name in the file.
+add_task(async function() {
+  const dbg = await initDebugger("doc-duplicate-functions.html", "doc-duplicate-functions");
+  const source = findSource(dbg, "doc-duplicate-functions");
+
+  await selectSource(dbg, source.url);
+  await addBreakpoint(dbg, source.url, 19);
+
+  await reload(dbg, source.url);
+  await waitForState(dbg, state => dbg.selectors.getBreakpointCount(state) == 1);
+  is(firstBreakpoint(dbg).location.line, 19, "Breakpoint is on line 19");
+});
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions.js
@@ -4,21 +4,16 @@
 /**
  * tests the watch expressions component
  * 1. add watch expressions
  * 2. edit watch expressions
  * 3. delete watch expressions
  * 4. expanding properties when not paused
  */
 
-const expressionSelectors = {
-  plusIcon: ".watch-expressions-pane button.plus",
-  input: "input.input-expression"
-};
-
 function getLabel(dbg, index) {
   return findElement(dbg, "expressionNode", index).innerText;
 }
 
 function getValue(dbg, index) {
   return findElement(dbg, "expressionValue", index).innerText;
 }
 
@@ -27,41 +22,16 @@ function assertEmptyValue(dbg, index) {
   if (value) {
     is(value.innerText, "");
     return;
   }
 
   is(value, null);
 }
 
-async function addExpression(dbg, input) {
-  info("Adding an expression");
-
-  const plusIcon = findElementWithSelector(dbg, expressionSelectors.plusIcon);
-  if (plusIcon) {
-    plusIcon.click();
-  }
-  findElementWithSelector(dbg, expressionSelectors.input).focus();
-  type(dbg, input);
-  pressKey(dbg, "Enter");
-
-  await waitForDispatch(dbg, "EVALUATE_EXPRESSION");
-}
-
-async function editExpression(dbg, input) {
-  info("Updating the expression");
-  dblClickElement(dbg, "expressionNode", 1);
-  // Position cursor reliably at the end of the text.
-  pressKey(dbg, "End");
-  type(dbg, input);
-  const evaluated = waitForDispatch(dbg, "EVALUATE_EXPRESSIONS");
-  pressKey(dbg, "Enter");
-  await evaluated;
-}
-
 add_task(async function() {
   const dbg = await initDebugger("doc-script-switching.html");
 
   invokeInTab("firstCall");
   await waitForPaused(dbg);
 
   await addExpression(dbg, "f");
   is(getLabel(dbg, 1), "f");
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-inspector-integration.js
@@ -0,0 +1,27 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that clicking the DOM node button in any ObjectInspect
+// opens the Inspector panel
+
+function waitForInspectorPanelChange(dbg) {
+  const { toolbox } = dbg;
+
+  return new Promise(resolve => {
+    toolbox.getPanelWhenReady("inspector").then(() => {
+    ok(toolbox.inspector, "Inspector is shown.");
+    resolve(toolbox.inspector);
+    });
+  });
+}
+
+add_task(async function() {
+  const dbg = await initDebugger("doc-script-switching.html");
+
+  await addExpression(dbg, "window.document.body.firstChild");
+
+  await waitForElementWithSelector(dbg, "button.open-inspector");
+  findElementWithSelector(dbg, "button.open-inspector").click();
+
+  await waitForInspectorPanelChange(dbg);
+});
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/doc-duplicate-functions.html
@@ -0,0 +1,24 @@
+<!-- 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/. -->
+<html>
+ <head>
+   <title>Debugger test page</title>
+ </head>
+ <body>
+   <h1> debugger test page2</h1>
+   <script type="text/javascript">
+     function a() {
+       function render() {
+         console.log('a');
+       }
+     }
+
+     function b() {
+       function render() {
+         console.log('b');
+       }
+     }
+   </script>
+ </body>
+</html>
--- a/devtools/client/debugger/new/test/mochitest/helpers.js
+++ b/devtools/client/debugger/new/test/mochitest/helpers.js
@@ -1027,16 +1027,17 @@ const selectors = {
   expressionNode: i =>
     `.expressions-list .expression-container:nth-child(${i}) .object-label`,
   expressionValue: i =>
     `.expressions-list .expression-container:nth-child(${i}) .object-delimiter + *`,
   expressionClose: i =>
     `.expressions-list .expression-container:nth-child(${i}) .close`,
   expressionInput: ".expressions-list  input.input-expression",
   expressionNodes: ".expressions-list .tree-node",
+  expressionPlus: ".watch-expressions-pane button.plus",
   scopesHeader: ".scopes-pane ._header",
   breakpointItem: i => `.breakpoints-list div:nth-of-type(${i})`,
   breakpointItems: `.breakpoints-list .breakpoint`,
   breakpointContextMenu: {
     disableSelf: "#node-menu-disable-self",
     disableAll: "#node-menu-disable-all",
     disableOthers: "#node-menu-disable-others",
     enableSelf: "#node-menu-enable-self",
@@ -1047,19 +1048,20 @@ const selectors = {
   },
   scopes: ".scopes-list",
   scopeNode: i => `.scopes-list .tree-node:nth-child(${i}) .object-label`,
   scopeValue: i =>
     `.scopes-list .tree-node:nth-child(${i}) .object-delimiter + *`,
   frame: i => `.frames ul li:nth-child(${i})`,
   frames: ".frames ul li",
   gutter: i => `.CodeMirror-code *:nth-child(${i}) .CodeMirror-linenumber`,
+  // These work for bobth the breakpoint listing and gutter marker
   gutterContextMenu: {
-    addConditionalBreakpoint: "#node-menu-add-conditional-breakpoint",
-    editBreakpoint: "#node-menu-edit-conditional-breakpoint"
+    addConditionalBreakpoint: "#node-menu-add-condition, #node-menu-add-conditional-breakpoint",
+    editBreakpoint: "#node-menu-edit-condition, #node-menu-edit-conditional-breakpoint"
   },
   menuitem: i => `menupopup menuitem:nth-child(${i})`,
   pauseOnExceptions: ".pause-exceptions",
   breakpoint: ".CodeMirror-code > .new-breakpoint",
   highlightLine: ".CodeMirror-code > .highlight-line",
   debugLine: ".new-debug-line",
   debugErrorLine: ".new-debug-line-error",
   codeMirror: ".CodeMirror",
@@ -1111,16 +1113,20 @@ function findElement(dbg, elementName, .
 }
 
 function findElementWithSelector(dbg, selector) {
   return dbg.win.document.querySelector(selector);
 }
 
 function findAllElements(dbg, elementName, ...args) {
   const selector = getSelector(elementName, ...args);
+  return findAllElementsWithSelector(dbg, selector)
+}
+
+function findAllElementsWithSelector(dbg, selector) {
   return dbg.win.document.querySelectorAll(selector);
 }
 
 /**
  * Simulates a mouse click in the debugger DOM.
  *
  * @memberof mochitest/helpers
  * @param {Object} dbg
@@ -1154,16 +1160,17 @@ function dblClickElement(dbg, elementNam
     { clickCount: 2 },
     dbg.win
   );
 }
 
 function rightClickElement(dbg, elementName, ...args) {
   const selector = getSelector(elementName, ...args);
   const doc = dbg.win.document;
+
   return EventUtils.synthesizeMouseAtCenter(
     doc.querySelector(selector),
     { type: "contextmenu" },
     dbg.win
   );
 }
 
 function selectContextMenuItem(dbg, selector) {
@@ -1377,8 +1384,33 @@ async function waitForNodeToGainFocus(db
   }, `waiting for source node ${index} to be focused`);
 }
 
 async function assertNodeIsFocused(dbg, index) {
   await waitForNodeToGainFocus(dbg, index);
   const node = findElement(dbg, "sourceNode", index);
   ok(node.classList.contains("focused"), `node ${index} is focused`);
 }
+
+async function addExpression(dbg, input) {
+  info("Adding an expression");
+
+  const plusIcon = findElementWithSelector(dbg, selectors.expressionPlus);
+  if (plusIcon) {
+    plusIcon.click();
+  }
+  findElementWithSelector(dbg, selectors.expressionInput).focus();
+  type(dbg, input);
+  pressKey(dbg, "Enter");
+
+  await waitForDispatch(dbg, "EVALUATE_EXPRESSION");
+}
+
+async function editExpression(dbg, input) {
+  info("Updating the expression");
+  dblClickElement(dbg, "expressionNode", 1);
+  // Position cursor reliably at the end of the text.
+  pressKey(dbg, "End");
+  type(dbg, input);
+  const evaluated = waitForDispatch(dbg, "EVALUATE_EXPRESSIONS");
+  pressKey(dbg, "Enter");
+  await evaluated;
+}
\ No newline at end of file
--- a/devtools/client/locales/en-US/debugger.properties
+++ b/devtools/client/locales/en-US/debugger.properties
@@ -596,17 +596,21 @@ sourceFooter.mappedSourceTooltip=(Source
 sourceFooter.mappedSuffix=(mapped)
 
 # LOCALIZATION NOTE (sourceFooter.codeCoverage): Text associated
 # with a code coverage button
 sourceFooter.codeCoverage=Code coverage
 
 # LOCALIZATION NOTE (sourceFooter.currentCursorPosition): Text associated
 # with the current cursor line and column
-sourceFooter.currentCursorPosition=(%S, %S)
+sourceFooter.currentCursorPosition=(%1$S, %2$S)
+
+# LOCALIZATION NOTE (sourceFooter.currentCursorPosition.tooltip): Text associated
+# with the current cursor line and column
+sourceFooter.currentCursorPosition.tooltip=(Line %1$S, column %2$S)
 
 # LOCALIZATION NOTE (sourceTabs.closeTabButtonTooltip): The tooltip that is displayed
 # for close tab button in source tabs.
 sourceTabs.closeTabButtonTooltip=Close tab
 
 # LOCALIZATION NOTE (scopes.header): Scopes right sidebar pane header.
 scopes.header=Scopes
 
--- a/dom/html/test/forms/mochitest.ini
+++ b/dom/html/test/forms/mochitest.ini
@@ -46,17 +46,17 @@ skip-if = android_version == '18' # bug 
 [test_input_file_picker.html]
 [test_input_list_attribute.html]
 [test_input_number_l10n.html]
 # We don't build ICU for Firefox for Android:
 skip-if = os == "android"
 [test_input_number_key_events.html]
 [test_input_number_mouse_events.html]
 # Not run on Firefox for Android where the spin buttons are hidden:
-skip-if = os == "android"
+skip-if = os == "android" || (os == "mac" && debug) # Bug 1484442
 [test_input_number_rounding.html]
 skip-if = os == "android"
 [test_input_number_validation.html]
 # We don't build ICU for Firefox for Android:
 skip-if = os == "android"
 [test_input_number_focus.html]
 [test_input_number_placeholder_shown.html]
 [test_input_range_attr_order.html]
--- a/dom/media/BaseMediaResource.cpp
+++ b/dom/media/BaseMediaResource.cpp
@@ -2,16 +2,17 @@
 
 #include "ChannelMediaResource.h"
 #include "CloneableWithRangeMediaResource.h"
 #include "FileMediaResource.h"
 #include "MediaContainerType.h"
 #include "mozilla/dom/BlobImpl.h"
 #include "mozilla/dom/BlobURLProtocolHandler.h"
 #include "mozilla/dom/HTMLMediaElement.h"
+#include "mozilla/InputStreamLengthHelper.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsICloneableInputStream.h"
 #include "nsIFile.h"
 #include "nsIFileChannel.h"
 #include "nsIInputStream.h"
 #include "nsISeekableStream.h"
 #include "nsNetUtil.h"
@@ -54,34 +55,35 @@ already_AddRefed<BaseMediaResource> Base
     IgnoredErrorResult rv;
 
     nsCOMPtr<nsIInputStream> stream;
     blobImpl->CreateInputStream(getter_AddRefs(stream), rv);
     if (NS_WARN_IF(rv.Failed())) {
       return nullptr;
     }
 
-    // It's better to read the size from the blob instead of using ::Available,
-    // because, if the stream implements nsIAsyncInputStream interface,
-    // ::Available will not return the size of the stream, but what can be
-    // currently read.
+    // If this stream knows its own size synchronously, we can still use
+    // FileMediaResource. If the size is known, it means that the reading
+    // doesn't require any async operation.  This is required because
+    // FileMediaResource doesn't work with nsIAsyncInputStreams.
+    int64_t length;
+    if (InputStreamLengthHelper::GetSyncLength(stream, &length) &&
+        length >= 0) {
+      RefPtr<BaseMediaResource> resource =
+          new FileMediaResource(aCallback, aChannel, uri, length);
+      return resource.forget();
+    }
+
+    // Also if the stream doesn't know its own size synchronously, we can still
+    // read the length from the blob.
     uint64_t size = blobImpl->GetSize(rv);
     if (NS_WARN_IF(rv.Failed())) {
       return nullptr;
     }
 
-    // If the URL is a blob URL, with a seekable inputStream, we can still use
-    // a FileMediaResource.
-    nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(stream);
-    if (seekableStream) {
-      RefPtr<BaseMediaResource> resource =
-          new FileMediaResource(aCallback, aChannel, uri, size);
-      return resource.forget();
-    }
-
     // Maybe this blob URL can be cloned with a range.
     nsCOMPtr<nsICloneableInputStreamWithRange> cloneableWithRange =
         do_QueryInterface(stream);
     if (cloneableWithRange) {
       RefPtr<BaseMediaResource> resource = new CloneableWithRangeMediaResource(
           aCallback, aChannel, uri, stream, size);
       return resource.forget();
     }
--- a/gfx/2d/CaptureCommandList.h
+++ b/gfx/2d/CaptureCommandList.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_gfx_2d_CaptureCommandList_h
 #define mozilla_gfx_2d_CaptureCommandList_h
 
 #include "mozilla/Move.h"
 #include "mozilla/PodOperations.h"
 #include <vector>
+#include <limits>
 
 #include "DrawCommand.h"
 #include "Logging.h"
 
 namespace mozilla {
 namespace gfx {
 
 class CaptureCommandList {
@@ -31,40 +32,51 @@ class CaptureCommandList {
     mStorage = std::move(aOther.mStorage);
     mLastCommand = aOther.mLastCommand;
     aOther.mLastCommand = nullptr;
     return *this;
   }
 
   template <typename T>
   T* Append() {
-    size_t oldSize = mStorage.size();
-    mStorage.resize(mStorage.size() + sizeof(T) + sizeof(uint32_t));
-    uint8_t* nextDrawLocation = &mStorage.front() + oldSize;
-    *(uint32_t*)(nextDrawLocation) = sizeof(T) + sizeof(uint32_t);
-    T* newCommand = reinterpret_cast<T*>(nextDrawLocation + sizeof(uint32_t));
-    mLastCommand = newCommand;
-    return newCommand;
+    static_assert(sizeof(T) + sizeof(uint16_t) + sizeof(uint16_t) <=
+                      std::numeric_limits<uint16_t>::max(),
+                  "encoding is too small to contain advance");
+    const uint16_t kAdvance = sizeof(T) + sizeof(uint16_t) + sizeof(uint16_t);
+
+    size_t size = mStorage.size();
+    mStorage.resize(size + kAdvance);
+
+    uint8_t* current = &mStorage.front() + size;
+    *(uint16_t*)(current) = kAdvance;
+    current += sizeof(uint16_t);
+    *(uint16_t*)(current) = ~kAdvance;
+    current += sizeof(uint16_t);
+
+    T* command = reinterpret_cast<T*>(current);
+    mLastCommand = command;
+    return command;
   }
 
   template <typename T>
   T* ReuseOrAppend() {
     {  // Scope lock
       if (mLastCommand != nullptr && mLastCommand->GetType() == T::Type) {
         return reinterpret_cast<T*>(mLastCommand);
       }
     }
     return Append<T>();
   }
 
   bool IsEmpty() const { return mStorage.empty(); }
 
   template <typename T>
   bool BufferWillAlloc() const {
-    return mStorage.size() + sizeof(uint32_t) + sizeof(T) > mStorage.capacity();
+    const uint16_t kAdvance = sizeof(T) + sizeof(uint16_t) + sizeof(uint16_t);
+    return mStorage.size() + kAdvance > mStorage.capacity();
   }
 
   size_t BufferCapacity() const { return mStorage.capacity(); }
 
   void Clear();
 
   class iterator {
    public:
@@ -74,17 +86,21 @@ class CaptureCommandList {
         mCurrent = &mParent.mStorage.front();
         mEnd = mCurrent + mParent.mStorage.size();
       }
     }
 
     bool Done() const { return mCurrent >= mEnd; }
     void Next() {
       MOZ_ASSERT(!Done());
-      mCurrent += *reinterpret_cast<uint32_t*>(mCurrent);
+      uint16_t advance = *reinterpret_cast<uint16_t*>(mCurrent);
+      uint16_t redundant =
+          ~*reinterpret_cast<uint16_t*>(mCurrent + sizeof(uint16_t));
+      MOZ_RELEASE_ASSERT(advance == redundant);
+      mCurrent += advance;
     }
     DrawingCommand* Get() {
       MOZ_ASSERT(!Done());
       return reinterpret_cast<DrawingCommand*>(mCurrent + sizeof(uint32_t));
     }
 
    private:
     CaptureCommandList& mParent;
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -339,16 +339,82 @@ static bool GetDWriteFamilyName(const Re
   RefPtr<IDWriteLocalizedStrings> names;
   HRESULT hr = aFamily->GetFamilyNames(getter_AddRefs(names));
   if (FAILED(hr)) {
     return false;
   }
   return GetDWriteName(names, aOutName);
 }
 
+static void GetFontFileNames(RefPtr<IDWriteFontFace> aFontFace,
+                             std::vector<WCHAR>& aFamilyName,
+                             std::vector<WCHAR>& aFileNames) {
+  MOZ_ASSERT(aFamilyName.size() >= 1 && aFamilyName.back() == 0);
+
+  UINT32 numFiles;
+  HRESULT hr = aFontFace->GetFiles(&numFiles, nullptr);
+  if (FAILED(hr)) {
+    gfxCriticalNote << "Failed getting file count for font \"" << &aFamilyName[0] << "\"";
+    return;
+  } else if (!numFiles) {
+    gfxCriticalNote << "No files found for font \"" << &aFamilyName[0] << "\"";
+    return;
+  }
+  std::vector<RefPtr<IDWriteFontFile>> files;
+  files.resize(numFiles);
+  hr = aFontFace->GetFiles(&numFiles, getter_AddRefs(files[0]));
+  if (FAILED(hr)) {
+    gfxCriticalNote << "Failed getting files for font \"" << &aFamilyName[0] << "\"";
+    return;
+  }
+
+  for(auto& file : files) {
+    const void* key;
+    UINT32 keySize;
+    hr = file->GetReferenceKey(&key, &keySize);
+    if (FAILED(hr)) {
+      gfxCriticalNote << "Failed getting file ref key for font \"" << &aFamilyName[0] << "\"";
+      return;
+    }
+    RefPtr<IDWriteFontFileLoader> loader;
+    hr = file->GetLoader(getter_AddRefs(loader));
+    if (FAILED(hr)) {
+      gfxCriticalNote << "Failed getting file loader for font \"" << &aFamilyName[0] << "\"";
+      return;
+    }
+    RefPtr<IDWriteLocalFontFileLoader> localLoader;
+    loader->QueryInterface(__uuidof(IDWriteLocalFontFileLoader), (void**)getter_AddRefs(localLoader));
+    if (!localLoader) {
+      gfxCriticalNote << "Failed querying loader interface for font \"" << &aFamilyName[0] << "\"";
+      return;
+    }
+    UINT32 pathLen;
+    hr = localLoader->GetFilePathLengthFromKey(key, keySize, &pathLen);
+    if (FAILED(hr)) {
+      gfxCriticalNote << "Failed getting path length for font \"" << &aFamilyName[0] << "\"";
+      return;
+    }
+    size_t offset = aFileNames.size();
+    aFileNames.resize(offset + pathLen + 1);
+    hr = localLoader->GetFilePathFromKey(key, keySize, &aFileNames[offset], pathLen + 1);
+    if (FAILED(hr)) {
+      aFileNames.resize(offset);
+      gfxCriticalNote << "Failed getting path for font \"" << &aFamilyName[0] << "\"";
+      return;
+    }
+    MOZ_ASSERT(aFileNames.back() == 0);
+    DWORD attribs = GetFileAttributesW(&aFileNames[offset]);
+    if (attribs == INVALID_FILE_ATTRIBUTES) {
+      gfxCriticalNote << "sending font family \"" << &aFamilyName[0]
+                      << "\" with invalid file \"" << &aFileNames[offset]
+                      << "\"";
+    }
+  }
+}
+
 bool UnscaledFontDWrite::GetWRFontDescriptor(WRFontDescriptorOutput aCb,
                                              void* aBaton) {
   if (!mFont) {
     return false;
   }
 
   RefPtr<IDWriteFontFamily> family;
   HRESULT hr = mFont->GetFontFamily(getter_AddRefs(family));
@@ -381,64 +447,17 @@ bool UnscaledFontDWrite::GetWRFontDescri
   UINT32 idx;
   BOOL exists;
   hr = systemFonts->FindFamilyName(familyName.data(), &idx, &exists);
   if (FAILED(hr) || !exists) {
     return false;
   }
 
   // FIXME: Debugging kluge for bug 1455848. Remove once debugged!
-  UINT32 numFiles;
-  hr = mFontFace->GetFiles(&numFiles, nullptr);
-  if (FAILED(hr) || !numFiles) {
-    return false;
-  }
-  std::vector<RefPtr<IDWriteFontFile>> files;
-  files.resize(numFiles);
-  hr = mFontFace->GetFiles(&numFiles, getter_AddRefs(files[0]));
-  if (FAILED(hr)) {
-    return false;
-  }
-  MOZ_ASSERT(familyName.size() >= 1 && familyName.back() == 0);
-  for(auto& file : files) {
-    const void* key;
-    UINT32 keySize;
-    hr = file->GetReferenceKey(&key, &keySize);
-    if (FAILED(hr)) {
-      return false;
-    }
-    RefPtr<IDWriteFontFileLoader> loader;
-    hr = file->GetLoader(getter_AddRefs(loader));
-    if (FAILED(hr)) {
-      return false;
-    }
-    RefPtr<IDWriteLocalFontFileLoader> localLoader;
-    loader->QueryInterface(__uuidof(IDWriteLocalFontFileLoader), (void**)getter_AddRefs(localLoader));
-    if (!localLoader) {
-      return false;
-    }
-    UINT32 pathLen;
-    hr = localLoader->GetFilePathLengthFromKey(key, keySize, &pathLen);
-    if (FAILED(hr)) {
-      return false;
-    }
-    size_t offset = familyName.size();
-    familyName.resize(offset + pathLen + 1);
-    hr = localLoader->GetFilePathFromKey(key, keySize, &familyName[offset], pathLen + 1);
-    if (FAILED(hr)) {
-      return false;
-    }
-    MOZ_ASSERT(familyName.back() == 0);
-    DWORD attribs = GetFileAttributesW(&familyName[offset]);
-    if (attribs == INVALID_FILE_ATTRIBUTES) {
-      gfxCriticalNote << "sending font family \"" << &familyName[0]
-                      << "\" with invalid file \"" << &familyName[offset]
-                      << "\"";
-    }
-  }
+  GetFontFileNames(mFontFace, familyName, familyName);
 
   // The style information that identifies the font can be encoded easily in
   // less than 32 bits. Since the index is needed for font descriptors, only
   // the family name and style information, pass along the style in the index
   // data to avoid requiring a more complicated structure packing for it in
   // the data payload.
   uint32_t index = weight | (stretch << 16) | (style << 24);
   aCb(reinterpret_cast<const uint8_t*>(familyName.data()),
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -222,16 +222,22 @@ void ClientSingleTiledLayerBuffer::Paint
   }
 
   if (asyncPaint) {
     if (!backBuffer->mCapture->IsEmpty()) {
       UniquePtr<PaintTask> task(new PaintTask());
       task->mCapture = backBuffer->mCapture;
       task->mTarget = backBuffer->mBackBuffer;
       task->mClients = std::move(backBuffer->mTextureClients);
+      if (discardedFrontBuffer) {
+        task->mClients.AppendElement(discardedFrontBuffer);
+      }
+      if (discardedFrontBufferOnWhite) {
+        task->mClients.AppendElement(discardedFrontBufferOnWhite);
+      }
 
       // The target is an alias for the capture, and the paint thread expects
       // to be the only one with a reference to the capture
       backBuffer->mTarget = nullptr;
       backBuffer->mCapture = nullptr;
 
       PaintThread::Get()->QueuePaintTask(std::move(task));
       mManager->SetQueuedAsyncPaints();
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc
@@ -68,16 +68,21 @@ Channel::ChannelImpl::ChannelImpl(const 
     : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
       ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
       ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)),
       shared_secret_(0),
       waiting_for_shared_secret_(false) {
   Init(mode, listener);
 
   if (mode == MODE_SERVER) {
+    // We don't need the pipe name because we've been passed a handle, but we do
+    // need to get the shared secret from the channel_id.
+    PipeName(channel_id, &shared_secret_);
+    waiting_for_shared_secret_ = !!shared_secret_;
+
     // Use the existing handle that was dup'd to us
     pipe_ = server_pipe;
     EnqueueHelloMessage();
   } else {
     // Take the normal init path to connect to the server pipe
     CreatePipe(channel_id, mode);
   }
 }
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -671,16 +671,19 @@ case "$target" in
           changequote([,])
         fi
         dnl VS2013+ supports -Gw for better linker optimizations.
         dnl http://blogs.msdn.com/b/vcblog/archive/2013/09/11/introducing-gw-compiler-switch.aspx
         dnl Disabled on ASan because it causes false-positive ODR violations.
         if test -z "$MOZ_ASAN"; then
             CFLAGS="$CFLAGS -Gw"
             CXXFLAGS="$CXXFLAGS -Gw"
+        else
+            # String tail merging doesn't play nice with ASan's ODR checker.
+            LDFLAGS="$LDFLAGS -opt:nolldtailmerge"
         fi
         # khuey says we can safely ignore MSVC warning C4251
         # MSVC warning C4244 (implicit type conversion may lose data) warns
         # and requires workarounds for perfectly valid code.  Also, GCC/clang
         # don't warn about it by default. So for consistency/sanity, we turn
         # it off on MSVC, too.
         # MSVC warning C4267 warns for narrowing type conversions from size_t
         # to 32-bit integer types on 64-bit platforms.  Since this is virtually
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-sticky/nested-sticky-1-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<style>
+html, body {
+    margin: 0;
+}
+thead tr {
+    position: relative;
+    top: 50px;
+}
+th {
+    background-color: green;
+}
+td {
+    background-color: blue;
+}
+</style>
+<body>
+<table cellspacing=0>
+    <thead>
+        <tr><th>Sticky header</th></tr>
+    </thead>
+    <tbody>
+        <tr><td style="height: 2000px; vertical-align: top">table cell</td></tr>
+    </tbody>
+</table>
+<script>
+  addEventListener('load', function() {
+    document.scrollingElement.scrollTop = 50;
+    document.documentElement.classList.remove('reftest-wait');
+  }, false);
+</script>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-sticky/nested-sticky-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html reftest-async-scroll reftest-async-scroll-y="50">
+<style>
+html, body {
+    margin: 0;
+}
+thead tr {
+    position: sticky;
+    top: 0;
+}
+th {
+    position: sticky;
+    top: -2px;
+    background-color: green;
+}
+td {
+    background-color: blue;
+}
+</style>
+<body>
+<table cellspacing=0>
+    <thead>
+        <tr><th>Sticky header</th></tr>
+    </thead>
+    <tbody>
+        <tr><td style="height: 2000px; vertical-align: top">table cell</td></tr>
+    </tbody>
+</table>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-sticky/nested-sticky-2-ref.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<style>
+html, body {
+    margin: 0;
+}
+thead tr {
+    position: relative;
+    top: 50px;
+}
+th {
+    background-color: green;
+}
+th:first-child {
+    position: relative;
+    top: 10px;
+}
+td {
+    background-color: blue;
+}
+</style>
+<body>
+<table cellspacing=0>
+    <thead>
+        <tr><th>Sticky</th><th>header</th></tr>
+    </thead>
+    <tbody>
+        <tr><td style="height: 2000px; vertical-align: top">table cell</td><td>sibling</td></tr>
+    </tbody>
+</table>
+<script>
+  addEventListener('load', function() {
+    document.scrollingElement.scrollTop = 50;
+    document.documentElement.classList.remove('reftest-wait');
+  }, false);
+</script>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-sticky/nested-sticky-2.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html reftest-async-scroll reftest-async-scroll-y="50">
+<style>
+html, body {
+    margin: 0;
+}
+thead tr {
+    position: sticky;
+    top: 0;
+}
+th {
+    background-color: green;
+}
+th:first-child {
+    position: sticky;
+    top: 10px;
+}
+td {
+    background-color: blue;
+}
+</style>
+<body>
+<table cellspacing=0>
+    <thead>
+        <tr><th>Sticky</th><th>header</th></tr>
+    </thead>
+    <tbody>
+        <tr><td style="height: 2000px; vertical-align: top">table cell</td><td>sibling</td></tr>
+    </tbody>
+</table>
+</html>
--- a/layout/reftests/position-sticky/reftest.list
+++ b/layout/reftests/position-sticky/reftest.list
@@ -46,8 +46,10 @@ fails == column-contain-1a.html column-c
 == column-contain-2.html column-contain-2-ref.html
 == block-in-inline-1.html block-in-inline-1-ref.html
 fuzzy-if(skiaContent,0-1,0-22) fuzzy-if(winWidget&&!layersGPUAccelerated,0-116,0-1320) fuzzy-if(Android,0-8,0-1533) == block-in-inline-2.html block-in-inline-2-ref.html
 fuzzy-if(Android,0-8,0-630) fuzzy-if(OSX,0-1,0-11) fuzzy-if(skiaContent,0-1,0-220) fuzzy-if(winWidget&&!layersGPUAccelerated,0-116,0-1320) == block-in-inline-3.html block-in-inline-3-ref.html
 == block-in-inline-continuations.html block-in-inline-continuations-ref.html
 == iframe-1.html iframe-1-ref.html
 skip-if(Android) == transformed-1.html transformed-1-ref.html # bug 1430787 for android
 fuzzy-if(Android,0-2,0-4) skip-if(!asyncPan||Android) == transformed-2.html transformed-2-ref.html # bug 1430787 for android
+skip-if(!asyncPan) fuzzy-if(Android,0-3,0-4) == nested-sticky-1.html nested-sticky-1-ref.html
+skip-if(!asyncPan) fuzzy-if(Android,0-3,0-4) fuzzy-if(/^Windows\x20NT\x206\.1/.test(http.oscpu),0-4,0-104) == nested-sticky-2.html nested-sticky-2-ref.html
--- a/media/audioipc/server/src/server.rs
+++ b/media/audioipc/server/src/server.rs
@@ -80,39 +80,41 @@ struct ServerStreamCallbacks {
     /// RPC interface to callback server running in client
     rpc: rpc::ClientProxy<CallbackReq, CallbackResp>,
 }
 
 impl ServerStreamCallbacks {
     fn data_callback(&mut self, input: &[u8], output: &mut [u8]) -> isize {
         trace!("Stream data callback: {} {}", input.len(), output.len());
 
-        // len is of input and output is frame len. Turn these into the real lengths.
+        // FFI wrapper (data_cb_c) converted buffers to [u8] slices but len is frames *not* bytes.
+        // Convert slices to correct length now we have {input,output}_frame_size available.
         let real_input = unsafe {
             let nbytes = input.len() * self.input_frame_size as usize;
             slice::from_raw_parts(input.as_ptr(), nbytes)
         };
+        let real_output = unsafe {
+            let nbytes = output.len() * self.output_frame_size as usize;
+            slice::from_raw_parts_mut(output.as_mut_ptr(), nbytes)
+        };
 
         self.input_shm.write(real_input).unwrap();
 
         let r = self
             .rpc
             .call(CallbackReq::Data(
                 output.len() as isize,
                 self.output_frame_size as usize,
             )).wait();
 
         match r {
             Ok(CallbackResp::Data(frames)) => {
                 if frames >= 0 {
                     let nbytes = frames as usize * self.output_frame_size as usize;
-                    let real_output = unsafe {
-                        trace!("Resize output to {}", nbytes);
-                        slice::from_raw_parts_mut(output.as_mut_ptr(), nbytes)
-                    };
+                    trace!("Reslice output to {}", nbytes);
                     self.output_shm.read(&mut real_output[..nbytes]).unwrap();
                 }
                 frames
             }
             _ => {
                 debug!("Unexpected message {:?} during data_callback", r);
                 -1
             }
--- a/old-configure.in
+++ b/old-configure.in
@@ -823,16 +823,19 @@ case "$target" in
             SSSE3_FLAGS="-mssse3"
         fi
         dnl VS2013+ supports -Gw for better linker optimizations.
         dnl http://blogs.msdn.com/b/vcblog/archive/2013/09/11/introducing-gw-compiler-switch.aspx
         dnl Disabled on ASan because it causes false-positive ODR violations.
         if test -z "$MOZ_ASAN"; then
             CFLAGS="$CFLAGS -Gw"
             CXXFLAGS="$CXXFLAGS -Gw"
+        else
+            # String tail merging doesn't play nice with ASan's ODR checker.
+            LDFLAGS="$LDFLAGS -opt:nolldtailmerge"
         fi
         # khuey says we can safely ignore MSVC warning C4251
         # MSVC warning C4244 (implicit type conversion may lose data) warns
         # and requires workarounds for perfectly valid code.  Also, GCC/clang
         # don't warn about it by default. So for consistency/sanity, we turn
         # it off on MSVC, too.
         # MSVC warning C4267 warns for narrowing type conversions from size_t
         # to 32-bit integer types on 64-bit platforms.  Since this is virtually
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -923,18 +923,18 @@ bool AntiTrackingCommon::IsFirstPartySto
   nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
   if (thirdPartyUtil) {
     bool thirdParty = false;
     nsresult rv = thirdPartyUtil->IsThirdPartyWindow(aWindow->GetOuterWindow(),
                                                      aURI, &thirdParty);
     // The result of this assertion depends on whether IsThirdPartyWindow
     // succeeds, because otherwise IsThirdPartyWindowOrChannel artificially
     // fails.
-    MOZ_ASSERT(nsContentUtils::IsThirdPartyWindowOrChannel(
-                   aWindow, nullptr, aURI) == NS_SUCCEEDED(rv));
+    MOZ_ASSERT_IF(NS_SUCCEEDED(rv), nsContentUtils::IsThirdPartyWindowOrChannel(
+                                        aWindow, nullptr, aURI) == thirdParty);
   }
 #endif
 
   nsCOMPtr<nsIPrincipal> parentPrincipal;
   nsCOMPtr<nsIURI> parentPrincipalURI;
   nsCOMPtr<nsIURI> trackingURI;
   nsAutoCString trackingOrigin;
   if (!GetParentPrincipalAndTrackingOrigin(