merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 20 Jul 2016 11:20:15 +0200
changeset 305798 e904e18d7dfcd8097f92d44104ca1462fc5d1335
parent 305581 a6ca570852571ae98ef10902af8bc9a27543383c (diff)
parent 305797 6f5fc76550ed13a9cdde280276dc618f6aae5276 (current diff)
child 305799 ed8e23b5e0c7b739e61173bb180cf3410a306679
child 305804 61336ab346f1c80a5cbe4173a1b1836e474315cb
push id30467
push usercbook@mozilla.com
push dateWed, 20 Jul 2016 09:21:53 +0000
treeherdermozilla-central@e904e18d7dfc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone50.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-inbound to mozilla-central a=merge
dom/indexedDB/ActorsParent.cpp
dom/media/eme/CDMProxy.cpp
dom/plugins/ipc/interpose/plugin_child_quirks.mm
dom/xul/nsXULElement.cpp
gfx/gl/GLLibraryEGL.cpp
gfx/layers/AtomicRefCountedWithFinalize.h
gfx/layers/ipc/CompositorLRU.cpp
gfx/layers/ipc/CompositorLRU.h
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
layout/style/nsStyleStruct.cpp
media/libyuv/BUILD.gn
media/libyuv/CMakeLists.txt
media/libyuv/PRESUBMIT.py
media/libyuv/README.md
media/libyuv/build_overrides/build.gni
media/libyuv/chromium/README
media/libyuv/docs/environment_variables.md
media/libyuv/docs/filtering.md
media/libyuv/docs/formats.md
media/libyuv/docs/getting_started.md
media/libyuv/docs/rotation.md
media/libyuv/download_vs_toolchain.py
media/libyuv/gyp_libyuv
media/libyuv/gyp_libyuv.py
media/libyuv/include/libyuv/compare_row.h
media/libyuv/include/libyuv/rotate_row.h
media/libyuv/setup_links.py
media/libyuv/source/compare_gcc.cc
media/libyuv/source/compare_neon64.cc
media/libyuv/source/rotate_any.cc
media/libyuv/source/rotate_common.cc
media/libyuv/source/rotate_gcc.cc
media/libyuv/source/rotate_neon64.cc
media/libyuv/source/rotate_win.cc
media/libyuv/source/row_gcc.cc
media/libyuv/source/row_neon64.cc
media/libyuv/source/scale_any.cc
media/libyuv/source/scale_gcc.cc
media/libyuv/source/scale_neon64.cc
media/libyuv/sync_chromium.py
media/libyuv/third_party/gflags/BUILD.gn
media/libyuv/third_party/gflags/LICENSE
media/libyuv/third_party/gflags/README.libyuv
media/libyuv/third_party/gflags/gen/posix/include/gflags/gflags.h
media/libyuv/third_party/gflags/gen/posix/include/gflags/gflags_completions.h
media/libyuv/third_party/gflags/gen/posix/include/gflags/gflags_declare.h
media/libyuv/third_party/gflags/gen/posix/include/gflags/gflags_gflags.h
media/libyuv/third_party/gflags/gen/posix/include/private/config.h
media/libyuv/third_party/gflags/gen/win/include/gflags/gflags.h
media/libyuv/third_party/gflags/gen/win/include/gflags/gflags_completions.h
media/libyuv/third_party/gflags/gen/win/include/gflags/gflags_declare.h
media/libyuv/third_party/gflags/gen/win/include/gflags/gflags_gflags.h
media/libyuv/third_party/gflags/gen/win/include/private/config.h
media/libyuv/third_party/gflags/gflags.gyp
media/libyuv/tools/OWNERS
media/libyuv/tools/gritsettings/README
media/libyuv/tools/gritsettings/resource_ids
media/libyuv/tools/msan/OWNERS
media/libyuv/tools/msan/blacklist.txt
media/libyuv/tools/ubsan/OWNERS
media/libyuv/tools/ubsan/blacklist.txt
media/libyuv/tools/ubsan/vptr_blacklist.txt
media/libyuv/unit_test/color_test.cc
media/libyuv/unit_test/testdata/juno.txt
media/libyuv/util/android/test_runner.py
mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java
mobile/android/base/java/org/mozilla/gecko/SurfaceBits.java
mobile/android/base/moz.build
netwerk/test/unit/test_bug651100.js
netwerk/test/unit/test_bug712914_secinfo_validation.js
taskcluster/ci/legacy/tasks/branches/base_jobs.yml
taskcluster/taskgraph/try_option_syntax.py
testing/mochitest/browser.eslintrc
testing/web-platform/meta/2dcontext/drawing-text-to-the-canvas/2d.text.draw.fill.maxWidth.negative.html.ini
testing/web-platform/meta/2dcontext/drawing-text-to-the-canvas/2d.text.draw.fill.maxWidth.zero.html.ini
testing/web-platform/meta/XMLHttpRequest/FormData-append.html.ini
testing/web-platform/meta/XMLHttpRequest/open-open-send.htm.ini
testing/web-platform/meta/XMLHttpRequest/open-open-sync-send.htm.ini
testing/web-platform/meta/XMLHttpRequest/open-send-open.htm.ini
testing/web-platform/meta/XMLHttpRequest/open-sync-open-send.htm.ini
testing/web-platform/meta/XMLHttpRequest/open-url-multi-window-3.htm.ini
testing/web-platform/meta/XMLHttpRequest/send-entity-body-get-head-async.htm.ini
testing/web-platform/meta/XMLHttpRequest/send-entity-body-get-head.htm.ini
testing/web-platform/meta/fetch/nosniff/image.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/script-language-type.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-http/fetch-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-http/fetch-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-http/fetch-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-http/iframe-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-http/iframe-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-http/iframe-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-http/script-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-http/script-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-http/script-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-http/xhr-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-http/xhr-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-http/xhr-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/fetch-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/fetch-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/fetch-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/iframe-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/iframe-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/iframe-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/script-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/script-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/script-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/xhr-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/xhr-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/cross-origin/http-https/xhr-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/fetch-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/fetch-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/fetch-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/iframe-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/iframe-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/iframe-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/script-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/script-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/script-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/xhr-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/xhr-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-http/xhr-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-https/fetch-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-https/fetch-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-https/fetch-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-https/iframe-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-https/iframe-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-https/iframe-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-https/script-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-https/script-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-https/script-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-https/xhr-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-https/xhr-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/no-referrer/http-rp/same-origin/http-https/xhr-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-http/fetch-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-http/fetch-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-http/fetch-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-http/iframe-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-http/iframe-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-http/iframe-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-http/script-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-http/script-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-http/script-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-http/xhr-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-http/xhr-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-http/xhr-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-https/fetch-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-https/fetch-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-https/fetch-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-https/iframe-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-https/iframe-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-https/iframe-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-https/script-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-https/script-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-https/script-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-https/xhr-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-https/xhr-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/cross-origin/http-https/xhr-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-http/fetch-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-http/fetch-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-http/fetch-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-http/iframe-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-http/iframe-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-http/iframe-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-http/script-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-http/script-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-http/script-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-http/xhr-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-http/xhr-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-http/xhr-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-https/fetch-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-https/fetch-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-https/fetch-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-https/iframe-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-https/iframe-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-https/iframe-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-https/script-tag/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-https/script-tag/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-https/script-tag/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-https/xhr-request/generic.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-https/xhr-request/generic.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-only/http-rp/same-origin/http-https/xhr-request/generic.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/fetch-request/cross-origin.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/fetch-request/cross-origin.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/fetch-request/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/iframe-tag/cross-origin.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/iframe-tag/cross-origin.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/iframe-tag/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/script-tag/cross-origin.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/script-tag/cross-origin.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/script-tag/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/xhr-request/cross-origin.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/xhr-request/cross-origin.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-http/xhr-request/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/fetch-request/cross-origin.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/fetch-request/cross-origin.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/fetch-request/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/iframe-tag/cross-origin.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/iframe-tag/cross-origin.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/iframe-tag/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/script-tag/cross-origin.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/script-tag/cross-origin.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/script-tag/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/xhr-request/cross-origin.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/xhr-request/cross-origin.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/cross-origin/http-https/xhr-request/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/fetch-request/same-origin-insecure.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/iframe-tag/same-origin-insecure.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/script-tag/same-origin-insecure.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-http/xhr-request/same-origin-insecure.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/fetch-request/same-origin-downgrade.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/fetch-request/same-origin-downgrade.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/fetch-request/same-origin-downgrade.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/fetch-request/same-origin-insecure.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/fetch-request/same-origin-upgrade.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/fetch-request/same-origin-upgrade.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/fetch-request/same-origin-upgrade.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/iframe-tag/same-origin-downgrade.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/iframe-tag/same-origin-downgrade.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/iframe-tag/same-origin-downgrade.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/iframe-tag/same-origin-insecure.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/iframe-tag/same-origin-upgrade.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/iframe-tag/same-origin-upgrade.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/iframe-tag/same-origin-upgrade.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/script-tag/same-origin-downgrade.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/script-tag/same-origin-downgrade.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/script-tag/same-origin-downgrade.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/script-tag/same-origin-insecure.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/script-tag/same-origin-upgrade.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/script-tag/same-origin-upgrade.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/script-tag/same-origin-upgrade.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/xhr-request/same-origin-downgrade.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/xhr-request/same-origin-downgrade.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/xhr-request/same-origin-downgrade.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/xhr-request/same-origin-insecure.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/xhr-request/same-origin-upgrade.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/xhr-request/same-origin-upgrade.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-rp/same-origin/http-https/xhr-request/same-origin-upgrade.swap-origin-redirect.http.html.ini
toolkit/components/extensions/Extension.jsm
toolkit/xre/MacQuirks.h
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -53,21 +53,18 @@ pageInfoTreeView.prototype = {
     this.rowCountChanged(this.rows - 1, 1);
     if (this.selection.count == 0 && this.rowCount && !gImageElement) {
       this.selection.select(0);
     }
   },
 
   addRows: function(rows)
   {
-    this.data = this.data.concat(rows);
-    this.rowCountChanged(this.rows, rows.length);
-    this.rows = this.data.length;
-    if (this.selection.count == 0 && this.rowCount && !gImageElement) {
-      this.selection.select(0);
+    for (let row of rows) {
+      this.addRow(row);
     }
   },
 
   rowCountChanged: function(index, count)
   {
     this.tree.rowCountChanged(index, count);
   },
 
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -3780,21 +3780,16 @@
              * are hidden). This checks to make sure all conditions are
              * satisfied, and then records the tab switch as finished.
              */
             maybeFinishTabSwitch: function () {
               if (this.switchInProgress && this.requestedTab &&
                   this.getTabState(this.requestedTab) == this.STATE_LOADED) {
                 // After this point the tab has switched from the content thread's point of view.
                 // The changes will be visible after the next refresh driver tick + composite.
-                let event = new CustomEvent("TabSwitched", {
-                  bubbles: true,
-                  cancelable: true
-                });
-                this.tabbrowser.dispatchEvent(event);
                 let time = TelemetryStopwatch.timeElapsed("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
                 if (time != -1) {
                   TelemetryStopwatch.finish("FX_TAB_SWITCH_TOTAL_E10S_MS", window);
                   this.log("DEBUG: tab switch time = " + time);
                   this.addMarker("AsyncTabSwitch:Finish");
                 }
                 this.switchInProgress = false;
               }
--- a/browser/base/content/test/general/browser_parsable_css.js
+++ b/browser/base/content/test/general/browser_parsable_css.js
@@ -18,17 +18,17 @@ const kWhitelist = [
   // Tracked in bug 1004428.
   {sourceName: /aboutaccounts\/(main|normalize)\.css$/i},
   // TokBox SDK assets, see bug 1032469.
   {sourceName: /loop\/.*sdk-content\/.*\.css$/i},
   // Loop standalone client CSS uses placeholder cross browser pseudo-element
   {sourceName: /loop\/.*\.css$/i,
    errorMessage: /Unknown pseudo-class.*placeholder/i},
   {sourceName: /loop\/.*shared\/css\/common.css$/i,
-   errorMessage: /Unknown property 'user-select'/i},
+   errorMessage: /Unknown property .user-select./i},
   // Highlighter CSS uses a UA-only pseudo-class, see bug 985597.
   {sourceName: /highlighters\.css$/i,
    errorMessage: /Unknown pseudo-class.*moz-native-anonymous/i},
   // Responsive Design Mode CSS uses a UA-only pseudo-class, see Bug 1241714.
   {sourceName: /responsive-ua\.css$/i,
    errorMessage: /Unknown pseudo-class.*moz-dropdown-list/i},
 ];
 
--- a/browser/base/content/test/general/parsingTestHelpers.jsm
+++ b/browser/base/content/test/general/parsingTestHelpers.jsm
@@ -65,17 +65,18 @@ function iterateOverPath(path, extension
       let file = parentDir.clone();
       file.append(entry.name);
       // the build system might leave dead symlinks hanging around, which are
       // returned as part of the directory iterator, but don't actually exist:
       if (file.exists()) {
         let uriSpec = getURLForFile(file);
         files.push(Services.io.newURI(uriSpec, null, null));
       }
-    } else if (entry.name.endsWith(".ja") || entry.name.endsWith(".jar")) {
+    } else if (entry.name.endsWith(".ja") || entry.name.endsWith(".jar") ||
+               entry.name.endsWith(".zip") || entry.name.endsWith(".xpi")) {
       let file = parentDir.clone();
       file.append(entry.name);
       for (let extension of extensions) {
         let jarEntryIterator = generateEntriesFromJarFile(file, extension);
         files.push(...jarEntryIterator);
       }
     }
   };
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -499,54 +499,51 @@ const CustomizableWidgets = [
       });
       return item;
     },
   }, {
     id: "privatebrowsing-button",
     shortcutId: "key_privatebrowsing",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(e) {
-      let win = e.target && e.target.ownerGlobal;
-      if (win && typeof win.OpenBrowserWindow == "function") {
+      let win = e.target.ownerGlobal;
+      if (typeof win.OpenBrowserWindow == "function") {
         win.OpenBrowserWindow({private: true});
       }
     }
   }, {
     id: "save-page-button",
     shortcutId: "key_savePage",
     tooltiptext: "save-page-button.tooltiptext3",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
-      let win = aEvent.target &&
-                aEvent.target.ownerGlobal;
-      if (win && typeof win.saveBrowser == "function") {
+      let win = aEvent.target.ownerGlobal;
+      if (typeof win.saveBrowser == "function") {
         win.saveBrowser(win.gBrowser.selectedBrowser);
       }
     }
   }, {
     id: "find-button",
     shortcutId: "key_find",
     tooltiptext: "find-button.tooltiptext3",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
-      let win = aEvent.target &&
-                aEvent.target.ownerGlobal;
-      if (win && win.gFindBar) {
+      let win = aEvent.target.ownerGlobal;
+      if (win.gFindBar) {
         win.gFindBar.onFindCommand();
       }
     }
   }, {
     id: "open-file-button",
     shortcutId: "openFileKb",
     tooltiptext: "open-file-button.tooltiptext3",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
-      let win = aEvent.target
-                && aEvent.target.ownerGlobal;
-      if (win && typeof win.BrowserOpenFileWindow == "function") {
+      let win = aEvent.target.ownerGlobal;
+      if (typeof win.BrowserOpenFileWindow == "function") {
         win.BrowserOpenFileWindow();
       }
     }
   }, {
     id: "sidebar-button",
     type: "view",
     viewId: "PanelUI-sidebar",
     tooltiptext: "sidebar-button.tooltiptext2",
@@ -612,19 +609,18 @@ const CustomizableWidgets = [
       return node;
     }
   }, {
     id: "add-ons-button",
     shortcutId: "key_openAddons",
     tooltiptext: "add-ons-button.tooltiptext3",
     defaultArea: CustomizableUI.AREA_PANEL,
     onCommand: function(aEvent) {
-      let win = aEvent.target &&
-                aEvent.target.ownerGlobal;
-      if (win && typeof win.BrowserOpenAddonsMgr == "function") {
+      let win = aEvent.target.ownerGlobal;
+      if (typeof win.BrowserOpenAddonsMgr == "function") {
         win.BrowserOpenAddonsMgr();
       }
     }
   }, {
     id: "zoom-controls",
     type: "custom",
     tooltiptext: "zoom-controls.tooltiptext2",
     defaultArea: CustomizableUI.AREA_PANEL,
@@ -1149,19 +1145,18 @@ if (Services.prefs.getBoolPref("privacy.
     }
   });
 }
 
 let preferencesButton = {
   id: "preferences-button",
   defaultArea: CustomizableUI.AREA_PANEL,
   onCommand: function(aEvent) {
-    let win = aEvent.target &&
-              aEvent.target.ownerGlobal;
-    if (win && typeof win.openPreferences == "function") {
+    let win = aEvent.target.ownerGlobal;
+    if (typeof win.openPreferences == "function") {
       win.openPreferences();
     }
   }
 };
 if (AppConstants.platform == "win") {
   preferencesButton.label = "preferences-button.labelWin";
   preferencesButton.tooltiptext = "preferences-button.tooltipWin2";
 } else if (AppConstants.platform == "macosx") {
--- a/browser/components/downloads/content/allDownloadsViewOverlay.js
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.js
@@ -297,18 +297,21 @@ HistoryDownloadElementShell.prototype = 
     return status;
   },
 
   onStateChanged() {
     this._updateState();
 
     if (this.element.selected) {
       goUpdateDownloadCommands();
+    } else {
+      // If a state change occurs in an item that is not currently selected,
+      // this is the only command that may be affected.
+      goUpdateCommand("downloadsCmd_clearDownloads");
     }
-    goUpdateCommand("downloadsCmd_clearDownloads");
   },
 
   onChanged() {
     // This cannot be placed within onStateChanged because
     // when a download goes from hasBlockedData to !hasBlockedData
     // it will still remain in the same state.
     this.element.classList.toggle("temporary-block",
                                   !!this.download.hasBlockedData);
@@ -1131,18 +1134,17 @@ DownloadsPlacesView.prototype = {
 
   onDownloadRemoved(download) {
     this._removeSessionDownloadFromView(download);
   },
 
   // nsIController
   supportsCommand(aCommand) {
     // Firstly, determine if this is a command that we can handle.
-    if (!aCommand.startsWith("cmd_") &&
-        !aCommand.startsWith("downloadsCmd_")) {
+    if (!DownloadsViewUI.isCommandName(aCommand)) {
       return false;
     }
     if (!(aCommand in this) &&
         !(aCommand in HistoryDownloadElementShell.prototype)) {
       return false;
     }
     // If this function returns true, other controllers won't get a chance to
     // process the command even if isCommandEnabled returns false, so it's
@@ -1413,16 +1415,16 @@ for (let methodName of ["load", "applyFi
     throw new Error("|" + methodName +
                     "| is not implemented by the downloads view.");
   }
 }
 
 function goUpdateDownloadCommands() {
   function updateCommandsForObject(object) {
     for (let name in object) {
-      if (name.startsWith("cmd_") || name.startsWith("downloadsCmd_")) {
+      if (DownloadsViewUI.isCommandName(name)) {
         goUpdateCommand(name);
       }
     }
   }
-  updateCommandsForObject(this);
+  updateCommandsForObject(DownloadsPlacesView.prototype);
   updateCommandsForObject(HistoryDownloadElementShell.prototype);
 }
--- a/browser/components/downloads/content/downloads.css
+++ b/browser/components/downloads/content/downloads.css
@@ -172,31 +172,40 @@ richlistitem.download button {
 #downloadsPanel-mainView .download-state[state="8"] .downloadConfirmBlock,
 #downloadsPanel-mainView .download-state[state="8"] .downloadChooseUnblock,
 #downloadsPanel-mainView .download-state[state="8"] .downloadChooseOpen,
 #downloadsPanel-mainView .download-state[state="8"] .downloadRetry,
 #downloadsPanel-mainView .download-state[state="8"] .downloadShow {
   display: none;
 }
 
+/* Make the panel wide enough to show the download list items without improperly
+   truncating them. */
+#downloadsPanel-multiView > .panel-viewcontainer,
+#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack,
+#downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack > .panel-mainview {
+  overflow: visible;
+  max-width: unset;
+}
+
 /* Show the "show blocked info" button. */
 #downloadsPanel-mainView .download-state[state="8"] .downloadShowBlockedInfo {
   display: inline;
 }
 
 /** When the main view is showing... **/
 
 /* The subview should be off to the right and not visible at all. */
 #downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype=main] > .panel-subviews {
-  transform: translateX(100%);
+  transform: translateX(101%);
   transition: transform var(--panelui-subview-transition-duration);
 }
 
 #downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype=main] > .panel-subviews:-moz-locale-dir(rtl) {
-  transform: translateX(-100%);
+  transform: translateX(-101%);
 }
 
 /** When the subview is showing... **/
 
 /* Hide the buttons of all downloads except the one that triggered the
    subview. */
 #downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack[viewtype="subview"] .download-state:not([showingsubview]) .downloadButton {
   display: none;
--- a/browser/components/downloads/content/downloadsOverlay.xul
+++ b/browser/components/downloads/content/downloadsOverlay.xul
@@ -129,17 +129,17 @@
           <spacer flex="1"/>
           <vbox id="downloadsFooter">
             <hbox id="downloadsSummary"
                   align="center"
                   orient="horizontal"
                   onkeydown="DownloadsSummary.onKeyDown(event);"
                   onclick="DownloadsSummary.onClick(event);">
               <image class="downloadTypeIcon" />
-              <vbox>
+              <vbox id="downloadsSummaryChildBox">
                 <description id="downloadsSummaryDescription"
                              style="min-width: &downloadsSummary.minWidth2;"/>
                 <progressmeter id="downloadsSummaryProgress"
                                class="downloadProgress"
                                min="0"
                                max="100"
                                mode="normal" />
                 <description id="downloadsSummaryDetails"
--- a/browser/components/extensions/ext-commands.js
+++ b/browser/components/extensions/ext-commands.js
@@ -72,20 +72,22 @@ CommandList.prototype = {
   loadCommandsFromManifest(manifest) {
     let commands = new Map();
     // For Windows, chrome.runtime expects 'win' while chrome.commands
     // expects 'windows'.  We can special case this for now.
     let os = PlatformInfo.os == "win" ? "windows" : PlatformInfo.os;
     for (let name of Object.keys(manifest.commands)) {
       let command = manifest.commands[name];
       let shortcut = command.suggested_key[os] || command.suggested_key.default;
-      commands.set(name, {
-        description: command.description,
-        shortcut: shortcut.replace(/\s+/g, ""),
-      });
+      if (shortcut) {
+        commands.set(name, {
+          description: command.description,
+          shortcut: shortcut.replace(/\s+/g, ""),
+        });
+      }
     }
     return commands;
   },
 
   /**
    * Registers the commands to a document.
    * @param {ChromeWindow} window The XUL window to insert the Keyset.
    */
@@ -119,17 +121,17 @@ CommandList.prototype = {
     // and it is currently ignored when set to the empty string.
     keyElement.setAttribute("oncommand", "//");
 
     /* eslint-disable mozilla/balanced-listeners */
     // We remove all references to the key elements when the extension is shutdown,
     // therefore the listeners for these elements will be garbage collected.
     keyElement.addEventListener("command", (event) => {
       if (name == "_execute_page_action") {
-        let win = event.target.ownerGlobal;
+        let win = event.target.ownerDocument.defaultView;
         pageActionFor(this.extension).triggerAction(win);
       } else {
         this.emit("command", name);
       }
     });
     /* eslint-enable mozilla/balanced-listeners */
 
     return keyElement;
--- a/browser/components/extensions/test/browser/browser_ext_commands_onCommand.js
+++ b/browser/components/extensions/test/browser/browser_ext_commands_onCommand.js
@@ -1,98 +1,254 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
+Cu.import("resource://gre/modules/AppConstants.jsm");
+
 add_task(function* test_user_defined_commands() {
+  const testCommands = [
+    // Ctrl Shortcuts
+    {
+      name: "toggle-ctrl-a",
+      shortcut: "Ctrl+A",
+      key: "A",
+      modifiers: {
+        accelKey: true,
+      },
+    },
+    {
+      name: "toggle-ctrl-up",
+      shortcut: "Ctrl+Up",
+      key: "VK_UP",
+      modifiers: {
+        accelKey: true,
+      },
+    },
+    // Alt Shortcuts
+    {
+      name: "toggle-alt-1",
+      shortcut: "Alt+1",
+      key: "1",
+      modifiers: {
+        altKey: true,
+      },
+    },
+    {
+      name: "toggle-alt-a",
+      shortcut: "Alt+A",
+      key: "A",
+      modifiers: {
+        altKey: true,
+      },
+    },
+    {
+      name: "toggle-alt-down",
+      shortcut: "Alt+Down",
+      key: "VK_DOWN",
+      modifiers: {
+        altKey: true,
+      },
+    },
+    // Mac Shortcuts
+    {
+      name: "toggle-command-shift-page-up",
+      shortcutMac: "Command+Shift+PageUp",
+      key: "VK_PAGE_UP",
+      modifiers: {
+        accelKey: true,
+        shiftKey: true,
+      },
+    },
+    {
+      name: "toggle-mac-control-b",
+      shortcut: "Ctrl+B",
+      shortcutMac: "MacCtrl+B",
+      key: "B",
+      modifiers: {
+        ctrlKey: true,
+      },
+    },
+    // Ctrl+Shift Shortcuts
+    {
+      name: "toggle-ctrl-shift-1",
+      shortcut: "Ctrl+Shift+1",
+      key: "1",
+      modifiers: {
+        accelKey: true,
+        shiftKey: true,
+      },
+    },
+    {
+      name: "toggle-ctrl-shift-i",
+      shortcut: "Ctrl+Shift+I",
+      key: "I",
+      modifiers: {
+        accelKey: true,
+        shiftKey: true,
+      },
+    },
+    {
+      name: "toggle-ctrl-shift-left",
+      shortcut: "Ctrl+Shift+Left",
+      key: "VK_LEFT",
+      modifiers: {
+        accelKey: true,
+        shiftKey: true,
+      },
+    },
+    // Alt+Shift Shortcuts
+    {
+      name: "toggle-alt-shift-1",
+      shortcut: "Alt+Shift+1",
+      key: "1",
+      modifiers: {
+        altKey: true,
+        shiftKey: true,
+      },
+    },
+    {
+      name: "toggle-alt-shift-a",
+      shortcut: "Alt+Shift+A",
+      key: "A",
+      modifiers: {
+        altKey: true,
+        shiftKey: true,
+      },
+    },
+    {
+      name: "toggle-alt-shift-right",
+      shortcut: "Alt+Shift+Right",
+      key: "VK_RIGHT",
+      modifiers: {
+        altKey: true,
+        shiftKey: true,
+      },
+    },
+    // Misc Shortcuts
+    {
+      name: "unrecognized-property-name",
+      shortcut: "Alt+Shift+3",
+      key: "3",
+      modifiers: {
+        altKey: true,
+        shiftKey: true,
+      },
+      unrecognized_property: "with-a-random-value",
+    },
+    {
+      name: "spaces-in-shortcut-name",
+      shortcut: "  Alt + Shift + 2  ",
+      key: "2",
+      modifiers: {
+        altKey: true,
+        shiftKey: true,
+      },
+    },
+  ];
+
   // Create a window before the extension is loaded.
   let win1 = yield BrowserTestUtils.openNewBrowserWindow();
   yield BrowserTestUtils.loadURI(win1.gBrowser.selectedBrowser, "about:robots");
   yield BrowserTestUtils.browserLoaded(win1.gBrowser.selectedBrowser);
 
+  let commands = {};
+  let isMac = AppConstants.platform == "macosx";
+  let totalMacOnlyCommands = 0;
+
+  for (let testCommand of testCommands) {
+    let command = {
+      suggested_key: {},
+    };
+
+    if (testCommand.shortcut) {
+      command.suggested_key.default = testCommand.shortcut;
+    }
+
+    if (testCommand.shortcutMac) {
+      command.suggested_key.mac = testCommand.shortcutMac;
+    }
+
+    if (testCommand.shortcutMac && !testCommand.shortcut) {
+      totalMacOnlyCommands++;
+    }
+
+    if (testCommand.unrecognized_property) {
+      command.unrecognized_property = testCommand.unrecognized_property;
+    }
+
+    commands[testCommand.name] = command;
+  }
+
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "commands": {
-        "toggle-feature-using-alt-shift-3": {
-          "suggested_key": {
-            "default": "Alt+Shift+3",
-          },
-        },
-        "toggle-feature-using-alt-shift-comma": {
-          "suggested_key": {
-            "default": "Alt+Shift+Comma",
-          },
-          "unrecognized_property": "with-a-random-value",
-        },
-        "toggle-feature-with-whitespace-in-suggested-key": {
-          "suggested_key": {
-            "default": "  Alt + Shift + 2  ",
-          },
-        },
-      },
+      "commands": commands,
     },
 
     background: function() {
       browser.commands.onCommand.addListener(commandName => {
         browser.test.sendMessage("oncommand", commandName);
       });
       browser.test.sendMessage("ready");
     },
   });
 
-
   SimpleTest.waitForExplicitFinish();
   let waitForConsole = new Promise(resolve => {
     SimpleTest.monitorConsole(resolve, [{
       message: /Reading manifest: Error processing commands.*.unrecognized_property: An unexpected property was found/,
     }]);
   });
 
   yield extension.startup();
   yield extension.awaitMessage("ready");
 
+  function* runTest() {
+    for (let testCommand of testCommands) {
+      if (testCommand.shortcutMac && !isMac) {
+        continue;
+      }
+      EventUtils.synthesizeKey(testCommand.key, testCommand.modifiers);
+      let message = yield extension.awaitMessage("oncommand");
+      is(message, testCommand.name, "Expected onCommand listener to fire with the correct command name");
+    }
+  }
+
   // Create another window after the extension is loaded.
   let win2 = yield BrowserTestUtils.openNewBrowserWindow();
   yield BrowserTestUtils.loadURI(win2.gBrowser.selectedBrowser, "about:config");
   yield BrowserTestUtils.browserLoaded(win2.gBrowser.selectedBrowser);
 
+  let totalTestCommands = Object.keys(testCommands).length;
+  let expectedCommandsRegistered = isMac ? totalTestCommands : totalTestCommands - totalMacOnlyCommands;
+
   // Confirm the keysets have been added to both windows.
   let keysetID = `ext-keyset-id-${makeWidgetId(extension.id)}`;
   let keyset = win1.document.getElementById(keysetID);
   ok(keyset != null, "Expected keyset to exist");
-  is(keyset.childNodes.length, 3, "Expected keyset to have 3 children");
+  is(keyset.childNodes.length, expectedCommandsRegistered, "Expected keyset to have the correct number of children");
 
   keyset = win2.document.getElementById(keysetID);
   ok(keyset != null, "Expected keyset to exist");
-  is(keyset.childNodes.length, 3, "Expected keyset to have 3 children");
+  is(keyset.childNodes.length, expectedCommandsRegistered, "Expected keyset to have the correct number of children");
 
   // Confirm that the commands are registered to both windows.
   yield focusWindow(win1);
-  EventUtils.synthesizeKey("3", {altKey: true, shiftKey: true});
-  let message = yield extension.awaitMessage("oncommand");
-  is(message, "toggle-feature-using-alt-shift-3", "Expected onCommand listener to fire with correct message");
+  yield runTest();
 
   yield focusWindow(win2);
-  EventUtils.synthesizeKey("VK_COMMA", {altKey: true, shiftKey: true});
-  message = yield extension.awaitMessage("oncommand");
-  is(message, "toggle-feature-using-alt-shift-comma", "Expected onCommand listener to fire with correct message");
-
-  EventUtils.synthesizeKey("2", {altKey: true, shiftKey: true});
-  message = yield extension.awaitMessage("oncommand");
-  is(message, "toggle-feature-with-whitespace-in-suggested-key", "Expected onCommand listener to fire with correct message");
+  yield runTest();
 
   yield extension.unload();
 
   // Confirm that the keysets have been removed from both windows after the extension is unloaded.
   keyset = win1.document.getElementById(keysetID);
   is(keyset, null, "Expected keyset to be removed from the window");
 
   keyset = win2.document.getElementById(keysetID);
   is(keyset, null, "Expected keyset to be removed from the window");
 
   yield BrowserTestUtils.closeWindow(win1);
   yield BrowserTestUtils.closeWindow(win2);
 
   SimpleTest.endMonitorConsole();
   yield waitForConsole;
 });
-
-
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -120,24 +120,23 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "SimpleServiceDiscovery",
                                   "resource://gre/modules/SimpleServiceDiscovery.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
                                   "resource:///modules/ContentSearch.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "TabCrashHandler",
                                   "resource:///modules/ContentCrashHandlers.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
+                                  "resource://gre/modules/PluralForm.jsm");
 if (AppConstants.MOZ_CRASHREPORTER) {
   XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
                                     "resource:///modules/ContentCrashHandlers.jsm");
   XPCOMUtils.defineLazyModuleGetter(this, "CrashSubmit",
                                     "resource://gre/modules/CrashSubmit.jsm");
-  XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
-                                    "resource://gre/modules/PluralForm.jsm");
-
 }
 
 XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
   return Services.strings.createBundle('chrome://branding/locale/brand.properties');
 });
 
 XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
   return Services.strings.createBundle('chrome://browser/locale/browser.properties');
@@ -2477,18 +2476,22 @@ BrowserGlue.prototype = {
       if (URIs.length == 1) {
         title = bundle.GetStringFromName("tabArrivingNotification.title");
         const pageTitle = URIs[0].title || firstTab.linkedBrowser.contentTitle
                           || URIs[0].uri;
         body = bundle.formatStringFromName("tabArrivingNotification.body", [pageTitle, deviceName], 2);
       } else {
         title = bundle.GetStringFromName("tabsArrivingNotification.title");
         const tabArrivingBody = URIs.every(URI => URI.clientId == URIs[0].clientId) ?
-                                "tabsArrivingNotification.body" : "tabsArrivingNotificationMultiple.body";
-        body = bundle.formatStringFromName(tabArrivingBody, [URIs.length, deviceName], 2);
+                                "unnamedTabsArrivingNotification.body" :
+                                "unnamedTabsArrivingNotificationMultiple.body";
+        body = bundle.GetStringFromName(tabArrivingBody);
+        body = PluralForm.get(URIs.length, body);
+        body = body.replace("#1", URIs.length);
+        body = body.replace("#2", deviceName);
       }
 
       const clickCallback = (subject, topic, data) => {
         if (topic == "alertclickcallback") {
           win.gBrowser.selectedTab = firstTab;
         }
       }
       AlertsService.showAlertNotification(null, title, body, true, null, clickCallback);
--- a/browser/components/sessionstore/content/content-sessionStore.js
+++ b/browser/components/sessionstore/content/content-sessionStore.js
@@ -365,21 +365,21 @@ var SessionHistoryListener = {
  */
 var ScrollPositionListener = {
   init: function () {
     addEventListener("scroll", this);
     gFrameTree.addObserver(this);
   },
 
   handleEvent: function (event) {
-    let frame = event.target && event.target.defaultView;
+    let frame = event.target.defaultView;
 
     // Don't collect scroll data for frames created at or after the load event
     // as SessionStore can't restore scroll data for those.
-    if (frame && gFrameTree.contains(frame)) {
+    if (gFrameTree.contains(frame)) {
       MessageQueue.push("scroll", () => this.collect());
     }
   },
 
   onFrameTreeCollected: function () {
     MessageQueue.push("scroll", () => this.collect());
   },
 
@@ -412,22 +412,21 @@ var ScrollPositionListener = {
 var FormDataListener = {
   init: function () {
     addEventListener("input", this, true);
     addEventListener("change", this, true);
     gFrameTree.addObserver(this);
   },
 
   handleEvent: function (event) {
-    let frame = event.target &&
-                event.target.ownerGlobal;
+    let frame = event.target.ownerGlobal;
 
     // Don't collect form data for frames created at or after the load event
     // as SessionStore can't restore form data for those.
-    if (frame && gFrameTree.contains(frame)) {
+    if (gFrameTree.contains(frame)) {
       MessageQueue.push("formdata", () => this.collect());
     }
   },
 
   onFrameTreeReset: function () {
     MessageQueue.push("formdata", () => null);
   },
 
--- a/browser/extensions/pocket/content/main.js
+++ b/browser/extensions/pocket/content/main.js
@@ -130,22 +130,17 @@ var pktUI = (function() {
     function showSignUp() {
         // AB test: Direct logged-out users to tab vs panel
         if (pktApi.getSignupPanelTabTestVariant() == 'tab')
         {
             let site = Services.prefs.getCharPref("extensions.pocket.site");
             openTabWithUrl('https://' + site + '/firefox_learnmore?src=ff_ext&s=ffi&t=buttonclick', true);
 
             // force the panel closed before it opens
-            // wrapped in setTimeout to avoid race condition after logging out
-            // if this test goes to 100%, we should move this logic up before
-            // the panel is actually opened
-            setTimeout(function() {
-                getPanel().hidePopup();
-            }, 0);
+            getPanel().hidePopup();
 
             return;
         }
 
         // Control: Show panel as normal
         getFirefoxAccountSignedInUser(function(userdata)
         {
             var fxasignedin = (typeof userdata == 'object' && userdata !== null) ? '1' : '0';
@@ -327,17 +322,19 @@ var pktUI = (function() {
           iframe.style.height = options.height + "px";
         }
     }
 
     /**
      * Called when the signup and saved panel was hidden
      */
     function panelDidHide() {
-
+        // clear the onShow and onHide values
+        _currentPanelDidShow = null;
+        _currentPanelDidHide = null;
     }
 
     /**
      * Register all of the messages needed for the panels
      */
     function registerEventMessages() {
         var iframe = getPanelFrame();
 
--- a/browser/locales/en-US/chrome/browser/accounts.properties
+++ b/browser/locales/en-US/chrome/browser/accounts.properties
@@ -42,14 +42,18 @@ sendTabToAllDevices.menuitem = All Devic
 # LOCALIZATION NOTE (tabArrivingNotification.title, tabArrivingNotification.body,
 # tabsArrivingNotification.title, tabsArrivingNotification.body)
 # These strings are used in a notification shown when we're opening tab(s) another device sent us to display.
 tabArrivingNotification.title = Tab received
 # LOCALIZATION NOTE (tabArrivingNotification.body) %1 is the title of the tab and %2 is the device name.
 tabArrivingNotification.body = “%1$S” has arrived from %2$S.
 
 tabsArrivingNotification.title = Multiple tabs received
-# LOCALIZATION NOTE (tabsArrivingNotification.body) %1 is the number of tabs received and %2 is the device name.
-tabsArrivingNotification.body = %1$S tabs have arrived from %2$S.
-# LOCALIZATION NOTE (tabsArrivingNotificationMultiple.body)
-# This string is used in a notification shown when we're opening tab(s) that several devices sent us to display.
-# %S is the number of tabs received
-tabsArrivingNotificationMultiple.body = %S tabs have arrived from your connected devices.
+# LOCALIZATION NOTE (unnamedTabsArrivingNotification.body):
+# Semi-colon list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of tabs received and #2 is the device name.
+unnamedTabsArrivingNotification.body = #1 tab has arrived from #2.;#1 tabs have arrived from #2.
+# LOCALIZATION NOTE (unnamedTabsArrivingNotificationMultiple.body):
+# Semi-colon list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of tabs received.
+unnamedTabsArrivingNotificationMultiple.body = #1 tab has arrived from your connected devices.;#1 tabs have arrived from your connected devices.
--- a/browser/themes/shared/addons/addon-install-anchor.svg
+++ b/browser/themes/shared/addons/addon-install-anchor.svg
@@ -1,19 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
      width="16" height="16" viewBox="0 0 16 16">
   <defs>
-    <style>
-      use:not(:target) {
-        display: none;
-      }
-      .style-icon-notification {
-        fill: #999;
-      }
-    </style>
     <path id="shape-notifications-addons" d="M10,15c0.5,0,1-0.4,1-1v-3c0,0,0-0.8,0.8-0.8c0.6,0,0.6,0.8,1.8,0.8c0.6,0,1.5-0.2,1.5-2c0-1.8-0.9-2-1.5-2 c-1.1,0-1.1,0.7-1.8,0.7C11,7.7,11,7,11,7V6c0-0.6-0.5-1-1-1H8c0,0-0.8,0-0.8-0.8C7.2,3.6,8,3.6,8,2.5C8,1.9,7.8,1,6,1 C4.2,1,4,1.9,4,2.5c0,1.1,0.8,1.1,0.8,1.8C4.8,5,4,5,4,5H2C1.5,5,1,5.4,1,6l0,1.5c0,0-0.1,1,1.1,1c0.8,0,0.9-1,1.9-1 C4.5,7.4,5,8,5,9c0,1-0.5,1.6-1,1.6c-1,0-1.1-1.1-1.9-1.1C0.9,9.5,1,10.8,1,10.8V14c0,0.6,0.5,1,1,1l2.6,0c0,0,1.1,0,1.1-1 c0-0.8-1-0.1-1-1.1c0-0.5,0.7-1.2,1.8-1.2s1.8,0.7,1.8,1.2c0,1-1.1,0.3-1.1,1.1c0,1,1.2,1,1.2,1H10z"/>
   </defs>
-  <use id="default" xlink:href="#shape-notifications-addons" class="style-icon-notification"/>
+  <use id="default" xlink:href="#shape-notifications-addons" />
 </svg>
--- a/browser/themes/shared/downloads/downloads.inc.css
+++ b/browser/themes/shared/downloads/downloads.inc.css
@@ -27,21 +27,33 @@
 #emptyDownloads {
   padding: 10px 20px;
   /* The panel can be wider than this description after the blocked subview is
      shown, so center the text. */
   text-align: center;
 }
 
 #downloadsSummary {
-  padding: 8px 38px 8px 12px;
+  --summary-padding-end: 38px;
+  --summary-padding-start: 12px;
+  padding: 8px var(--summary-padding-end) 8px var(--summary-padding-start);
   cursor: pointer;
   -moz-user-focus: normal;
 }
 
+#downloadsSummary:-moz-locale-dir(rtl) {
+  padding-right: var(--summary-padding-start);
+  padding-left: var(--summary-padding-end);
+}
+
+#downloadsSummaryChildBox {
+  -moz-margin-start: var(--summary-padding-start);
+  -moz-margin-end: var(--summary-padding-end);
+}
+
 #downloadsSummary > .downloadTypeIcon {
   list-style-image: url("chrome://browser/skin/downloads/download-summary.png");
 }
 
 %ifdef XP_MACOSX
 @media (min-resolution: 2dppx) {
   #downloadsSummary > .downloadTypeIcon {
     list-style-image: url("chrome://browser/skin/downloads/download-summary@2x.png");
@@ -82,17 +94,17 @@ richlistitem[type="download"] {
 richlistitem[type="download"]:first-child {
   border-top: 1px solid transparent;
 }
 
 richlistitem[type="download"]:last-child {
   border-bottom: 1px solid transparent;
 }
 
-.downloadStackIcon {
+.downloadTypeIcon {
   --inline-offset: 8px;
   --block-offset: 4px;
   --icon-size: 32px;
 }
 
 .downloadTypeIcon {
   margin-inline-end: 8px;
   /* Prevent flickering when changing states. */
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -61,17 +61,17 @@
   skin/classic/browser/heartbeat-star-lit.svg                  (../shared/heartbeat-star-lit.svg)
   skin/classic/browser/heartbeat-star-off.svg                  (../shared/heartbeat-star-off.svg)
   skin/classic/browser/identity-icon.svg                       (../shared/identity-block/identity-icon.svg)
   skin/classic/browser/identity-not-secure.svg                 (../shared/identity-block/identity-not-secure.svg)
   skin/classic/browser/identity-secure.svg                     (../shared/identity-block/identity-secure.svg)
   skin/classic/browser/identity-mixed-passive-loaded.svg       (../shared/identity-block/identity-mixed-passive-loaded.svg)
   skin/classic/browser/identity-mixed-active-loaded.svg        (../shared/identity-block/identity-mixed-active-loaded.svg)
   skin/classic/browser/info.svg                                (../shared/info.svg)
-  skin/classic/browser/permissions.svg                         (../shared/permissions.svg)
+  skin/classic/browser/notification-icons.svg                  (../shared/notification-icons.svg)
   skin/classic/browser/tracking-protection-16.svg              (../shared/identity-block/tracking-protection-16.svg)
   skin/classic/browser/tracking-protection-disabled-16.svg     (../shared/identity-block/tracking-protection-disabled-16.svg)
   skin/classic/browser/newtab/close.png                        (../shared/newtab/close.png)
   skin/classic/browser/newtab/controls.svg                     (../shared/newtab/controls.svg)
   skin/classic/browser/newtab/whimsycorn.png                   (../shared/newtab/whimsycorn.png)
   skin/classic/browser/preferences/in-content/favicon.ico      (../shared/incontentprefs/favicon.ico)
   skin/classic/browser/preferences/in-content/icons.svg        (../shared/incontentprefs/icons.svg)
   skin/classic/browser/preferences/in-content/search.css       (../shared/incontentprefs/search.css)
@@ -122,22 +122,16 @@
   skin/classic/browser/session-restore.svg                     (../shared/incontent-icons/session-restore.svg)
   skin/classic/browser/tab-crashed.svg                         (../shared/incontent-icons/tab-crashed.svg)
   skin/classic/browser/favicon-search-16.svg                   (../shared/favicon-search-16.svg)
   skin/classic/browser/icon-search-64.svg                      (../shared/incontent-icons/icon-search-64.svg)
   skin/classic/browser/welcome-back.svg                        (../shared/incontent-icons/welcome-back.svg)
   skin/classic/browser/reader-tour.png                         (../shared/reader/reader-tour.png)
   skin/classic/browser/reader-tour@2x.png                      (../shared/reader/reader-tour@2x.png)
   skin/classic/browser/readerMode.svg                          (../shared/reader/readerMode.svg)
-  skin/classic/browser/notification-pluginNormal.png           (../shared/plugins/notification-pluginNormal.png)
-  skin/classic/browser/notification-pluginNormal@2x.png        (../shared/plugins/notification-pluginNormal@2x.png)
-  skin/classic/browser/notification-pluginAlert.png            (../shared/plugins/notification-pluginAlert.png)
-  skin/classic/browser/notification-pluginAlert@2x.png         (../shared/plugins/notification-pluginAlert@2x.png)
-  skin/classic/browser/notification-pluginBlocked.png          (../shared/plugins/notification-pluginBlocked.png)
-  skin/classic/browser/notification-pluginBlocked@2x.png       (../shared/plugins/notification-pluginBlocked@2x.png)
   skin/classic/browser/webRTC-camera-white-16.png              (../shared/webrtc/camera-white-16.png)
   skin/classic/browser/webRTC-microphone-white-16.png          (../shared/webrtc/microphone-white-16.png)
   skin/classic/browser/webRTC-screen-white-16.png              (../shared/webrtc/screen-white-16.png)
   skin/classic/browser/panic-panel/header.png                  (../shared/panic-panel/header.png)
   skin/classic/browser/panic-panel/header@2x.png               (../shared/panic-panel/header@2x.png)
   skin/classic/browser/panic-panel/header-small.png            (../shared/panic-panel/header-small.png)
   skin/classic/browser/panic-panel/header-small@2x.png         (../shared/panic-panel/header-small@2x.png)
   skin/classic/browser/panic-panel/icons.png                   (../shared/panic-panel/icons.png)
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -36,27 +36,33 @@
 }
 
 .popup-notification-icon {
   width: 64px;
   height: 64px;
   margin-inline-end: 10px;
 }
 
+#notification-popup-box > .notification-anchor-icon:hover {
+  fill: #606060;
+}
+
 /* INDIVIDUAL NOTIFICATIONS */
 
 /* For the moment we apply the color filter only on the icons listed here.
    The first two selectors are used by socialchat.xml (bug 1275558). */
 .webRTC-sharingDevices-notification-icon,
 .webRTC-sharingMicrophone-notification-icon,
 .camera-icon,
 .geo-icon,
 .indexedDB-icon,
+.install-icon,
 .login-icon,
 .microphone-icon,
+.plugin-icon,
 .pointerLock-icon,
 .popup-icon,
 .screen-icon,
 .desktop-notification-icon,
 .popup-notification-icon[popupid="geolocation"],
 .popup-notification-icon[popupid="indexedDB-permissions-prompt"],
 .popup-notification-icon[popupid="password"],
 .popup-notification-icon[popupid="pointerLock"],
@@ -76,124 +82,124 @@
 .webRTC-sharingDevices-notification-icon,
 .webRTC-sharingMicrophone-notification-icon,
 .in-use {
   fill: #fea01b;
 }
 
 .popup-notification-icon[popupid="web-notifications"],
 .desktop-notification-icon {
-  list-style-image: url(chrome://browser/skin/permissions.svg#desktop-notification);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#desktop-notification);
 }
 
 .desktop-notification-icon.blocked {
-  list-style-image: url(chrome://browser/skin/permissions.svg#desktop-notification-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#desktop-notification-blocked);
 }
 
 .geo-icon {
 %ifdef XP_MACOSX
-  list-style-image: url(chrome://browser/skin/permissions.svg#geo-osx);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-osx);
 %elif defined(MOZ_WIDGET_GTK)
-  list-style-image: url(chrome://browser/skin/permissions.svg#geo-linux);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-linux);
 %else
-  list-style-image: url(chrome://browser/skin/permissions.svg#geo-windows);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-windows);
 %endif
 }
 
 .geo-icon.blocked {
 %ifdef XP_MACOSX
-  list-style-image: url(chrome://browser/skin/permissions.svg#geo-osx-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-osx-blocked);
 %elif defined(MOZ_WIDGET_GTK)
-  list-style-image: url(chrome://browser/skin/permissions.svg#geo-linux-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-linux-blocked);
 %else
-  list-style-image: url(chrome://browser/skin/permissions.svg#geo-windows-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-windows-blocked);
 %endif
 }
 
 .popup-notification-icon[popupid="geolocation"] {
 %ifdef XP_MACOSX
-  list-style-image: url(chrome://browser/skin/permissions.svg#geo-osx);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-osx);
 %elif defined(MOZ_WIDGET_GTK)
-  list-style-image: url(chrome://browser/skin/permissions.svg#geo-linux-detailed);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-linux-detailed);
 %else
-  list-style-image: url(chrome://browser/skin/permissions.svg#geo-windows-detailed);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#geo-windows-detailed);
 %endif
 }
 
 .popup-notification-icon[popupid="indexedDB-permissions-prompt"],
 .indexedDB-icon {
-  list-style-image: url(chrome://browser/skin/permissions.svg#indexedDB);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#indexedDB);
 }
 
 .indexedDB-icon.blocked {
-  list-style-image: url(chrome://browser/skin/permissions.svg#indexedDB-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#indexedDB-blocked);
 }
 
 .login-icon {
-  list-style-image: url(chrome://browser/skin/permissions.svg#login);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#login);
 }
 
 .popup-notification-icon[popupid="password"] {
-  list-style-image: url(chrome://browser/skin/permissions.svg#login-detailed);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#login-detailed);
 }
 
 #login-fill-notification-icon {
   /* Temporary solution until the capture and fill doorhangers are unified. */
   transform: scaleX(-1);
 }
 
 /* The first selector is used by socialchat.xml (bug 1275558). */
 .webRTC-sharingDevices-notification-icon,
 .camera-icon,
 .popup-notification-icon[popupid="webRTC-shareDevices"],
 .popup-notification-icon[popupid="webRTC-sharingDevices"] {
-  list-style-image: url(chrome://browser/skin/permissions.svg#camera);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#camera);
 }
 
 .camera-icon.blocked {
-  list-style-image: url(chrome://browser/skin/permissions.svg#camera-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#camera-blocked);
 }
 
 /* The first selector is used by socialchat.xml (bug 1275558). */
 .webRTC-sharingMicrophone-notification-icon,
 .microphone-icon {
-  list-style-image: url(chrome://browser/skin/permissions.svg#microphone);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone);
 }
 
 .microphone-icon.blocked {
-  list-style-image: url(chrome://browser/skin/permissions.svg#microphone-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone-blocked);
 }
 
 .popup-notification-icon[popupid="webRTC-shareMicrophone"],
 .popup-notification-icon[popupid="webRTC-sharingMicrophone"] {
-  list-style-image: url(chrome://browser/skin/permissions.svg#microphone-detailed);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#microphone-detailed);
 }
 
 .popup-notification-icon[popupid="webRTC-shareScreen"],
 .popup-notification-icon[popupid="webRTC-sharingScreen"],
 .screen-icon {
-  list-style-image: url(chrome://browser/skin/permissions.svg#screen);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#screen);
 }
 
 .screen-icon.blocked {
-  list-style-image: url(chrome://browser/skin/permissions.svg#screen-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#screen-blocked);
 }
 
 .popup-notification-icon[popupid="pointerLock"],
 .pointerLock-icon {
-  list-style-image: url(chrome://browser/skin/permissions.svg#pointerLock);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#pointerLock);
 }
 
 .pointerLock-icon.blocked {
-  list-style-image: url(chrome://browser/skin/permissions.svg#pointerLock-blocked);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#pointerLock-blocked);
 }
 
 /* This icon has a block sign in it, so we don't need a blocked version. */
 .popup-icon {
-  list-style-image: url("chrome://browser/skin/permissions.svg#popup");
+  list-style-image: url("chrome://browser/skin/notification-icons.svg#popup");
 }
 
 /* EME */
 
 .popup-notification-icon[popupid="drmContentPlaying"],
 .drm-icon {
   list-style-image: url("chrome://browser/skin/drm-icon.svg#chains");
 }
@@ -258,51 +264,24 @@
 .popup-notification-icon[popupid*="offline-app-requested"],
 .popup-notification-icon[popupid="offline-app-usage"] {
   list-style-image: url(chrome://global/skin/icons/question-64.png);
 }
 
 /* PLUGINS */
 
 .plugin-icon {
-  list-style-image: url(chrome://browser/skin/notification-pluginNormal.png);
-}
-
-.plugin-icon.plugin-hidden {
-  list-style-image: url(chrome://browser/skin/notification-pluginAlert.png);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#plugin);
 }
 
 .plugin-icon.plugin-blocked {
-  list-style-image: url(chrome://browser/skin/notification-pluginBlocked.png);
-}
-
-.plugin-icon {
-  -moz-image-region: rect(0, 16px, 16px, 0);
+  list-style-image: url(chrome://browser/skin/notification-icons.svg#plugin-blocked);
+  fill: #d92215;
 }
 
-%ifdef XP_MACOSX
-@media (min-resolution: 1.1dppx) {
-  .plugin-icon {
-    list-style-image: url(chrome://browser/skin/notification-pluginNormal@2x.png);
-  }
-
-  .plugin-icon.plugin-hidden {
-    list-style-image: url(chrome://browser/skin/notification-pluginAlert@2x.png);
-  }
-
-  .plugin-icon.plugin-blocked {
-    list-style-image: url(chrome://browser/skin/notification-pluginBlocked@2x.png);
-  }
-
-  .plugin-icon {
-    -moz-image-region: rect(0, 32px, 32px, 0);
-  }
-}
-%endif
-
 #notification-popup-box[hidden] {
   /* Override display:none to make the pluginBlockedNotification animation work
      when showing the notification repeatedly. */
   display: -moz-box;
   visibility: collapse;
 }
 
 #plugins-notification-icon.plugin-blocked[showing] {
rename from browser/themes/shared/permissions.svg
rename to browser/themes/shared/notification-icons.svg
--- a/browser/themes/shared/permissions.svg
+++ b/browser/themes/shared/notification-icons.svg
@@ -28,16 +28,17 @@
     <path id="geo-osx-icon" d="m 0,16 16,0 0,16 12,-28 z" />
     <path id="geo-windows-icon" d="m 2,14 0,4 2,0 a 12,12 0 0 0 10,10 l 0,2 4,0 0,-2 a 12,12 0 0 0 10,-10 l 2,0 0,-4 -2,0 a 12,12 0 0 0 -10,-10 l 0,-2 -4,0 0,2 a 12,12 0 0 0 -10,10 z m 4,1.9 a 10,10 0 1 1 0,0.2 z m 4,0 a 6,6 0 1 1 0,0.2 z" />
     <path id="geo-windows-detailed-icon" d="m 2,14.5 0,3 2,0.5 a 12,12 0 0 0 10,10 l 0.5,2 3,0 0.5,-2 a 12,12 0 0 0 10,-10 l 2,-0.5 0,-3 -2,-0.5 a 12,12 0 0 0 -10,-10 l -0.5,-2 -3,0 -0.5,2 a 12,12 0 0 0 -10,10 z m 4,1.4 a 10,10 0 1 1 0,0.2 z m 3,0 a 7,7 0 1 1 0,0.2 z" />
     <path id="indexedDB-icon" d="m 2,24 a 4,4 0 0 0 4,4 l 2,0 0,-4 -2,0 0,-16 20,0 0,16 -2,0 0,4 2,0 a 4,4 0 0 0 4,-4 l 0,-16 a 4,4 0 0 0 -4,-4 l -20,0 a 4,4 0 0 0 -4,4 z m 8,-2 6,7 6,-7 -4,0 0,-8 -4,0 0,8 z" />
     <path id="login-icon" d="m 2,26 0,4 6,0 0,-2 2,0 0,-2 1,0 0,-1 2,0 0,-3 2,0 2.5,-2.5 1.5,1.5 3,-3 a 8,8 0 1 0 -8,-8 l -3,3 2,2 z m 20,-18.1 a 2,2 0 1 1 0,0.2 z" />
     <path id="login-detailed-icon" d="m 1,27 0,3.5 a 0.5,0.5 0 0 0 0.5,0.5 l 5,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1.5 1.5,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1.5 1,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-1 1,0 a 0.5,0.5 0 0 0 0.5,-0.5 l 0,-2 2,0 2.5,-2.5 q 0.5,-0.5 1,0 l 1,1 c 0.5,0.5 1,0.5 1.5,-0.5 l 1,-2 a 9,9 0 1 0 -8,-8 l -2,1 c -1,0.5 -1,1 -0.5,1.5 l 1.5,1.5 q 0.5,0.5 0,1 z m 21,-19.1 a 2,2 0 1 1 0,0.2 z" />
     <path id="microphone-icon" d="m 8,14 0,4 a 8,8 0 0 0 6,7.7 l 0,2.3 -2,0 a 2,2 0 0 0 -2,2 l 12,0 a 2,2 0 0 0 -2,-2 l -2,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 -2,0 0,4 a 6,6 0 0 1 -12,0 l 0,-4 z m 4,4 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z" />
     <path id="microphone-detailed-icon" d="m 8,18 a 8,8 0 0 0 6,7.7 l 0,2.3 -1,0 a 3,2 0 0 0 -3,2 l 12,0 a 3,2 0 0 0 -3,-2 l -1,0 0,-2.3 a 8,8 0 0 0 6,-7.7 l 0,-4 a 1,1 0 0 0 -2,0 l 0,4 a 6,6 0 0 1 -12,0 l 0,-4 a 1,1 0 0 0 -2,0 z m 4,0 a 4,4 0 0 0 8,0 l 0,-12 a 4,4 0 0 0 -8,0 z" />
+    <path id="plugin-icon" d="m 2,26 a 2,2 0 0 0 2,2 l 24,0 a 2,2 0 0 0 2,-2 l 0,-16 a 2,2 0 0 0 -2,-2 l -24,0 a 2,2 0 0 0 -2,2 z m 2,-20 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z m 14,0 10,0 0,-2 a 2,2 0 0 0 -2,-2 l -6,0 a 2,2 0 0 0 -2,2 z" />
     <path id="pointerLock-icon" d="m 8,24 6,-5 5,10 4,-2 -5,-10 7,-1 -17,-14 z" />
     <path id="popup-icon" d="m 2,24 a 4,4 0 0 0 4,4 l 8,0 a 10,10 0 0 1 -2,-4 l -4,0 a 2,2 0 0 1 -2,-2 l 0,-12 18,0 0,2 a 10,10 0 0 1 4,2 l 0,-8 a 4,4 0 0 0 -4,-4 l -18,0 a 4,4 0 0 0 -4,4 z m 12,-2.1 a 8,8 0 1 1 0,0.2 m 10.7,-4.3 a 5,5 0 0 0 -6.9,6.9 z m -5.4,8.4 a 5,5 0 0 0 6.9,-6.9 z" />
     <path id="screen-icon" d="m 2,18 a 2,2 0 0 0 2,2 l 2,0 0,-6 a 4,4 0 0 1 4,-4 l 14,0 0,-6 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z m 6,10 a 2,2 0 0 0 2,2 l 18,0 a 2,2 0 0 0 2,-2 l 0,-14 a 2,2 0 0 0 -2,-2 l -18,0 a 2,2 0 0 0 -2,2 z" />
 
     <clipPath id="clip">
       <path d="m 0,0 0,31 31,-31 z m 6,32 26,0 0,-26 z"/>
     </clipPath>
   </defs>
@@ -56,16 +57,18 @@
   <use id="geo-windows-detailed" xlink:href="#geo-windows-detailed-icon" />
   <use id="indexedDB" xlink:href="#indexedDB-icon" />
   <use id="indexedDB-blocked" class="blocked" xlink:href="#indexedDB-icon" />
   <use id="login" xlink:href="#login-icon" />
   <use id="login-detailed" xlink:href="#login-detailed-icon" />
   <use id="microphone" xlink:href="#microphone-icon" />
   <use id="microphone-blocked" class="blocked" xlink:href="#microphone-icon" />
   <use id="microphone-detailed" xlink:href="#microphone-detailed-icon" />
+  <use id="plugin" xlink:href="#plugin-icon" />
+  <use id="plugin-blocked" class="blocked" xlink:href="#plugin-icon" />
   <use id="pointerLock" xlink:href="#pointerLock-icon" />
   <use id="pointerLock-blocked" class="blocked" xlink:href="#pointerLock-icon" />
   <use id="popup" xlink:href="#popup-icon" />
   <use id="screen" xlink:href="#screen-icon" />
   <use id="screen-blocked" class="blocked" xlink:href="#screen-icon" />
 
   <path id="strikeout" d="m 2,28 2,2 26,-26 -2,-2 z"/>
 </svg>
deleted file mode 100644
index 7492fdd8670488b9b510d913424dcc46f5e1ab57..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index f3359969b6f30fb090f424961024a72435e01b49..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index e2e9489004eb7cd0661d6ebf8d3a7be4869fddca..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 5126be01f08bfd06884278f6527e0e43f9029534..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 979e92b7f51d98aebc1ce4b6255954e6ee8a12dd..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index c081bbb475f9b60070fcae3817967da05f1b533b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -109,25 +109,25 @@
 
 .tab-sharing-icon-overlay {
   /* 16px of the icon + 6px of margin-inline-end of .tab-icon-image */
   margin-inline-start: -22px;
   position: relative;
 }
 
 .tab-sharing-icon-overlay[sharing="camera"] {
-  list-style-image: url("chrome://browser/skin/permissions.svg#camera");
+  list-style-image: url("chrome://browser/skin/notification-icons.svg#camera");
 }
 
 .tab-sharing-icon-overlay[sharing="microphone"] {
-  list-style-image: url("chrome://browser/skin/permissions.svg#microphone");
+  list-style-image: url("chrome://browser/skin/notification-icons.svg#microphone");
 }
 
 .tab-sharing-icon-overlay[sharing="screen"] {
-  list-style-image: url("chrome://browser/skin/permissions.svg#screen");
+  list-style-image: url("chrome://browser/skin/notification-icons.svg#screen");
 }
 
 .tab-sharing-icon-overlay[sharing] {
   filter: url("chrome://browser/skin/filters.svg#fill");
   fill: rgb(224, 41, 29);
 }
 
 .tab-icon-overlay {
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1018,76 +1018,144 @@ toolbar[brighttext] .toolbarbutton-1 > .
    * padding (2 * 2px) + border (2 * 1px), but as a minimum because otherwise
    * increase in text sizes would break things...
    */
   min-height: 24px;
 }
 
 /* ::::: fullscreen window controls ::::: */
 
-#window-controls {
-  margin-inline-start: 4px;
-}
-
 #minimize-button,
 #restore-button,
 #close-button {
-  list-style-image: url("chrome://global/skin/icons/windowControls.png");
-  padding: 0;
+  -moz-appearance: none;
+  border: none;
+  margin: 0 !important;
+  padding: 6px 12px;
 }
 
 #minimize-button {
-  -moz-image-region: rect(0, 16px, 16px, 0);
+  list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize);
 }
-#minimize-button:hover {
-  -moz-image-region: rect(16px, 16px, 32px, 0);
+
+#restore-button {
+  list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore);
 }
-#minimize-button:hover:active {
-  -moz-image-region: rect(32px, 16px, 48px, 0);
+
+#minimize-button:hover,
+#restore-button:hover {
+  background-color: hsla(0, 0%, 0%, .12);
 }
-#restore-button {
-  -moz-image-region: rect(0, 32px, 16px, 16px);
-}
-#restore-button:hover {
-  -moz-image-region: rect(16px, 32px, 32px, 16px);
-}
+
+#minimize-button:hover:active,
 #restore-button:hover:active {
-  -moz-image-region: rect(32px, 32px, 48px, 16px);
+  background-color: hsla(0, 0%, 0%, .22);
 }
+
 #close-button {
-  -moz-image-region: rect(0, 48px, 16px, 32px);
-  -moz-appearance: none;
-  border-style: none;
-  margin: 2px;
+  list-style-image: url(chrome://browser/skin/caption-buttons.svg#close);
+}
+
+#close-button:hover {
+  background-color: hsl(355, 86%, 49%);
+  list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-white);
+}
+
+#close-button:hover:active {
+  background-color: hsl(355, 82%, 69%);
+}
+
+toolbar[brighttext] #minimize-button {
+  list-style-image: url(chrome://browser/skin/caption-buttons.svg#minimize-white);
+}
+
+toolbar[brighttext] #restore-button {
+  list-style-image: url(chrome://browser/skin/caption-buttons.svg#restore-white);
+}
+
+toolbar[brighttext] #close-button {
+  list-style-image: url(chrome://browser/skin/caption-buttons.svg#close-white);
 }
-#close-button:hover {
-  -moz-image-region: rect(16px, 48px, 32px, 32px);
+
+@media (-moz-os-version: windows-xp),
+       (-moz-os-version: windows-vista),
+       (-moz-os-version: windows-win7) {
+  #window-controls {
+    margin-inline-start: 4px;
+  }
+
+  #minimize-button,
+  #restore-button,
+  #close-button {
+    list-style-image: url("chrome://global/skin/icons/windowControls.png");
+    padding: 0;
+  }
+
+  #minimize-button {
+    -moz-image-region: rect(0, 16px, 16px, 0);
+  }
+
+  #minimize-button:hover {
+    -moz-image-region: rect(16px, 16px, 32px, 0);
+  }
+
+  #minimize-button:hover:active {
+    -moz-image-region: rect(32px, 16px, 48px, 0);
+  }
+
+  #restore-button {
+    -moz-image-region: rect(0, 32px, 16px, 16px);
+  }
+
+  #restore-button:hover {
+    -moz-image-region: rect(16px, 32px, 32px, 16px);
+  }
+
+  #restore-button:hover:active {
+    -moz-image-region: rect(32px, 32px, 48px, 16px);
+  }
+
+  #close-button {
+    -moz-image-region: rect(0, 48px, 16px, 32px);
+    -moz-appearance: none;
+    border-style: none;
+    margin: 2px;
+  }
+
+  #close-button:hover {
+    -moz-image-region: rect(16px, 48px, 32px, 32px);
+  }
+
+  #close-button:hover:active {
+    -moz-image-region: rect(32px, 48px, 48px, 32px);
+  }
 }
-#close-button:hover:active {
-  -moz-image-region: rect(32px, 48px, 48px, 32px);
-}
-
-@media not all and (-moz-os-version: windows-xp) {
+
+@media (-moz-os-version: windows-vista),
+       (-moz-os-version: windows-win7) {
   #window-controls {
     -moz-box-align: start;
   }
 
   #minimize-button,
   #restore-button,
   #close-button {
     -moz-appearance: none;
     border-style: none;
     margin: 0;
   }
+
   #close-button {
     -moz-image-region: rect(0, 49px, 16px, 32px);
   }
+
   #close-button:hover {
     -moz-image-region: rect(16px, 49px, 32px, 32px);
   }
+
   #close-button:hover:active {
     -moz-image-region: rect(32px, 49px, 48px, 32px);
   }
 
   #minimize-button:-moz-locale-dir(rtl),
   #restore-button:-moz-locale-dir(rtl),
   #close-button:-moz-locale-dir(rtl) {
     transform: scaleX(-1);
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -34,16 +34,42 @@ typedef ASTConsumer *ASTConsumerPtr;
 // ones for compatibility with older versions.
 #define cxxConstructExpr constructExpr
 #define cxxConstructorDecl constructorDecl
 #define cxxMethodDecl methodDecl
 #define cxxNewExpr newExpr
 #define cxxRecordDecl recordDecl
 #endif
 
+// Check if the given expression contains an assignment expression.
+// This can either take the form of a Binary Operator or a
+// Overloaded Operator Call.
+bool HasSideEffectAssignment(const Expr *expr) {
+  if (auto opCallExpr = dyn_cast_or_null<CXXOperatorCallExpr>(expr)) {
+    auto binOp = opCallExpr->getOperator();
+    if (binOp == OO_Equal || (binOp >= OO_PlusEqual && binOp <= OO_PipeEqual)) {
+      return true;
+    }
+  } else if (auto binOpExpr = dyn_cast_or_null<BinaryOperator>(expr)) {
+    if (binOpExpr->isAssignmentOp()) {
+      return true;
+    }
+  }
+
+  // Recurse to children.
+  for (const Stmt *SubStmt : expr->children()) {
+    auto childExpr = dyn_cast_or_null<Expr>(SubStmt);
+    if (childExpr && HasSideEffectAssignment(childExpr)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 namespace {
 
 using namespace clang::ast_matchers;
 class DiagnosticsMatcher {
 public:
   DiagnosticsMatcher();
 
   ASTConsumerPtr makeASTConsumer() { return astMatcher.newASTConsumer(); }
@@ -119,31 +145,37 @@ private:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
   class RefCountedCopyConstructorChecker : public MatchFinder::MatchCallback {
   public:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
+  class AssertAssignmentChecker : public MatchFinder::MatchCallback {
+  public:
+    virtual void run(const MatchFinder::MatchResult &Result);
+  };
+
   ScopeChecker scopeChecker;
   ArithmeticArgChecker arithmeticArgChecker;
   TrivialCtorDtorChecker trivialCtorDtorChecker;
   NaNExprChecker nanExprChecker;
   NoAddRefReleaseOnReturnChecker noAddRefReleaseOnReturnChecker;
   RefCountedInsideLambdaChecker refCountedInsideLambdaChecker;
   ExplicitOperatorBoolChecker explicitOperatorBoolChecker;
   NoDuplicateRefCntMemberChecker noDuplicateRefCntMemberChecker;
   NeedsNoVTableTypeChecker needsNoVTableTypeChecker;
   NonMemMovableTemplateArgChecker nonMemMovableTemplateArgChecker;
   NonMemMovableMemberChecker nonMemMovableMemberChecker;
   ExplicitImplicitChecker explicitImplicitChecker;
   NoAutoTypeChecker noAutoTypeChecker;
   NoExplicitMoveConstructorChecker noExplicitMoveConstructorChecker;
   RefCountedCopyConstructorChecker refCountedCopyConstructorChecker;
+  AssertAssignmentChecker assertAttributionChecker;
   MatchFinder astMatcher;
 };
 
 namespace {
 
 std::string getDeclarationNamespace(const Decl *decl) {
   const DeclContext *DC =
       decl->getDeclContext()->getEnclosingNamespaceContext();
@@ -757,16 +789,25 @@ AST_MATCHER(QualType, autoNonAutoableTyp
 
 AST_MATCHER(CXXConstructorDecl, isExplicitMoveConstructor) {
   return Node.isExplicit() && Node.isMoveConstructor();
 }
 
 AST_MATCHER(CXXConstructorDecl, isCompilerProvidedCopyConstructor) {
   return !Node.isUserProvided() && Node.isCopyConstructor();
 }
+
+AST_MATCHER(CallExpr, isAssertAssignmentTestFunc) {
+  static const std::string assertName = "MOZ_AssertAssignmentTest";
+  const FunctionDecl *method = Node.getDirectCallee();
+
+  return method
+      && method->getDeclName().isIdentifier()
+      && method->getName() == assertName;
+}
 }
 }
 
 namespace {
 
 void CustomTypeAnnotation::dumpAnnotationReason(DiagnosticsEngine &Diag,
                                                 QualType T,
                                                 SourceLocation Loc) {
@@ -1065,16 +1106,20 @@ DiagnosticsMatcher::DiagnosticsMatcher()
       &noExplicitMoveConstructorChecker);
 
   astMatcher.addMatcher(
       cxxConstructExpr(
           hasDeclaration(cxxConstructorDecl(isCompilerProvidedCopyConstructor(),
                                             ofClass(hasRefCntMember()))))
           .bind("node"),
       &refCountedCopyConstructorChecker);
+
+  astMatcher.addMatcher(
+      callExpr(isAssertAssignmentTestFunc()).bind("funcCall"),
+      &assertAttributionChecker);
 }
 
 // These enum variants determine whether an allocation has occured in the code.
 enum AllocationVariety {
   AV_None,
   AV_Global,
   AV_Automatic,
   AV_Temporary,
@@ -1540,16 +1585,28 @@ void DiagnosticsMatcher::RefCountedCopyC
   // Everything we needed to know was checked in the matcher - we just report
   // the error here
   const CXXConstructExpr *E = Result.Nodes.getNodeAs<CXXConstructExpr>("node");
 
   Diag.Report(E->getLocation(), ErrorID);
   Diag.Report(E->getLocation(), NoteID);
 }
 
+void DiagnosticsMatcher::AssertAssignmentChecker::run(
+    const MatchFinder::MatchResult &Result) {
+  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
+  unsigned assignInsteadOfComp = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Error, "Forbidden assignment in assert expression");
+  const CallExpr *funcCall = Result.Nodes.getNodeAs<CallExpr>("funcCall");
+
+  if (funcCall && HasSideEffectAssignment(funcCall)) {
+    Diag.Report(funcCall->getLocStart(), assignInsteadOfComp);
+  }
+}
+
 class MozCheckAction : public PluginASTAction {
 public:
   ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI,
                                    StringRef fileName) override {
 #if CLANG_VERSION_FULL >= 306
     std::unique_ptr<MozChecker> checker(llvm::make_unique<MozChecker>(CI));
     ASTConsumerPtr other(checker->getOtherConsumer());
 
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/tests/TestAssertWithAssignment.cpp
@@ -0,0 +1,68 @@
+#include "mozilla/MacroArgs.h"
+
+static __attribute__((always_inline)) bool MOZ_AssertAssignmentTest(bool expr) {
+  return expr;
+}
+
+#define MOZ_UNLIKELY(x) (__builtin_expect(!!(x), 0))
+#define MOZ_CRASH() do { } while(0)
+#define MOZ_CHECK_ASSERT_ASSIGNMENT(expr) MOZ_AssertAssignmentTest(!!(expr))
+
+#define MOZ_ASSERT_HELPER1(expr) \
+  do { \
+    if (MOZ_UNLIKELY(!MOZ_CHECK_ASSERT_ASSIGNMENT(expr))) { \
+      MOZ_CRASH();\
+    } \
+  } while(0) \
+
+/* Now the two-argument form. */
+#define MOZ_ASSERT_HELPER2(expr, explain) \
+  do { \
+    if (MOZ_UNLIKELY(!MOZ_CHECK_ASSERT_ASSIGNMENT(expr))) { \
+      MOZ_CRASH();\
+    } \
+  } while(0) \
+
+#define MOZ_RELEASE_ASSERT_GLUE(a, b) a b
+#define MOZ_RELEASE_ASSERT(...) \
+  MOZ_RELEASE_ASSERT_GLUE( \
+    MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_ASSERT_HELPER, __VA_ARGS__), \
+    (__VA_ARGS__))
+
+#define MOZ_ASSERT(...) MOZ_RELEASE_ASSERT(__VA_ARGS__)
+
+void FunctionTest(int p) {
+  MOZ_ASSERT(p = 1); // expected-error {{Forbidden assignment in assert expression}}
+}
+
+void FunctionTest2(int p) {
+  MOZ_ASSERT(((p = 1)));  // expected-error {{Forbidden assignment in assert expression}}
+}
+
+void FunctionTest3(int p) {
+  MOZ_ASSERT(p != 3);
+}
+
+class TestOverloading {
+  int value;
+public:
+  explicit TestOverloading(int _val) : value(_val) {}
+  // different operators
+  explicit operator bool() const { return true; }
+  TestOverloading& operator=(const int _val) { value = _val; return *this; }
+
+  int& GetInt() {return value;}
+};
+
+void TestOverloadingFunc() {
+  TestOverloading p(2);
+  int f;
+
+  MOZ_ASSERT(p);
+  MOZ_ASSERT(p = 3); // expected-error {{Forbidden assignment in assert expression}}
+  MOZ_ASSERT(p, "p is not valid");
+  MOZ_ASSERT(p = 3, "p different than 3"); // expected-error {{Forbidden assignment in assert expression}}
+  MOZ_ASSERT(p.GetInt() = 2); // expected-error {{Forbidden assignment in assert expression}}
+  MOZ_ASSERT(p.GetInt() == 2);
+  MOZ_ASSERT(p.GetInt() == 2, f = 3);
+}
--- a/build/clang-plugin/tests/moz.build
+++ b/build/clang-plugin/tests/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # dummy library name to avoid skipping building the sources here.
 Library('clang-plugin-tests')
 
 SOURCES += [
+    'TestAssertWithAssignment.cpp',
     'TestBadImplicitConversionCtor.cpp',
     'TestCustomHeap.cpp',
     'TestExplicitOperatorBool.cpp',
     'TestGlobalClass.cpp',
     'TestHeapClass.cpp',
     'TestInheritTypeAnnotationsFromTemplateArgs.cpp',
     'TestMultipleAnnotations.cpp',
     'TestMustOverride.cpp',
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -46,16 +46,17 @@ SEARCH_PATHS = [
     'python/jsmin',
     'python/psutil',
     'python/pylru',
     'python/which',
     'python/pystache',
     'python/pyyaml/lib',
     'python/requests',
     'python/slugid',
+    'python/voluptuous',
     'build',
     'build/pymake',
     'config',
     'dom/bindings',
     'dom/bindings/parser',
     'dom/media/test/external',
     'layout/tools/reftest',
     'other-licenses/ply',
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -227,17 +227,17 @@ def get_compiler_info(compiler, language
         %COMPILER clang-cl
         %VERSION _MSC_FULL_VER
         #else
         %COMPILER msvc
         %VERSION _MSC_FULL_VER
         #endif
         #elif defined(__clang__)
         %COMPILER clang
-        #  if !__cplusplus || __cpp_static_assert
+        #  if !__cplusplus || __has_feature(cxx_alignof)
         %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
         #  endif
         #elif defined(__GNUC__)
         %COMPILER gcc
         %VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
         #endif
 
         #if __cplusplus
--- a/build/pgo/server-locations.txt
+++ b/build/pgo/server-locations.txt
@@ -176,16 +176,19 @@ http://itisatracker.org:80
 http://trackertest.org:80
 
 https://malware.example.com:443
 https://unwanted.example.com:443
 https://tracking.example.com:443
 https://not-tracking.example.com:443
 https://tracking.example.org:443
 
+# Bug 1281083
+http://bug1281083.example.com:80
+
 # Bug 483437, 484111
 https://www.bank1.com:443           privileged,cert=escapeattack1
 
 #
 # CONNECT for redirproxy results in a 302 redirect to
 # test1.example.com
 #
 https://redirproxy.example.com:443          privileged,redir=test1.example.com
--- a/caps/moz.build
+++ b/caps/moz.build
@@ -1,16 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
 MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
+BROWSER_CHROME_MANIFESTS += ['tests/mochitest/browser.ini']
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
 
 # Hack to make this file available as a resource:// URI.
 TESTING_JS_MODULES += [
     'tests/mochitest/resource_test_file.html',
 ]
 
 XPIDL_SOURCES += [
new file mode 100644
--- /dev/null
+++ b/caps/tests/mochitest/browser.ini
@@ -0,0 +1,1 @@
+[browser_checkloaduri.js]
new file mode 100644
--- /dev/null
+++ b/caps/tests/mochitest/browser_checkloaduri.js
@@ -0,0 +1,121 @@
+"use strict";
+
+let ssm = Services.scriptSecurityManager;
+
+const URLs = new Map([
+  ["http://www.example.com", [
+  // For each of these entries, the booleans represent whether the parent URI can:
+  // - load them
+  // - load them without principal inheritance
+  // - whether the URI can be created at all (some protocol handlers will
+  //   refuse to create certain variants)
+    ["http://www.example2.com", true, true, true],
+    ["feed:http://www.example2.com", false, false, true],
+    ["https://www.example2.com", true, true, true],
+    ["chrome://foo/content/bar.xul", false, false, true],
+    ["feed:chrome://foo/content/bar.xul", false, false, false],
+    ["view-source:http://www.example2.com", false, false, true],
+    ["view-source:feed:http://www.example2.com", false, false, true],
+    ["feed:view-source:http://www.example2.com", false, false, false],
+    ["data:text/html,Hi", true, false, true],
+    ["javascript:alert('hi')", true, false, true],
+  ]],
+  ["feed:http://www.example.com", [
+    ["http://www.example2.com", true, true, true],
+    ["feed:http://www.example2.com", true, true, true],
+    ["https://www.example2.com", true, true, true],
+    ["feed:https://www.example2.com", false, false, true],
+    ["chrome://foo/content/bar.xul", false, false, true],
+    ["feed:chrome://foo/content/bar.xul", false, false, false],
+    ["view-source:http://www.example2.com", false, false, true],
+    ["view-source:feed:http://www.example2.com", false, false, true],
+    ["feed:view-source:http://www.example2.com", false, false, false],
+    ["data:text/html,Hi", true, false, true],
+    ["javascript:alert('hi')", true, false, true],
+  ]],
+  ["view-source:http://www.example.com", [
+    ["http://www.example2.com", true, true, true],
+    ["feed:http://www.example2.com", false, false, true],
+    ["https://www.example2.com", true, true, true],
+    ["feed:https://www.example2.com", false, false, true],
+    ["chrome://foo/content/bar.xul", false, false, true],
+    ["feed:chrome://foo/content/bar.xul", false, false, false],
+    ["view-source:http://www.example2.com", true, true, true],
+    ["view-source:feed:http://www.example2.com", false, false, true],
+    ["feed:view-source:http://www.example2.com", false, false, false],
+    ["data:text/html,Hi", true, false, true],
+    ["javascript:alert('hi')", true, false, true],
+  ]],
+]);
+
+function testURL(source, target, canLoad, canLoadWithoutInherit, canCreate, flags) {
+  let threw = false;
+  let targetURI;
+  try {
+    targetURI = makeURI(target);
+  } catch (ex) {
+    ok(!canCreate, "Shouldn't be passing URIs that we can't create. Failed to create: " + target);
+    return;
+  }
+  ok(canCreate, "Created a URI for " + target + " which should " +
+     (canCreate ? "" : "not ") + "be possible.");
+  try {
+    ssm.checkLoadURIWithPrincipal(source, targetURI, flags);
+  } catch (ex) {
+    info(ex.message);
+    threw = true;
+  }
+  let inheritDisallowed = flags & ssm.DISALLOW_INHERIT_PRINCIPAL;
+  let shouldThrow = inheritDisallowed ? !canLoadWithoutInherit : !canLoad;
+  ok(threw == shouldThrow,
+     "Should " + (shouldThrow ? "" : "not ") + "throw an error when loading " +
+     target + " from " + source.URI.spec +
+     (inheritDisallowed ? " without" : " with") + " principal inheritance.");
+}
+
+add_task(function* () {
+  let baseFlags = ssm.STANDARD | ssm.DONT_REPORT_ERRORS;
+  for (let [sourceString, targetsAndExpectations] of URLs) {
+    let source = ssm.createCodebasePrincipal(makeURI(sourceString), {});
+    for (let [target, canLoad, canLoadWithoutInherit, canCreate] of targetsAndExpectations) {
+      testURL(source, target, canLoad, canLoadWithoutInherit, canCreate, baseFlags);
+      testURL(source, target, canLoad, canLoadWithoutInherit, canCreate,
+              baseFlags | ssm.DISALLOW_INHERIT_PRINCIPAL);
+    }
+  }
+
+  // Now test blob URIs, which we need to do in-content.
+  yield BrowserTestUtils.withNewTab("http://www.example.com/", function* (browser) {
+    yield ContentTask.spawn(
+      browser,
+      testURL.toString(),
+      function* (testURLFn) {
+        let testURL = eval("(" + testURLFn + ")");
+        let ssm = Services.scriptSecurityManager;
+        let baseFlags = ssm.STANDARD | ssm.DONT_REPORT_ERRORS;
+        let makeURI = Cu.import("resource://gre/modules/BrowserUtils.jsm", {}).BrowserUtils.makeURI;
+        let b = new content.Blob(["I am a blob"]);
+        let contentBlobURI = content.URL.createObjectURL(b);
+        let contentPrincipal = content.document.nodePrincipal;
+        // Loading this blob URI from the content page should work:
+        testURL(contentPrincipal, contentBlobURI, true, true, true, baseFlags);
+        testURL(contentPrincipal, contentBlobURI, true, true, true,
+                baseFlags | ssm.DISALLOW_INHERIT_PRINCIPAL);
+
+        testURL(contentPrincipal, "view-source:" + contentBlobURI, false, false, true,
+                baseFlags);
+        testURL(contentPrincipal, "view-source:" + contentBlobURI, false, false, true,
+                baseFlags | ssm.DISALLOW_INHERIT_PRINCIPAL);
+
+        // Feed URIs for blobs can't be created, so need to pass false as the fourth param.
+        for (let prefix of ["feed:", "view-source:feed:", "feed:view-source:"]) {
+          testURL(contentPrincipal, prefix + contentBlobURI, false, false, false,
+                  baseFlags);
+          testURL(contentPrincipal, prefix + contentBlobURI, false, false, false,
+                  baseFlags | ssm.DISALLOW_INHERIT_PRINCIPAL);
+        }
+      }
+    );
+
+  });
+});
--- a/devtools/client/definitions.js
+++ b/devtools/client/definitions.js
@@ -1,18 +1,16 @@
 /* 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/. */
 
 "use strict";
 
-const {Cc, Ci} = require("chrome");
 const Services = require("Services");
-
-loader.lazyGetter(this, "osString", () => Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS);
+const osString = Services.appinfo.OS;
 
 // Panels
 loader.lazyGetter(this, "OptionsPanel", () => require("devtools/client/framework/toolbox-options").OptionsPanel);
 loader.lazyGetter(this, "InspectorPanel", () => require("devtools/client/inspector/inspector-panel").InspectorPanel);
 loader.lazyGetter(this, "WebConsolePanel", () => require("devtools/client/webconsole/panel").WebConsolePanel);
 loader.lazyGetter(this, "DebuggerPanel", () => require("devtools/client/debugger/panel").DebuggerPanel);
 loader.lazyGetter(this, "StyleEditorPanel", () => require("devtools/client/styleeditor/styleeditor-panel").StyleEditorPanel);
 loader.lazyGetter(this, "ShaderEditorPanel", () => require("devtools/client/shadereditor/panel").ShaderEditorPanel);
--- a/devtools/client/eyedropper/eyedropper.js
+++ b/devtools/client/eyedropper/eyedropper.js
@@ -27,20 +27,16 @@ loader.lazyGetter(this, "ioService", fun
   return Cc["@mozilla.org/network/io-service;1"]
     .getService(Ci.nsIIOService);
 });
 
 loader.lazyGetter(this, "DOMUtils", function () {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 });
 
-loader.lazyGetter(this, "XULRuntime", function () {
-  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
-});
-
 loader.lazyGetter(this, "l10n", () => Services.strings
   .createBundle("chrome://devtools/locale/eyedropper.properties"));
 
 const EYEDROPPER_URL = "chrome://devtools/content/eyedropper/eyedropper.xul";
 const CROSSHAIRS_URL = "chrome://devtools/content/eyedropper/crosshairs.css";
 const NOCURSOR_URL = "chrome://devtools/content/eyedropper/nocursor.css";
 
 const ZOOM_PREF = "devtools.eyedropper.zoom";
@@ -112,17 +108,17 @@ function Eyedropper(chromeWindow, opts =
   this._onMouseMove = this._onMouseMove.bind(this);
   this._onMouseDown = this._onMouseDown.bind(this);
   this._onKeyDown = this._onKeyDown.bind(this);
   this._onFrameLoaded = this._onFrameLoaded.bind(this);
 
   this._chromeWindow = chromeWindow;
   this._chromeDocument = chromeWindow.document;
 
-  this._OS = XULRuntime.OS;
+  this._OS = Services.appinfo.OS;
 
   this._dragging = true;
   this.loaded = false;
 
   this._mouseMoveCounter = 0;
 
   this.format = Services.prefs.getCharPref(FORMAT_PREF); // color value format
   this.zoom = Services.prefs.getIntPref(ZOOM_PREF);      // zoom level - integer
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -1,12 +1,13 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
 
 "use strict";
 
 // This shared-head.js file is used for multiple mochitest test directories in
 // devtools.
 // It contains various common helper functions.
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr, Constructor: CC}
@@ -469,8 +470,22 @@ function waitForContextMenu(popup, butto
  * @return {Promise} resolves when the preferences have been updated
  */
 function pushPref(preferenceName, value) {
   return new Promise(resolve => {
     let options = {"set": [[preferenceName, value]]};
     SpecialPowers.pushPrefEnv(options, resolve);
   });
 }
+
+/**
+ * Lookup the provided dotted path ("prop1.subprop2.myProp") in the provided object.
+ *
+ * @param {Object} obj
+ *        Object to expand.
+ * @param {String} path
+ *        Dotted path to use to expand the object.
+ * @return {?} anything that is found at the provided path in the object.
+ */
+function lookupPath(obj, path) {
+  let segments = path.split(".");
+  return segments.reduce((prev, current) => prev[current], obj);
+}
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -65,19 +65,16 @@ loader.lazyRequireGetter(this, "system",
   "devtools/shared/system");
 loader.lazyRequireGetter(this, "getPreferenceFront",
   "devtools/shared/fronts/preference", true);
 loader.lazyRequireGetter(this, "KeyShortcuts",
   "devtools/client/shared/key-shortcuts", true);
 loader.lazyRequireGetter(this, "ZoomKeys",
   "devtools/client/shared/zoom-keys");
 
-loader.lazyGetter(this, "osString", () => {
-  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
-});
 loader.lazyGetter(this, "registerHarOverlay", () => {
   return require("devtools/client/netmonitor/har/toolbox-overlay").register;
 });
 
 // White-list buttons that can be toggled to prevent adding prefs for
 // addons that have manually inserted toolbarbuttons into DOM.
 // (By default, supported target is only local tab)
 const ToolboxButtons = exports.ToolboxButtons = [
@@ -490,17 +487,18 @@ Toolbox.prototype = {
   get ReactDOM() {
     return this.browserRequire("devtools/client/shared/vendor/react-dom");
   },
 
   _pingTelemetry: function () {
     this._telemetry.toolOpened("toolbox");
 
     this._telemetry.logOncePerBrowserVersion(OS_HISTOGRAM, system.getOSCPU());
-    this._telemetry.logOncePerBrowserVersion(OS_IS_64_BITS, system.is64Bit ? 1 : 0);
+    this._telemetry.logOncePerBrowserVersion(OS_IS_64_BITS,
+                                             Services.appinfo.is64Bit ? 1 : 0);
     this._telemetry.logOncePerBrowserVersion(SCREENSIZE_HISTOGRAM, system.getScreenDimensions());
   },
 
   /**
    * Because our panels are lazy loaded this is a good place to watch for
    * "pref-changed" events.
    * @param  {String} event
    *         The event type, "pref-changed".
--- a/devtools/client/inspector/computed/computed.js
+++ b/devtools/client/inspector/computed/computed.js
@@ -24,16 +24,18 @@ const {XPCOMUtils} = require("resource:/
 const {getCssProperties} = require("devtools/shared/fronts/css-properties");
 
 loader.lazyRequireGetter(this, "overlays",
   "devtools/client/inspector/shared/style-inspector-overlays");
 loader.lazyRequireGetter(this, "StyleInspectorMenu",
   "devtools/client/inspector/shared/style-inspector-menu");
 loader.lazyRequireGetter(this, "KeyShortcuts",
   "devtools/client/shared/key-shortcuts", true);
+loader.lazyRequireGetter(this, "LayoutView",
+  "devtools/client/inspector/layout/layout", true);
 
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                   "resource://gre/modules/PluralForm.jsm");
 
 XPCOMUtils.defineLazyGetter(CssComputedView, "_strings", function () {
   return Services.strings.createBundle(
     "chrome://devtools-shared/locale/styleinspector.properties");
 });
@@ -164,17 +166,16 @@ function CssComputedView(inspector, docu
   this._onCopy = this._onCopy.bind(this);
   this._onFilterStyles = this._onFilterStyles.bind(this);
   this._onClearSearch = this._onClearSearch.bind(this);
   this._onIncludeBrowserStyles = this._onIncludeBrowserStyles.bind(this);
   this._onFilterTextboxContextMenu =
     this._onFilterTextboxContextMenu.bind(this);
 
   let doc = this.styleDocument;
-  this.root = doc.getElementById("root");
   this.element = doc.getElementById("propertyContainer");
   this.searchField = doc.getElementById("computedview-searchbox");
   this.searchClearButton = doc.getElementById("computedview-searchinput-clear");
   this.includeBrowserStylesCheckbox =
     doc.getElementById("browser-style-checkbox");
 
   this.shortcuts = new KeyShortcuts({ window: this.styleWindow });
   this._onShortcut = this._onShortcut.bind(this);
@@ -772,17 +773,16 @@ CssComputedView.prototype = {
     this.searchField.removeEventListener("input", this._onFilterStyles);
     this.searchField.removeEventListener("contextmenu",
                                          this._onFilterTextboxContextMenu);
     this.searchClearButton.removeEventListener("click", this._onClearSearch);
     this.includeBrowserStylesCheckbox.removeEventListener("input",
       this._onIncludeBrowserStyles);
 
     // Nodes used in templating
-    this.root = null;
     this.element = null;
     this.panel = null;
     this.searchField = null;
     this.searchClearButton = null;
     this.includeBrowserStylesCheckbox = null;
 
     // Property views
     for (let propView of this.propertyViews) {
@@ -1402,85 +1402,86 @@ SelectorView.prototype = {
     });
   }
 };
 
 function ComputedViewTool(inspector, window) {
   this.inspector = inspector;
   this.document = window.document;
 
-  this.view = new CssComputedView(this.inspector, this.document,
+  this.computedView = new CssComputedView(this.inspector, this.document,
     this.inspector.pageStyle);
+  this.layoutView = new LayoutView(this.inspector, this.document);
 
   this.onSelected = this.onSelected.bind(this);
   this.refresh = this.refresh.bind(this);
   this.onPanelSelected = this.onPanelSelected.bind(this);
   this.onMutations = this.onMutations.bind(this);
   this.onResized = this.onResized.bind(this);
 
   this.inspector.selection.on("detached", this.onSelected);
   this.inspector.selection.on("new-node-front", this.onSelected);
   this.inspector.selection.on("pseudoclass", this.refresh);
   this.inspector.sidebar.on("computedview-selected", this.onPanelSelected);
   this.inspector.pageStyle.on("stylesheet-updated", this.refresh);
   this.inspector.walker.on("mutations", this.onMutations);
   this.inspector.walker.on("resize", this.onResized);
 
-  this.view.selectElement(null);
+  this.computedView.selectElement(null);
 
   this.onSelected();
 }
 
 ComputedViewTool.prototype = {
   isSidebarActive: function () {
-    if (!this.view) {
+    if (!this.computedView) {
       return false;
     }
     return this.inspector.sidebar.getCurrentTabID() == "computedview";
   },
 
   onSelected: function (event) {
     // Ignore the event if the view has been destroyed, or if it's inactive.
     // But only if the current selection isn't null. If it's been set to null,
     // let the update go through as this is needed to empty the view on
     // navigation.
-    if (!this.view) {
+    if (!this.computedView) {
       return;
     }
 
     let isInactive = !this.isSidebarActive() &&
                      this.inspector.selection.nodeFront;
     if (isInactive) {
       return;
     }
 
-    this.view.setPageStyle(this.inspector.pageStyle);
+    this.computedView.setPageStyle(this.inspector.pageStyle);
 
     if (!this.inspector.selection.isConnected() ||
         !this.inspector.selection.isElementNode()) {
-      this.view.selectElement(null);
+      this.computedView.selectElement(null);
       return;
     }
 
     if (!event || event == "new-node-front") {
       let done = this.inspector.updating("computed-view");
-      this.view.selectElement(this.inspector.selection.nodeFront).then(() => {
+      this.computedView.selectElement(this.inspector.selection.nodeFront).then(() => {
         done();
       });
     }
   },
 
   refresh: function () {
     if (this.isSidebarActive()) {
-      this.view.refreshPanel();
+      this.computedView.refreshPanel();
     }
   },
 
   onPanelSelected: function () {
-    if (this.inspector.selection.nodeFront === this.view._viewedElement) {
+    if (this.inspector.selection.nodeFront === this.computedView._viewedElement) {
       this.refresh();
     } else {
       this.onSelected();
     }
   },
 
   /**
    * When markup mutations occur, if an attribute of the selected node changes,
@@ -1511,17 +1512,18 @@ ComputedViewTool.prototype = {
     this.inspector.selection.off("pseudoclass", this.refresh);
     this.inspector.selection.off("new-node-front", this.onSelected);
     this.inspector.selection.off("detached", this.onSelected);
     this.inspector.sidebar.off("computedview-selected", this.onPanelSelected);
     if (this.inspector.pageStyle) {
       this.inspector.pageStyle.off("stylesheet-updated", this.refresh);
     }
 
-    this.view.destroy();
+    this.computedView.destroy();
+    this.layoutView.destroy();
 
-    this.view = this.document = this.inspector = null;
+    this.computedView = this.layoutView = this.document = this.inspector = null;
   }
 };
 
 exports.CssComputedView = CssComputedView;
 exports.ComputedViewTool = ComputedViewTool;
 exports.PropertyView = PropertyView;
--- a/devtools/client/inspector/computed/test/browser_computed_cycle_color.js
+++ b/devtools/client/inspector/computed/test/browser_computed_cycle_color.js
@@ -54,16 +54,17 @@ function* checkColorCycling(container, v
   for (let test of tests) {
     yield checkSwatchShiftClick(container, win, test.value, test.comment);
   }
 }
 
 function* checkSwatchShiftClick(container, win, expectedValue, comment) {
   let swatch = container.querySelector(".computedview-colorswatch");
   let valueNode = container.querySelector(".computedview-color");
+  swatch.scrollIntoView();
 
   let onUnitChange = swatch.once("unit-change");
   EventUtils.synthesizeMouseAtCenter(swatch, {
     type: "mousedown",
     shiftKey: true
   }, win);
   yield onUnitChange;
   is(valueNode.textContent, expectedValue, comment);
--- a/devtools/client/inspector/computed/test/browser_computed_keybindings_01.js
+++ b/devtools/client/inspector/computed/test/browser_computed_keybindings_01.js
@@ -20,16 +20,17 @@ add_task(function* () {
   let {inspector, view} = yield openComputedView();
   yield selectNode(".matches", inspector);
 
   let propView = getFirstVisiblePropertyView(view);
   let rulesTable = propView.matchedSelectorsContainer;
   let matchedExpander = propView.element;
 
   info("Focusing the property");
+  matchedExpander.scrollIntoView();
   let onMatchedExpanderFocus = once(matchedExpander, "focus", true);
   EventUtils.synthesizeMouseAtCenter(matchedExpander, {}, view.styleWindow);
   yield onMatchedExpanderFocus;
 
   yield checkToggleKeyBinding(view.styleWindow, "VK_SPACE", rulesTable,
                               inspector);
   yield checkToggleKeyBinding(view.styleWindow, "VK_RETURN", rulesTable,
                               inspector);
--- a/devtools/client/inspector/computed/test/browser_computed_matched-selectors-toggle.js
+++ b/devtools/client/inspector/computed/test/browser_computed_matched-selectors-toggle.js
@@ -25,17 +25,17 @@ add_task(function* () {
   yield testExpandOnDblClick(view, inspector);
   yield testCollapseOnDblClick(view, inspector);
 });
 
 function* testExpandOnTwistyClick({styleDocument, styleWindow}, inspector) {
   info("Testing that a property expands on twisty click");
 
   info("Getting twisty element");
-  let twisty = styleDocument.querySelector(".expandable");
+  let twisty = styleDocument.querySelector("#propertyContainer .expandable");
   ok(twisty, "Twisty found");
 
   let onExpand = inspector.once("computed-view-property-expanded");
   info("Clicking on the twisty element");
   twisty.click();
 
   yield onExpand;
 
@@ -44,17 +44,17 @@ function* testExpandOnTwistyClick({style
   ok(div.childNodes.length > 0,
     "Matched selectors are expanded on twisty click");
 }
 
 function* testCollapseOnTwistyClick({styleDocument, styleWindow}, inspector) {
   info("Testing that a property collapses on twisty click");
 
   info("Getting twisty element");
-  let twisty = styleDocument.querySelector(".expandable");
+  let twisty = styleDocument.querySelector("#propertyContainer .expandable");
   ok(twisty, "Twisty found");
 
   let onCollapse = inspector.once("computed-view-property-collapsed");
   info("Clicking on the twisty element");
   twisty.click();
 
   yield onCollapse;
 
@@ -66,16 +66,18 @@ function* testCollapseOnTwistyClick({sty
 
 function* testExpandOnDblClick({styleDocument, styleWindow}, inspector) {
   info("Testing that a property expands on container dbl-click");
 
   info("Getting computed property container");
   let container = styleDocument.querySelector(".property-view");
   ok(container, "Container found");
 
+  container.scrollIntoView();
+
   let onExpand = inspector.once("computed-view-property-expanded");
   info("Dbl-clicking on the container");
   EventUtils.synthesizeMouseAtCenter(container, {clickCount: 2}, styleWindow);
 
   yield onExpand;
 
   // Expanded means the matchedselectors div is not empty
   let div = styleDocument.querySelector(".property-content .matchedselectors");
--- a/devtools/client/inspector/computed/test/browser_computed_select-and-copy-styles.js
+++ b/devtools/client/inspector/computed/test/browser_computed_select-and-copy-styles.js
@@ -1,19 +1,17 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests that properties can be selected and copied from the computed view.
 
-XPCOMUtils.defineLazyGetter(this, "osString", function () {
-  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
-});
+const osString = Services.appinfo.OS;
 
 const TEST_URI = `
   <style type="text/css">
     span {
       font-variant-caps: small-caps;
       color: #000000;
     }
     .nomatches {
--- a/devtools/client/inspector/computed/test/head.js
+++ b/devtools/client/inspector/computed/test/head.js
@@ -127,17 +127,17 @@ function getComputedViewPropertyValue(vi
  *        The instance of the computed view panel
  * @param {Number} index
  *        The index of the property to be expanded
  * @return a promise that resolves when the property has been expanded, or
  * rejects if the property was not found
  */
 function expandComputedViewPropertyByIndex(view, index) {
   info("Expanding property " + index + " in the computed view");
-  let expandos = view.styleDocument.querySelectorAll(".expandable");
+  let expandos = view.styleDocument.querySelectorAll("#propertyContainer .expandable");
   if (!expandos.length || !expandos[index]) {
     return promise.reject();
   }
 
   let onExpand = view.inspector.once("computed-view-property-expanded");
   expandos[index].click();
   return onExpand;
 }
--- a/devtools/client/inspector/inspector-panel.js
+++ b/devtools/client/inspector/inspector-panel.js
@@ -26,17 +26,16 @@ const MenuItem = require("devtools/clien
 
 loader.lazyRequireGetter(this, "CSS", "CSS");
 
 loader.lazyRequireGetter(this, "CommandUtils", "devtools/client/shared/developer-toolbar", true);
 loader.lazyRequireGetter(this, "ComputedViewTool", "devtools/client/inspector/computed/computed", true);
 loader.lazyRequireGetter(this, "FontInspector", "devtools/client/inspector/fonts/fonts", true);
 loader.lazyRequireGetter(this, "HTMLBreadcrumbs", "devtools/client/inspector/breadcrumbs", true);
 loader.lazyRequireGetter(this, "InspectorSearch", "devtools/client/inspector/inspector-search", true);
-loader.lazyRequireGetter(this, "LayoutView", "devtools/client/inspector/layout/layout", true);
 loader.lazyRequireGetter(this, "MarkupView", "devtools/client/inspector/markup/markup", true);
 loader.lazyRequireGetter(this, "RuleViewTool", "devtools/client/inspector/rules/rules", true);
 loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/inspector/toolsidebar", true);
 loader.lazyRequireGetter(this, "ViewHelpers", "devtools/client/shared/widgets/view-helpers", true);
 
 loader.lazyGetter(this, "strings", () => {
   return Services.strings.createBundle("chrome://devtools/locale/inspector.properties");
 });
@@ -418,30 +417,24 @@ InspectorPanel.prototype = {
       strings.GetStringFromName("inspector.sidebar.ruleViewTitle"),
       defaultTab == "ruleview");
 
     this.sidebar.addExistingTab(
       "computedview",
       strings.GetStringFromName("inspector.sidebar.computedViewTitle"),
       defaultTab == "computedview");
 
-    this.sidebar.addExistingTab(
-      "layoutview",
-      strings.GetStringFromName("inspector.sidebar.layoutViewTitle"),
-      defaultTab == "layoutview");
-
     this._setDefaultSidebar = (event, toolId) => {
       Services.prefs.setCharPref("devtools.inspector.activeSidebar", toolId);
     };
 
     this.sidebar.on("select", this._setDefaultSidebar);
 
     this.ruleview = new RuleViewTool(this, this.panelWin);
     this.computedview = new ComputedViewTool(this, this.panelWin);
-    this.layoutview = new LayoutView(this, this.panelWin);
 
     if (this.target.form.animationsActor) {
       this.sidebar.addFrameTab(
         "animationinspector",
         strings.GetStringFromName("inspector.sidebar.animationInspectorTitle"),
         "chrome://devtools/content/animationinspector/animation-inspector.xhtml",
         defaultTab == "animationinspector");
     }
@@ -747,20 +740,16 @@ InspectorPanel.prototype = {
     if (this.computedview) {
       this.computedview.destroy();
     }
 
     if (this.fontInspector) {
       this.fontInspector.destroy();
     }
 
-    if (this.layoutview) {
-      this.layoutview.destroy();
-    }
-
     let cssPropertiesDestroyer = this._cssPropertiesLoaded.then(({front}) => {
       if (front) {
         front.destroy();
       }
     });
 
     this.sidebar.off("select", this._setDefaultSidebar);
     let sidebarDestroyer = this.sidebar.destroy();
--- a/devtools/client/inspector/inspector.xul
+++ b/devtools/client/inspector/inspector.xul
@@ -102,69 +102,74 @@
           </html:div>
           <html:label id="browser-style-checkbox-label" for="browser-style-checkbox">
             <html:input id="browser-style-checkbox"
                         type="checkbox"
                         class="includebrowserstyles"
                         label="&browserStylesLabel;"/>&browserStylesLabel;</html:label>
         </html:div>
 
-        <html:div id="propertyContainer">
-        </html:div>
-
-        <html:div id="computedview-no-results" hidden="">
-          &noPropertiesFound;
-        </html:div>
-      </html:div>
+        <html:div id="computedview-container">
+          <html:div id="layout-wrapper" class="theme-separator" tabindex="0">
+            <html:div id="layout-header">
+              <html:div id="layout-expander" class="expander theme-twisty expandable" open=""></html:div>
+              <html:span>&layoutViewTitle;</html:span>
+              <html:button class="devtools-button" id="layout-geometry-editor" title="&geometry.button.tooltip;"></html:button>
+            </html:div>
 
-      <html:div id="sidebar-panel-layoutview" class="devtools-monospace theme-sidebar inspector-tabpanel">
-        <html:div id="layout-wrapper">
-          <html:div id="layout-container">
-            <html:p id="layout-header">
-              <html:span id="layout-element-size"></html:span>
-              <html:section id="layout-position-group">
-                <html:button class="devtools-button" id="layout-geometry-editor" title="&geometry.button.tooltip;"></html:button>
-                <html:span id="layout-element-position"></html:span>
-              </html:section>
-            </html:p>
-
-            <html:div id="layout-main">
-              <html:span class="layout-legend" data-box="margin" title="&margin.tooltip;">&margin.tooltip;</html:span>
-              <html:div id="layout-margins" data-box="margin" title="&margin.tooltip;">
-                <html:span class="layout-legend" data-box="border" title="&border.tooltip;">&border.tooltip;</html:span>
-                <html:div id="layout-borders" data-box="border" title="&border.tooltip;">
-                  <html:span class="layout-legend" data-box="padding" title="&padding.tooltip;">&padding.tooltip;</html:span>
-                  <html:div id="layout-padding" data-box="padding" title="&padding.tooltip;">
-                    <html:div id="layout-content" data-box="content" title="&content.tooltip;">
+            <html:div id="layout-container">
+              <html:div id="layout-main">
+                <html:span class="layout-legend" data-box="margin" title="&margin.tooltip;">&margin.tooltip;</html:span>
+                <html:div id="layout-margins" data-box="margin" title="&margin.tooltip;">
+                  <html:span class="layout-legend" data-box="border" title="&border.tooltip;">&border.tooltip;</html:span>
+                  <html:div id="layout-borders" data-box="border" title="&border.tooltip;">
+                    <html:span class="layout-legend" data-box="padding" title="&padding.tooltip;">&padding.tooltip;</html:span>
+                    <html:div id="layout-padding" data-box="padding" title="&padding.tooltip;">
+                      <html:div id="layout-content" data-box="content" title="&content.tooltip;">
+                      </html:div>
                     </html:div>
                   </html:div>
                 </html:div>
+
+                <html:p class="layout-margin layout-top"><html:span data-box="margin" class="layout-editable" title="margin-top"></html:span></html:p>
+                <html:p class="layout-margin layout-right"><html:span data-box="margin" class="layout-editable" title="margin-right"></html:span></html:p>
+                <html:p class="layout-margin layout-bottom"><html:span data-box="margin" class="layout-editable" title="margin-bottom"></html:span></html:p>
+                <html:p class="layout-margin layout-left"><html:span data-box="margin" class="layout-editable" title="margin-left"></html:span></html:p>
+
+                <html:p class="layout-border layout-top"><html:span data-box="border" class="layout-editable" title="border-top"></html:span></html:p>
+                <html:p class="layout-border layout-right"><html:span data-box="border" class="layout-editable" title="border-right"></html:span></html:p>
+                <html:p class="layout-border layout-bottom"><html:span data-box="border" class="layout-editable" title="border-bottom"></html:span></html:p>
+                <html:p class="layout-border layout-left"><html:span data-box="border" class="layout-editable" title="border-left"></html:span></html:p>
+
+                <html:p class="layout-padding layout-top"><html:span data-box="padding" class="layout-editable" title="padding-top"></html:span></html:p>
+                <html:p class="layout-padding layout-right"><html:span data-box="padding" class="layout-editable" title="padding-right"></html:span></html:p>
+                <html:p class="layout-padding layout-bottom"><html:span data-box="padding" class="layout-editable" title="padding-bottom"></html:span></html:p>
+                <html:p class="layout-padding layout-left"><html:span data-box="padding" class="layout-editable" title="padding-left"></html:span></html:p>
+
+                <html:p class="layout-size"><html:span data-box="content" title="&content.tooltip;"></html:span></html:p>
               </html:div>
 
-              <html:p class="layout-border layout-top"><html:span data-box="border" class="layout-editable" title="border-top"></html:span></html:p>
-              <html:p class="layout-border layout-right"><html:span data-box="border" class="layout-editable" title="border-right"></html:span></html:p>
-              <html:p class="layout-border layout-bottom"><html:span data-box="border" class="layout-editable" title="border-bottom"></html:span></html:p>
-              <html:p class="layout-border layout-left"><html:span data-box="border" class="layout-editable" title="border-left"></html:span></html:p>
-
-              <html:p class="layout-margin layout-top"><html:span data-box="margin" class="layout-editable" title="margin-top"></html:span></html:p>
-              <html:p class="layout-margin layout-right"><html:span data-box="margin" class="layout-editable" title="margin-right"></html:span></html:p>
-              <html:p class="layout-margin layout-bottom"><html:span data-box="margin" class="layout-editable" title="margin-bottom"></html:span></html:p>
-              <html:p class="layout-margin layout-left"><html:span data-box="margin" class="layout-editable" title="margin-left"></html:span></html:p>
+              <html:div id="layout-info">
+                <html:span id="layout-element-size"></html:span>
+                <html:section id="layout-position-group">
+                  <html:span id="layout-element-position"></html:span>
+                </html:section>
+              </html:div>
 
-              <html:p class="layout-padding layout-top"><html:span data-box="padding" class="layout-editable" title="padding-top"></html:span></html:p>
-              <html:p class="layout-padding layout-right"><html:span data-box="padding" class="layout-editable" title="padding-right"></html:span></html:p>
-              <html:p class="layout-padding layout-bottom"><html:span data-box="padding" class="layout-editable" title="padding-bottom"></html:span></html:p>
-              <html:p class="layout-padding layout-left"><html:span data-box="padding" class="layout-editable" title="padding-left"></html:span></html:p>
+              <html:div style="display: none">
+                <html:p id="layout-dummy"></html:p>
+              </html:div>
+            </html:div>
+          </html:div>
 
-              <html:p class="layout-size"><html:span data-box="content" title="&content.tooltip;"></html:span></html:p>
-            </html:div>
+          <html:div id="propertyContainer" class="theme-separator" tabindex="0">
+          </html:div>
 
-            <html:div style="display: none">
-              <html:p id="layout-dummy"></html:p>
-            </html:div>
+          <html:div id="computedview-no-results" hidden="">
+            &noPropertiesFound;
           </html:div>
         </html:div>
       </html:div>
 
       <html:div id="sidebar-panel-fontinspector" class="devtools-monospace theme-sidebar inspector-tabpanel">
         <html:div class="devtools-toolbar">
           <html:div class="devtools-searchbox">
             <html:input id="font-preview-text-input"
--- a/devtools/client/inspector/layout/layout.js
+++ b/devtools/client/inspector/layout/layout.js
@@ -63,17 +63,17 @@ EditingSession.prototype = {
    * no style rules affect the property.
    *
    * @param property  The name of the property as a string
    */
   getProperty: function (property) {
     // Create a hidden element for getPropertyFromRule to use
     let div = this._doc.createElement("div");
     div.setAttribute("style", "display: none");
-    this._doc.getElementById("sidebar-panel-layoutview").appendChild(div);
+    this._doc.getElementById("sidebar-panel-computedview").appendChild(div);
     this._element = this._doc.createElement("p");
     div.appendChild(this._element);
 
     // As the rules are in order of priority we can just iterate until we find
     // the first that defines a value for the property and return that.
     for (let rule of this._rules) {
       let value = this.getPropertyFromRule(rule, property);
       if (value !== "") {
@@ -178,43 +178,52 @@ EditingSession.prototype = {
     this._doc = null;
     this._rules = null;
     this._modifications.clear();
   }
 };
 
 /**
  * The layout-view panel
- * @param {InspectorPanel} inspector An instance of the inspector-panel
- * currently loaded in the toolbox
- * @param {Window} win The window containing the panel
+ * @param {InspectorPanel} inspector
+ *        An instance of the inspector-panel currently loaded in the toolbox
+ * @param {Document} document
+ *        The document that will contain the layout view.
  */
-function LayoutView(inspector, win) {
+function LayoutView(inspector, document) {
   this.inspector = inspector;
-  this.doc = win.document;
+  this.doc = document;
+  this.wrapper = this.doc.getElementById("layout-wrapper");
+  this.container = this.doc.getElementById("layout-container");
+  this.expander = this.doc.getElementById("layout-expander");
   this.sizeLabel = this.doc.querySelector(".layout-size > span");
   this.sizeHeadingLabel = this.doc.getElementById("layout-element-size");
   this._geometryEditorHighlighter = null;
 
   this.init();
 }
 
 LayoutView.prototype = {
   init: function () {
     this.update = this.update.bind(this);
 
     this.onNewSelection = this.onNewSelection.bind(this);
     this.inspector.selection.on("new-node-front", this.onNewSelection);
 
     this.onNewNode = this.onNewNode.bind(this);
-    this.inspector.sidebar.on("layoutview-selected", this.onNewNode);
+    this.inspector.sidebar.on("computedview-selected", this.onNewNode);
 
     this.onSidebarSelect = this.onSidebarSelect.bind(this);
     this.inspector.sidebar.on("select", this.onSidebarSelect);
 
+    this.onToggleExpander = this.onToggleExpander.bind(this);
+    this.expander.addEventListener("click", this.onToggleExpander);
+    let header = this.doc.getElementById("layout-header");
+    header.addEventListener("dblclick", this.onToggleExpander);
+
     this.onPickerStarted = this.onPickerStarted.bind(this);
     this.onMarkupViewLeave = this.onMarkupViewLeave.bind(this);
     this.onMarkupViewNodeHover = this.onMarkupViewNodeHover.bind(this);
     this.onWillNavigate = this.onWillNavigate.bind(this);
 
     this.initBoxModelHighlighter();
 
     // Store for the different dimensions of the node.
@@ -308,17 +317,16 @@ LayoutView.prototype = {
     // Mark document as RTL or LTR:
     let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
                     .getService(Ci.nsIXULChromeRegistry);
     let dir = chromeReg.isLocaleRTL("global");
     let container = this.doc.getElementById("layout-container");
     container.setAttribute("dir", dir ? "rtl" : "ltr");
 
     let nodeGeometry = this.doc.getElementById("layout-geometry-editor");
-
     this.onGeometryButtonClick = this.onGeometryButtonClick.bind(this);
     nodeGeometry.addEventListener("click", this.onGeometryButtonClick);
   },
 
   initBoxModelHighlighter: function () {
     let highlightElts = this.doc.querySelectorAll("#layout-container *[title]");
     this.onHighlightMouseOver = this.onHighlightMouseOver.bind(this);
     this.onHighlightMouseOut = this.onHighlightMouseOut.bind(this);
@@ -410,17 +418,17 @@ LayoutView.prototype = {
   },
 
   /**
    * Is the layoutview visible in the sidebar.
    * @return {Boolean}
    */
   isViewVisible: function () {
     return this.inspector &&
-           this.inspector.sidebar.getCurrentTabID() == "layoutview";
+           this.inspector.sidebar.getCurrentTabID() == "computedview";
   },
 
   /**
    * Is the layoutview visible in the sidebar and is the current node valid to
    * be displayed in the view.
    * @return {Boolean}
    */
   isViewVisibleAndNodeValid: function () {
@@ -435,55 +443,62 @@ LayoutView.prototype = {
   destroy: function () {
     let highlightElts = this.doc.querySelectorAll("#layout-container *[title]");
 
     for (let element of highlightElts) {
       element.removeEventListener("mouseover", this.onHighlightMouseOver, true);
       element.removeEventListener("mouseout", this.onHighlightMouseOut, true);
     }
 
+    this.expander.removeEventListener("click", this.onToggleExpander);
+    let header = this.doc.getElementById("layout-header");
+    header.removeEventListener("dblclick", this.onToggleExpander);
+
     let nodeGeometry = this.doc.getElementById("layout-geometry-editor");
     nodeGeometry.removeEventListener("click", this.onGeometryButtonClick);
 
     this.inspector.off("picker-started", this.onPickerStarted);
 
     // Inspector Panel will destroy `markup` object on "will-navigate" event,
     // therefore we have to check if it's still available in case LayoutView
     // is destroyed immediately after.
     if (this.inspector.markup) {
       this.inspector.markup.off("leave", this.onMarkupViewLeave);
       this.inspector.markup.off("node-hover", this.onMarkupViewNodeHover);
     }
 
-    this.inspector.sidebar.off("layoutview-selected", this.onNewNode);
+    this.inspector.sidebar.off("computedview-selected", this.onNewNode);
     this.inspector.selection.off("new-node-front", this.onNewSelection);
     this.inspector.sidebar.off("select", this.onSidebarSelect);
     this.inspector._target.off("will-navigate", this.onWillNavigate);
 
-    this.sizeHeadingLabel = null;
-    this.sizeLabel = null;
     this.inspector = null;
     this.doc = null;
+    this.wrapper = null;
+    this.container = null;
+    this.expander = null;
+    this.sizeLabel = null;
+    this.sizeHeadingLabel = null;
 
     if (this.reflowFront) {
       this.untrackReflows();
       this.reflowFront.destroy();
       this.reflowFront = null;
     }
   },
 
   onSidebarSelect: function (e, sidebar) {
-    this.setActive(sidebar === "layoutview");
+    this.setActive(sidebar === "computedview");
   },
 
   /**
    * Selection 'new-node-front' event handler.
    */
   onNewSelection: function () {
-    let done = this.inspector.updating("layoutview");
+    let done = this.inspector.updating("computed-view");
     this.onNewNode()
       .then(() => this.hideGeometryEditor())
       .then(done, (err) => {
         console.error(err);
         done();
       }).catch(console.error);
   },
 
@@ -521,16 +536,28 @@ LayoutView.prototype = {
       this.showGeometryEditor();
     }
   },
 
   onPickerStarted: function () {
     this.hideGeometryEditor();
   },
 
+  onToggleExpander: function () {
+    let isOpen = this.expander.hasAttribute("open");
+
+    if (isOpen) {
+      this.container.hidden = true;
+      this.expander.removeAttribute("open");
+    } else {
+      this.container.hidden = false;
+      this.expander.setAttribute("open", "");
+    }
+  },
+
   onMarkupViewLeave: function () {
     this.showGeometryEditor(true);
   },
 
   onMarkupViewNodeHover: function () {
     this.hideGeometryEditor(false);
   },
 
@@ -545,34 +572,33 @@ LayoutView.prototype = {
    * @param {Boolean} isActive
    */
   setActive: function (isActive) {
     if (isActive === this.isActive) {
       return;
     }
     this.isActive = isActive;
 
-    let panel = this.doc.getElementById("sidebar-panel-layoutview");
-    panel.classList.toggle("inactive", !isActive);
-
     if (isActive) {
       this.trackReflows();
     } else {
       this.untrackReflows();
     }
   },
 
   /**
    * Compute the dimensions of the node and update the values in
-   * the layoutview/view.xhtml document.
+   * the inspector.xul document.
    * @return a promise that will be resolved when complete.
    */
   update: function () {
     let lastRequest = Task.spawn((function* () {
       if (!this.isViewVisibleAndNodeValid()) {
+        this.wrapper.hidden = true;
+        this.inspector.emit("layoutview-updated");
         return null;
       }
 
       let node = this.inspector.selection.nodeFront;
       let layout = yield this.inspector.pageStyle.getLayout(node, {
         autoMargins: this.isActive
       });
       let styleEntries = yield this.inspector.pageStyle.getApplied(node, {});
@@ -588,22 +614,16 @@ LayoutView.prototype = {
       let width = layout.width;
       let height = layout.height;
       let newLabel = SHARED_L10N.getFormatStr("dimensions", width, height);
 
       if (this.sizeHeadingLabel.textContent != newLabel) {
         this.sizeHeadingLabel.textContent = newLabel;
       }
 
-      // If the view isn't active, no need to do anything more.
-      if (!this.isActive) {
-        this.inspector.emit("layoutview-updated");
-        return null;
-      }
-
       for (let i in this.map) {
         let property = this.map[i].property;
         if (!(property in layout)) {
           // Depending on the actor version, some properties
           // might be missing.
           continue;
         }
         let parsedValue = parseFloat(layout[property]);
@@ -651,18 +671,20 @@ LayoutView.prototype = {
 
       let newValue = width + "\u00D7" + height;
       if (this.sizeLabel.textContent != newValue) {
         this.sizeLabel.textContent = newValue;
       }
 
       this.elementRules = styleEntries.map(e => e.rule);
 
+      this.wrapper.hidden = false;
+
       this.inspector.emit("layoutview-updated");
-      return undefined;
+      return null;
     }).bind(this)).catch(console.error);
 
     this._lastRequest = lastRequest;
     return this._lastRequest;
   },
 
   /**
    * Update the text in the tooltip shown when hovering over a value to provide
--- a/devtools/client/inspector/layout/test/head.js
+++ b/devtools/client/inspector/layout/test/head.js
@@ -50,33 +50,33 @@ function selectAndHighlightNode(nodeOrSe
 
 /**
  * Open the toolbox, with the inspector tool visible, and the layout-view
  * sidebar tab selected.
  * @return a promise that resolves when the inspector is ready and the layout
  * view is visible and ready
  */
 function openLayoutView() {
-  return openInspectorSidebarTab("layoutview").then(data => {
+  return openInspectorSidebarTab("computedview").then(data => {
     // The actual highligher show/hide methods are mocked in layoutview tests.
     // The highlighter is tested in devtools/inspector/test.
     function mockHighlighter({highlighter}) {
       highlighter.showBoxModel = function () {
         return promise.resolve();
       };
       highlighter.hideBoxModel = function () {
         return promise.resolve();
       };
     }
     mockHighlighter(data.toolbox);
 
     return {
       toolbox: data.toolbox,
       inspector: data.inspector,
-      view: data.inspector.layoutview,
+      view: data.inspector.computedview.layoutView,
       testActor: data.testActor
     };
   });
 }
 
 /**
  * Wait for the layoutview-updated event.
  * @return a promise
--- a/devtools/client/inspector/markup/markup.js
+++ b/devtools/client/inspector/markup/markup.js
@@ -48,16 +48,17 @@ const Heritage = require("sdk/core/herit
 const {parseAttribute} =
       require("devtools/client/shared/node-attribute-parser");
 const {Task} = require("devtools/shared/task");
 const {scrollIntoViewIfNeeded} = require("devtools/shared/layout/utils");
 const {PrefObserver} = require("devtools/client/styleeditor/utils");
 const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
 const {template} = require("devtools/shared/gcli/templater");
 const nodeConstants = require("devtools/shared/dom-node-constants");
+const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "CSS", "CSS");
 loader.lazyGetter(this, "AutocompletePopup", () => {
   return require("devtools/client/shared/autocomplete-popup").AutocompletePopup;
 });
 
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
@@ -627,24 +628,24 @@ MarkupView.prototype = {
 
   /**
    * Create a TreeWalker to find the next/previous
    * node for selection.
    */
   _selectionWalker: function (start) {
     let walker = this.doc.createTreeWalker(
       start || this._elt,
-      Ci.nsIDOMNodeFilter.SHOW_ELEMENT,
+      nodeFilterConstants.SHOW_ELEMENT,
       function (element) {
         if (element.container &&
             element.container.elt === element &&
             element.container.visible) {
-          return Ci.nsIDOMNodeFilter.FILTER_ACCEPT;
+          return nodeFilterConstants.FILTER_ACCEPT;
         }
-        return Ci.nsIDOMNodeFilter.FILTER_SKIP;
+        return nodeFilterConstants.FILTER_SKIP;
       }
     );
     walker.currentNode = this._selectedContainer.elt;
     return walker;
   },
 
   _onCopy: function (evt) {
     // Ignore copy events from editors
@@ -3188,16 +3189,17 @@ ElementEditor.prototype = {
       attr.remove();
     }
   },
 
   _createAttribute: function (attribute, before = null) {
     // Create the template editor, which will save some variables here.
     let data = {
       attrName: attribute.name,
+      tabindex: this.container.canFocus ? "0" : "-1",
     };
     this.template("attribute", data);
     let {attr, inner, name, val} = data;
 
     // Double quotes need to be handled specially to prevent DOMParser failing.
     // name="v"a"l"u"e" when editing -> name='v"a"l"u"e"'
     // name="v'a"l'u"e" when editing -> name="v'a&quot;l'u&quot;e"
     let editValueDisplayed = attribute.value || "";
--- a/devtools/client/inspector/markup/markup.xhtml
+++ b/devtools/client/inspector/markup/markup.xhtml
@@ -71,17 +71,17 @@
  --></span>
 
     <span id="template-attribute"
           save="${attr}"
           data-attr="${attrName}"
           data-value="${attrValue}"
           class="attreditor"
           style="display:none"> <!--
-   --><span class="editable" save="${inner}" tabindex="-1"><!--
+   --><span class="editable" save="${inner}" tabindex="${tabindex}"><!--
      --><span save="${name}" class="attr-name theme-fg-color2"></span><!--
      -->=&quot;<!--
      --><span save="${val}" class="attr-value theme-fg-color6"></span><!--
      -->&quot;<!--
    --></span><!--
  --></span>
 
     <span id="template-text" save="${elt}" class="editor text"><!--
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -29,16 +29,17 @@ support-files =
   doc_markup_toggle.html
   doc_markup_tooltip.png
   doc_markup_void_elements.html
   doc_markup_void_elements.xhtml
   doc_markup_xul.xul
   head.js
   helper_attributes_test_runner.js
   helper_events_test_runner.js
+  helper_markup_accessibility_navigation.js
   helper_outerhtml_test_runner.js
   helper_style_attr_test_runner.js
   lib_jquery_1.0.js
   lib_jquery_1.1.js
   lib_jquery_1.2_min.js
   lib_jquery_1.3_min.js
   lib_jquery_1.4_min.js
   lib_jquery_1.6_min.js
@@ -50,16 +51,18 @@ support-files =
   !/devtools/client/framework/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_markup_accessibility_focus_blur.js]
 skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
 [browser_markup_accessibility_navigation.js]
 skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
+[browser_markup_accessibility_navigation_after_edit.js]
+skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
 [browser_markup_accessibility_semantics.js]
 [browser_markup_anonymous_01.js]
 [browser_markup_anonymous_02.js]
 skip-if = e10s # scratchpad.xul is not loading in e10s window
 [browser_markup_anonymous_03.js]
 [browser_markup_anonymous_04.js]
 [browser_markup_copy_image_data.js]
 subsuite = clipboard
@@ -85,16 +88,17 @@ subsuite = clipboard
 [browser_markup_events_jquery_1.3.js]
 [browser_markup_events_jquery_1.4.js]
 [browser_markup_events_jquery_1.6.js]
 [browser_markup_events_jquery_1.7.js]
 [browser_markup_events_jquery_1.11.1.js]
 [browser_markup_events_jquery_2.1.1.js]
 [browser_markup_events-overflow.js]
 skip-if = true # Bug 1177550
+[browser_markup_events-windowed-host.js]
 [browser_markup_links_01.js]
 [browser_markup_links_02.js]
 [browser_markup_links_03.js]
 [browser_markup_links_04.js]
 subsuite = clipboard
 [browser_markup_links_05.js]
 [browser_markup_links_06.js]
 [browser_markup_links_07.js]
--- a/devtools/client/inspector/markup/test/browser_markup_accessibility_navigation.js
+++ b/devtools/client/inspector/markup/test/browser_markup_accessibility_navigation.js
@@ -1,17 +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/. */
+/* import-globals-from helper_markup_accessibility_navigation.js */
 
 "use strict";
 
-/* global getContainerForSelector, openInspectorForURL */
+// Test keyboard navigation accessibility of inspector's markup view.
 
-// Test keyboard navigation accessibility of inspector's markup view.
+loadHelperScript("helper_markup_accessibility_navigation.js");
 
 /**
  * Test data has the format of:
  * {
  *   desc              {String}   description for better logging
  *   key               {String}   key event's key
  *   options           {?Object}  optional event data such as shiftKey, etc
  *   focused           {String}   path to expected focused element relative to
@@ -232,70 +233,45 @@ const TESTS = [
     focused: "docBody",
     activedescendant: "body.tagLine",
     key: "VK_UP",
     options: { },
     waitFor: "inspector-updated"
   },
 ];
 
+let containerID = 0;
 let elms = {};
-let containerID = 0;
 
 add_task(function* () {
   let { inspector } = yield openInspectorForURL(`data:text/html;charset=utf-8,
     <h1 id="some-id" class="some-class">foo<span>Child span<span></h1>`);
-  let markup = inspector.markup;
-  let doc = markup.doc;
-  let win = doc.defaultView;
 
   // Record containers that are created after inspector is initialized to be
   // useful in testing.
   inspector.on("container-created", memorizeContainer);
   registerCleanupFunction(() => {
     inspector.off("container-created", memorizeContainer);
   });
 
-  elms.docBody = doc.body;
-  elms.root = markup.getContainer(markup._rootNode);
+  elms.docBody = inspector.markup.doc.body;
+  elms.root = inspector.markup.getContainer(inspector.markup._rootNode);
   elms.header = yield getContainerForSelector("h1", inspector);
   elms.body = yield getContainerForSelector("body", inspector);
 
   // Initial focus is on root element and active descendant should be set on
   // body tag line.
-  testNavigationState(doc, elms.docBody, elms.body.tagLine);
+  testNavigationState(inspector, elms, elms.docBody, elms.body.tagLine);
 
   // Focus on the tree element.
   elms.root.elt.focus();
 
-  for (let {desc, waitFor, focused, activedescendant, key, options} of TESTS) {
-    info(desc);
-    let updated;
-    if (waitFor) {
-      updated = waitFor === "inspector-updated" ?
-        inspector.once(waitFor) : markup.once(waitFor);
-    } else {
-      updated = Promise.resolve();
-    }
+  for (let testData of TESTS) {
+    yield runAccessibilityNavigationTest(inspector, elms, testData);
+  }
 
-    EventUtils.synthesizeKey(key, options, win);
-    yield updated;
-    testNavigationState(doc, getElm(focused), getElm(activedescendant));
-  }
+  elms = null;
 });
 
 // Record all containers that are created dynamically into elms object.
 function memorizeContainer(event, container) {
   elms[`container-${containerID++}`] = container;
 }
-
-// Parse and lookup an element from elms object based on dotted path.
-function getElm(path) {
-  let segments = path.split(".");
-  return segments.reduce((prev, current) => prev[current], elms);
-}
-
-function testNavigationState(doc, focused, activedescendant) {
-  let id = activedescendant.getAttribute("id");
-  is(doc.activeElement, focused, `Keyboard focus should be set to ${focused}`);
-  is(elms.root.elt.getAttribute("aria-activedescendant"), id,
-    `Active descendant should be set to ${id}`);
-}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_accessibility_navigation_after_edit.js
@@ -0,0 +1,126 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* import-globals-from helper_markup_accessibility_navigation.js */
+
+"use strict";
+
+// Test keyboard navigation accessibility is preserved after editing attributes.
+
+loadHelperScript("helper_markup_accessibility_navigation.js");
+
+const TEST_URI = '<div id="some-id" class="some-class"></div>';
+
+/**
+ * Test data has the format of:
+ * {
+ *   desc              {String}   description for better logging
+ *   key               {String}   key event's key
+ *   options           {?Object}  optional event data such as shiftKey, etc
+ *   focused           {String}   path to expected focused element relative to
+ *                                its container
+ *   activedescendant  {String}   path to expected aria-activedescendant element
+ *                                relative to its container
+ *   waitFor           {String}   optional event to wait for if keyboard actions
+ *                                result in asynchronous updates
+ * }
+ */
+const TESTS = [
+  {
+    desc: "Select header container",
+    focused: "root.elt",
+    activedescendant: "div.tagLine",
+    key: "VK_DOWN",
+    options: { },
+    waitFor: "inspector-updated"
+  },
+  {
+    desc: "Focus on header tag",
+    focused: "div.focusableElms.0",
+    activedescendant: "div.tagLine",
+    key: "VK_RETURN",
+    options: { }
+  },
+  {
+    desc: "Activate header tag editor",
+    focused: "div.editor.tag.inplaceEditor.input",
+    activedescendant: "div.tagLine",
+    key: "VK_RETURN",
+    options: { }
+  },
+  {
+    desc: "Activate header id attribute editor",
+    focused: "div.editor.attrList.children.0.children.1.inplaceEditor.input",
+    activedescendant: "div.tagLine",
+    key: "VK_TAB",
+    options: { }
+  },
+  {
+    desc: "Deselect text in header id attribute editor",
+    focused: "div.editor.attrList.children.0.children.1.inplaceEditor.input",
+    activedescendant: "div.tagLine",
+    key: "VK_TAB",
+    options: { }
+  },
+  {
+    desc: "Move the cursor to the left",
+    focused: "div.editor.attrList.children.0.children.1.inplaceEditor.input",
+    activedescendant: "div.tagLine",
+    key: "VK_LEFT",
+    options: { }
+  },
+  {
+    desc: "Modify the attribute",
+    focused: "div.editor.attrList.children.0.children.1.inplaceEditor.input",
+    activedescendant: "div.tagLine",
+    key: "A",
+    options: { }
+  },
+  {
+    desc: "Commit the attribute change",
+    focused: "div.focusableElms.1",
+    activedescendant: "div.tagLine",
+    key: "VK_RETURN",
+    options: { },
+    waitFor: "inspector-updated"
+  },
+  {
+    desc: "Tab and focus on header class attribute",
+    focused: "div.focusableElms.2",
+    activedescendant: "div.tagLine",
+    key: "VK_TAB",
+    options: { }
+  },
+  {
+    desc: "Tab and focus on header new attribute node",
+    focused: "div.focusableElms.3",
+    activedescendant: "div.tagLine",
+    key: "VK_TAB",
+    options: { }
+  },
+];
+
+let elms = {};
+
+add_task(function* () {
+  let url = `data:text/html;charset=utf-8,${TEST_URI}`;
+  let { inspector } = yield openInspectorForURL(url);
+
+  elms.docBody = inspector.markup.doc.body;
+  elms.root = inspector.markup.getContainer(inspector.markup._rootNode);
+  elms.div = yield getContainerForSelector("div", inspector);
+  elms.body = yield getContainerForSelector("body", inspector);
+
+  // Initial focus is on root element and active descendant should be set on
+  // body tag line.
+  testNavigationState(inspector, elms, elms.docBody, elms.body.tagLine);
+
+  // Focus on the tree element.
+  elms.root.elt.focus();
+
+  for (let testData of TESTS) {
+    yield runAccessibilityNavigationTest(inspector, elms, testData);
+  }
+
+  elms = null;
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_events-windowed-host.js
@@ -0,0 +1,61 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+/*
+ * Test that the event details tooltip can be hidden by clicking outside of the tooltip
+ * after switching hosts.
+ */
+
+const TEST_URL = URL_ROOT + "doc_markup_events-overflow.html";
+
+registerCleanupFunction(() => {
+  // Restore the default Toolbox host position after the test.
+  Services.prefs.clearUserPref("devtools.toolbox.host");
+});
+
+add_task(function* () {
+  let { inspector, toolbox } = yield openInspectorForURL(TEST_URL);
+  yield runTests(inspector);
+
+  yield toolbox.switchHost("window");
+  yield runTests(inspector);
+
+  yield toolbox.switchHost("bottom");
+  yield runTests(inspector);
+
+  yield toolbox.destroy();
+});
+
+function* runTests(inspector) {
+  let markupContainer = yield getContainerForSelector("#events", inspector);
+  let evHolder = markupContainer.elt.querySelector(".markupview-events");
+  let tooltip = inspector.markup.eventDetailsTooltip;
+
+  info("Clicking to open event tooltip.");
+
+  let onInspectorUpdated = inspector.once("inspector-updated");
+  let onTooltipShown = tooltip.once("shown");
+  EventUtils.synthesizeMouseAtCenter(evHolder, {}, inspector.markup.doc.defaultView);
+
+  yield onTooltipShown;
+  // New node is selected when clicking on the events bubble, wait for inspector-updated.
+  yield onInspectorUpdated;
+
+  ok(tooltip.isVisible(), "EventTooltip visible.");
+
+  onInspectorUpdated = inspector.once("inspector-updated");
+  let onTooltipHidden = tooltip.once("hidden");
+
+  info("Click on another tag to hide the event tooltip");
+  let h1 = yield getContainerForSelector("h1", inspector);
+  let tag = h1.elt.querySelector(".tag");
+  EventUtils.synthesizeMouseAtCenter(tag, {}, inspector.markup.doc.defaultView);
+
+  yield onTooltipHidden;
+  // New node is selected, wait for inspector-updated.
+  yield onInspectorUpdated;
+
+  ok(!tooltip.isVisible(), "EventTooltip hidden.");
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/helper_markup_accessibility_navigation.js
@@ -0,0 +1,70 @@
+/* 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/. */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+/* import-globals-from head.js */
+"use strict";
+
+/**
+ * Execute a keyboard event and check that the state is as expected (focused element, aria
+ * attribute etc...).
+ *
+ * @param {InspectorPanel} inspector
+ *        Current instance of the inspector being tested.
+ * @param {Object} elms
+ *        Map of elements that will be used to retrieve live references to children
+ *        elements
+ * @param {Element} focused
+ *        Element expected to be focused
+ * @param {Element} activedescendant
+ *        Element expected to be the aria activedescendant of the root node
+ */
+function testNavigationState(inspector, elms, focused, activedescendant) {
+  let doc = inspector.markup.doc;
+  let id = activedescendant.getAttribute("id");
+  is(doc.activeElement, focused, `Keyboard focus should be set to ${focused}`);
+  is(elms.root.elt.getAttribute("aria-activedescendant"), id,
+    `Active descendant should be set to ${id}`);
+}
+
+/**
+ * Execute a keyboard event and check that the state is as expected (focused element, aria
+ * attribute etc...).
+ *
+ * @param {InspectorPanel} inspector
+ *        Current instance of the inspector being tested.
+ * @param {Object} elms
+ *        MarkupContainers/Elements that will be used to retrieve references to other
+ *        elements based on objects' paths.
+ * @param {Object} testData
+ *        - {String} desc: description for better logging.
+ *        - {String} key: keyboard event's key.
+ *        - {Object} options, optional: event data such as shiftKey, etc.
+ *        - {String} focused: path to expected focused element in elms map.
+ *        - {String} activedescendant: path to expected aria-activedescendant element in
+ *          elms map.
+ *        - {String} waitFor, optional: markupview event to wait for if keyboard actions
+ *          result in async updates. Also accepts the inspector event "inspector-updated".
+ */
+function* runAccessibilityNavigationTest(inspector, elms,
+  {desc, key, options, focused, activedescendant, waitFor}) {
+  info(desc);
+
+  let markup = inspector.markup;
+  let doc = markup.doc;
+  let win = doc.defaultView;
+
+  let updated;
+  if (waitFor) {
+    updated = waitFor === "inspector-updated" ?
+      inspector.once(waitFor) : markup.once(waitFor);
+  } else {
+    updated = Promise.resolve();
+  }
+  EventUtils.synthesizeKey(key, options, win);
+  yield updated;
+
+  let focusedElement = lookupPath(elms, focused);
+  let activeDescendantElement = lookupPath(elms, activedescendant);
+  testNavigationState(inspector, elms, focusedElement, activeDescendantElement);
+}
--- a/devtools/client/inspector/rules/models/rule.js
+++ b/devtools/client/inspector/rules/models/rule.js
@@ -1,29 +1,25 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const {Cc, Ci} = require("chrome");
+const {Ci} = require("chrome");
 const promise = require("promise");
 const CssLogic = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 const {TextProperty} =
       require("devtools/client/inspector/rules/models/text-property");
 const {promiseWarn} = require("devtools/client/inspector/shared/utils");
 const {parseDeclarations} = require("devtools/shared/css-parsing-utils");
-const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "osString", function () {
-  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
-});
+const Services = require("Services");
 
 /**
  * Rule is responsible for the following:
  *   Manages a single style declaration or rule.
  *   Applies changes to the properties in a rule.
  *   Maintains a list of TextProperty objects.
  *
  * @param {ElementStyle} elementStyle
@@ -655,17 +651,17 @@ Rule.prototype = {
   },
 
   /**
    * Return a string representation of the rule.
    */
   stringifyRule: function () {
     let selectorText = this.selectorText;
     let cssText = "";
-    let terminator = osString === "WINNT" ? "\r\n" : "\n";
+    let terminator = Services.appinfo.OS === "WINNT" ? "\r\n" : "\n";
 
     for (let textProp of this.textProps) {
       if (!textProp.invisible) {
         cssText += "\t" + textProp.stringifyProperty() + terminator;
       }
     }
 
     return selectorText + " {" + terminator + cssText + "}";
--- a/devtools/client/inspector/rules/test/browser_rules_copy_styles.js
+++ b/devtools/client/inspector/rules/test/browser_rules_copy_styles.js
@@ -4,19 +4,17 @@
 
 "use strict";
 
 /**
  * Tests the behaviour of the copy styles context menu items in the rule
  * view.
  */
 
-XPCOMUtils.defineLazyGetter(this, "osString", function () {
-  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
-});
+const osString = Services.appinfo.OS;
 
 const TEST_URI = URL_ROOT + "doc_copystyles.html";
 
 add_task(function* () {
   yield addTab(TEST_URI);
   let { inspector, view } = yield openRuleView();
   yield selectNode("#testid", inspector);
 
--- a/devtools/client/inspector/rules/test/browser_rules_select-and-copy-styles.js
+++ b/devtools/client/inspector/rules/test/browser_rules_select-and-copy-styles.js
@@ -1,19 +1,17 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests that properties can be selected and copied from the rule view
 
-XPCOMUtils.defineLazyGetter(this, "osString", function () {
-  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
-});
+const osString = Services.appinfo.OS;
 
 const TEST_URI = `
   <style type="text/css">
     html {
       color: #000000;
     }
     span {
       font-variant: small-caps; color: #000000;
--- a/devtools/client/inspector/shared/test/browser_styleinspector_context-menu-copy-color_01.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_context-menu-copy-color_01.js
@@ -19,17 +19,17 @@ add_task(function* () {
   yield testView("ruleview", inspector);
   yield testView("computedview", inspector);
 });
 
 function* testView(viewId, inspector) {
   info("Testing " + viewId);
 
   yield inspector.sidebar.select(viewId);
-  let view = inspector[viewId].view;
+  let view = inspector[viewId].view || inspector[viewId].computedView;
   yield selectNode("div", inspector);
 
   testIsColorValueNode(view);
   testIsColorPopupOnAllNodes(view);
   yield clearCurrentNodeSelection(inspector);
 }
 
 /**
--- a/devtools/client/inspector/shared/test/browser_styleinspector_refresh_when_active.js
+++ b/devtools/client/inspector/shared/test/browser_styleinspector_refresh_when_active.js
@@ -15,17 +15,17 @@ add_task(function* () {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openRuleView();
 
   yield selectNode("#one", inspector);
 
   is(getRuleViewPropertyValue(view, "element", "color"), "red",
     "The rule-view shows the properties for test node one");
 
-  let cView = inspector.computedview.view;
+  let cView = inspector.computedview.computedView;
   let prop = getComputedViewProperty(cView, "color");
   ok(!prop, "The computed-view doesn't show the properties for test node one");
 
   info("Switching to the computed-view");
   let onComputedViewReady = inspector.once("computed-view-refreshed");
   selectComputedView(inspector);
   yield onComputedViewReady;
 
--- a/devtools/client/inspector/test/head.js
+++ b/devtools/client/inspector/test/head.js
@@ -292,17 +292,17 @@ function openRuleView() {
  * view is visible and ready
  */
 function openComputedView() {
   return openInspectorSidebarTab("computedview").then(data => {
     return {
       toolbox: data.toolbox,
       inspector: data.inspector,
       testActor: data.testActor,
-      view: data.inspector.computedview.view
+      view: data.inspector.computedview.computedView
     };
   });
 }
 
 /**
  * Select the rule view sidebar tab on an already opened inspector panel.
  *
  * @param {InspectorPanel} inspector
@@ -318,17 +318,17 @@ function selectRuleView(inspector) {
  * Select the computed view sidebar tab on an already opened inspector panel.
  *
  * @param {InspectorPanel} inspector
  *        The opened inspector panel
  * @return {CssComputedView} the computed view
  */
 function selectComputedView(inspector) {
   inspector.sidebar.select("computedview");
-  return inspector.computedview.view;
+  return inspector.computedview.computedView;
 }
 
 /**
  * Get the NodeFront for a node that matches a given css selector, via the
  * protocol.
  * @param {String|NodeFront} selector
  * @param {InspectorPanel} inspector The instance of InspectorPanel currently
  * loaded in the toolbox
--- a/devtools/client/locales/en-US/layoutview.dtd
+++ b/devtools/client/locales/en-US/layoutview.dtd
@@ -11,16 +11,17 @@
   - You want to make that choice consistent across the developer tools.
   - A good criteria is the language in which you'd find the best
   - documentation on web development on the web. -->
 
 <!-- LOCALIZATION NOTE (*.tooltip): These tooltips are not regular tooltips.
   -  The text appears on the bottom right corner of the layout view when
   -  the corresponding box is hovered. -->
 
+<!ENTITY layoutViewTitle          "Box Model">
 <!ENTITY margin.tooltip           "margin">
 <!ENTITY border.tooltip           "border">
 <!ENTITY padding.tooltip          "padding">
 <!ENTITY content.tooltip          "content">
 
 <!-- LOCALIZATION NOTE: This label is displayed as a tooltip that appears when
   -  hovering over the button that allows users to edit the position of an
   -  element in the page. -->
--- a/devtools/client/netmonitor/har/har-builder.js
+++ b/devtools/client/netmonitor/har/har-builder.js
@@ -1,23 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const { Ci, Cc } = require("chrome");
 const { defer, all } = require("promise");
 const { LocalizationHelper } = require("devtools/client/shared/l10n");
+const Services = require("Services");
+const appInfo = Services.appinfo;
 
 loader.lazyRequireGetter(this, "NetworkHelper", "devtools/shared/webconsole/network-helper");
 
-loader.lazyGetter(this, "appInfo", () => {
-  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
-});
-
 loader.lazyGetter(this, "L10N", () => {
   return new LocalizationHelper("chrome://devtools/locale/har.properties");
 });
 
 const HAR_VERSION = "1.1";
 
 /**
  * This object is responsible for building HAR file. See HAR spec:
--- a/devtools/client/performance/test/browser.ini
+++ b/devtools/client/performance/test/browser.ini
@@ -97,16 +97,17 @@ support-files =
 [browser_perf-telemetry-02.js]
 [browser_perf-telemetry-03.js]
 [browser_perf-telemetry-04.js]
 # [browser_perf-theme-toggle.js] TODO bug 1256350
 [browser_perf-tree-abstract-01.js]
 [browser_perf-tree-abstract-02.js]
 [browser_perf-tree-abstract-03.js]
 [browser_perf-tree-abstract-04.js]
+[browser_perf-tree-abstract-05.js]
 [browser_perf-tree-view-01.js]
 [browser_perf-tree-view-02.js]
 [browser_perf-tree-view-03.js]
 [browser_perf-tree-view-04.js]
 [browser_perf-tree-view-05.js]
 [browser_perf-tree-view-06.js]
 [browser_perf-tree-view-07.js]
 [browser_perf-tree-view-08.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/performance/test/browser_perf-tree-abstract-05.js
@@ -0,0 +1,104 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+/**
+ * Tests if the abstract tree base class for the profiler's tree view
+ * supports PageUp/PageDown/Home/End keys.
+ */
+
+const { appendAndWaitForPaint } = require("devtools/client/performance/test/helpers/dom-utils");
+const { synthesizeCustomTreeClass } = require("devtools/client/performance/test/helpers/synth-utils");
+const { once } = require("devtools/client/performance/test/helpers/event-utils");
+
+add_task(function* () {
+  let { MyCustomTreeItem } = synthesizeCustomTreeClass();
+
+  let container = document.createElement("vbox");
+  container.style.height = '100%';
+  container.style.overflow = 'scroll';
+  yield appendAndWaitForPaint(gBrowser.selectedBrowser.parentNode, container);
+
+  let myDataSrc = {
+    label: "root",
+    children: []
+  };
+
+  for (let i = 0; i < 1000; i++) {
+    myDataSrc.children.push({
+      label: "child-" + i,
+      children: []
+    });
+  }
+
+  let treeRoot = new MyCustomTreeItem(myDataSrc, { parent: null });
+  treeRoot.attachTo(container);
+  treeRoot.focus();
+  treeRoot.expand();
+
+  is(document.commandDispatcher.focusedElement, treeRoot.target,
+    "The root node is focused.");
+
+  // Test HOME and END
+
+  key("VK_END");
+  is(document.commandDispatcher.focusedElement,
+    treeRoot.getChild(myDataSrc.children.length - 1).target,
+    "The last node is focused.");
+
+  key("VK_HOME");
+  is(document.commandDispatcher.focusedElement, treeRoot.target,
+    "The first (root) node is focused.");
+
+  // Test PageUp and PageDown
+
+  let nodesPerPageSize = treeRoot._getNodesPerPageSize();
+
+  key("VK_PAGE_DOWN");
+  is(document.commandDispatcher.focusedElement,
+    treeRoot.getChild(nodesPerPageSize - 1).target,
+    "The first node in the second page is focused.");
+
+  key("VK_PAGE_DOWN");
+  is(document.commandDispatcher.focusedElement,
+    treeRoot.getChild(nodesPerPageSize * 2 - 1).target,
+    "The first node in the third page is focused.");
+
+  key("VK_PAGE_UP");
+  is(document.commandDispatcher.focusedElement,
+    treeRoot.getChild(nodesPerPageSize - 1).target,
+    "The first node in the second page is focused.");
+
+  key("VK_PAGE_UP");
+  is(document.commandDispatcher.focusedElement, treeRoot.target,
+    "The first (root) node is focused.");
+
+  // Test PageUp in the middle of the first page
+
+  let middleIndex = Math.floor(nodesPerPageSize / 2);
+
+  treeRoot.getChild(middleIndex).target.focus();
+  is(document.commandDispatcher.focusedElement,
+    treeRoot.getChild(middleIndex).target,
+    "The middle node in the first page is focused.");
+
+  key("VK_PAGE_UP");
+  is(document.commandDispatcher.focusedElement, treeRoot.target,
+    "The first (root) node is focused.");
+
+  // Test PageDown in the middle of the last page
+
+  middleIndex = Math.ceil(myDataSrc.children.length - middleIndex);
+
+  treeRoot.getChild(middleIndex).target.focus();
+  is(document.commandDispatcher.focusedElement,
+    treeRoot.getChild(middleIndex).target,
+    "The middle node in the last page is focused.");
+
+  key("VK_PAGE_DOWN");
+  is(document.commandDispatcher.focusedElement,
+    treeRoot.getChild(myDataSrc.children.length - 1).target,
+    "The last node is focused.");
+
+  container.remove();
+});
--- a/devtools/client/responsivedesign/test/head.js
+++ b/devtools/client/responsivedesign/test/head.js
@@ -142,17 +142,17 @@ var openInspectorSideBar = Task.async(fu
   let {toolbox, inspector} = yield openInspector();
 
   info("Selecting the " + id + " sidebar");
   inspector.sidebar.select(id);
 
   return {
     toolbox: toolbox,
     inspector: inspector,
-    view: inspector[id].view
+    view: inspector[id].view || inspector[id].computedView
   };
 });
 
 /**
  * Checks whether the inspector's sidebar corresponding to the given id already
  * exists
  * @param {InspectorPanel}
  * @param {String}
--- a/devtools/client/shared/DOMHelpers.jsm
+++ b/devtools/client/shared/DOMHelpers.jsm
@@ -2,16 +2,18 @@
  * 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/. */
 
 "use strict";
 
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
 
 this.EXPORTED_SYMBOLS = ["DOMHelpers"];
 
 /**
  * DOMHelpers
  * Makes DOM traversal easier. Goes through iframes.
  *
  * @constructor
@@ -98,17 +100,17 @@ DOMHelpers.prototype = {
         return child;
     }
 
     return null;  // we have no children worth showing.
   },
 
   getFirstChild: function Helpers_getFirstChild(node)
   {
-    let SHOW_ALL = Components.interfaces.nsIDOMNodeFilter.SHOW_ALL;
+    let SHOW_ALL = nodeFilterConstants.SHOW_ALL;
     this.treeWalker = node.ownerDocument.createTreeWalker(node,
       SHOW_ALL, null);
     return this.treeWalker.firstChild();
   },
 
   getNextSibling: function Helpers_getNextSibling(node)
   {
     let next = this.treeWalker.nextSibling();
--- a/devtools/client/shared/components/reps/array.js
+++ b/devtools/client/shared/components/reps/array.js
@@ -55,17 +55,17 @@ define(function (require, exports, modul
           items.push(ItemRep({
             object: exc,
             delim: delim,
             key: i
           }));
         }
       }
 
-      if (array.length > max + 1) {
+      if (array.length > max) {
         items.pop();
 
         let objectLink = this.props.objectLink || DOM.span;
         items.push(Caption({
           key: "more",
           object: objectLink({
             object: this.props.object
           }, "more…")
--- a/devtools/client/shared/components/reps/grip-array.js
+++ b/devtools/client/shared/components/reps/grip-array.js
@@ -82,17 +82,17 @@ define(function (require, exports, modul
           items.push(GripArrayItem(Object.assign({}, this.props, {
             object: exc,
             delim: delim,
             key: i}
           )));
         }
       }
 
-      if (array.length > max + 1) {
+      if (array.length > max) {
         items.pop();
         let objectLink = this.props.objectLink || span;
         items.push(Caption({
           key: "more",
           object: objectLink({
             object: this.props.object
           }, "more…")
         }));
--- a/devtools/client/shared/components/test/mochitest/chrome.ini
+++ b/devtools/client/shared/components/test/mochitest/chrome.ini
@@ -5,24 +5,26 @@ support-files =
 [test_frame_01.html]
 [test_HSplitBox_01.html]
 [test_notification_box_01.html]
 [test_notification_box_02.html]
 [test_notification_box_03.html]
 [test_reps_array.html]
 [test_reps_attribute.html]
 [test_reps_date-time.html]
+[test_reps_document.html]
 [test_reps_function.html]
 [test_reps_grip.html]
 [test_reps_grip-array.html]
 [test_reps_null.html]
 [test_reps_number.html]
 [test_reps_object.html]
 [test_reps_object-with-text.html]
 [test_reps_object-with-url.html]
+[test_reps_regexp.html]
 [test_reps_string.html]
 [test_reps_stylesheet.html]
 [test_reps_text-node.html]
 [test_reps_undefined.html]
 [test_reps_window.html]
 [test_sidebar_toggle.html]
 [test_tree_01.html]
 [test_tree_02.html]
--- a/devtools/client/shared/components/test/mochitest/test_reps_array.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_array.html
@@ -14,23 +14,28 @@ Test ArrayRep rep
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
   let { ArrayRep } = browserRequire("devtools/client/shared/components/reps/array");
 
   let componentUnderTest = ArrayRep;
+  const maxLength = {
+    short: 3,
+    long: 300
+  };
 
   try {
     yield testBasic();
 
     // Test property iterator
     yield testMaxProps();
-    yield testMoreThanMaxProps();
+    yield testMoreThanShortMaxProps();
+    yield testMoreThanLongMaxProps();
     yield testRecursiveArray();
 
     // Test that properties are rendered as expected by ItemRep
     yield testNested();
   } catch(e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
@@ -88,36 +93,63 @@ window.onload = Task.async(function* () 
         mode: "long",
         expectedOutput: defaultOutput,
       }
     ];
 
     testRepRenderModes(modeTests, "testMaxProps", componentUnderTest, stub);
   }
 
-  function testMoreThanMaxProps() {
-    const stub = Array(302).fill("foo");
-    const defaultOutput = `["foo", "foo", "foo", more…]`;
+  function testMoreThanShortMaxProps() {
+    const stub = Array(maxLength.short + 1).fill("foo");
+    const defaultShortOutput = `[${Array(maxLength.short).fill("\"foo\"").join(", ")}, more…]`;
 
     const modeTests = [
       {
         mode: undefined,
-        expectedOutput: defaultOutput,
+        expectedOutput: defaultShortOutput,
       },
       {
         mode: "tiny",
-        expectedOutput: `[302]`,
+        expectedOutput: `[${maxLength.short + 1}]`,
       },
       {
         mode: "short",
-        expectedOutput: defaultOutput,
+        expectedOutput: defaultShortOutput,
       },
       {
         mode: "long",
-        expectedOutput: `[${Array(300).fill("\"foo\"").join(", ")}, more…]`,
+        expectedOutput: `[${Array(maxLength.short + 1).fill("\"foo\"").join(", ")}]`,
+      }
+    ];
+
+    testRepRenderModes(modeTests, "testMoreThanMaxProps", componentUnderTest, stub);
+  }
+
+  function testMoreThanLongMaxProps() {
+    const stub = Array(maxLength.long + 1).fill("foo");
+    const defaultShortOutput = `[${Array(maxLength.short).fill("\"foo\"").join(", ")}, more…]`;
+    const defaultLongOutput = `[${Array(maxLength.long).fill("\"foo\"").join(", ")}, more…]`;
+
+    const modeTests = [
+      {
+        mode: undefined,
+        expectedOutput: defaultShortOutput,
+      },
+      {
+        mode: "tiny",
+        expectedOutput: `[${maxLength.long + 1}]`,
+      },
+      {
+        mode: "short",
+        expectedOutput: defaultShortOutput,
+      },
+      {
+        mode: "long",
+        expectedOutput: defaultLongOutput,
       }
     ];
 
     testRepRenderModes(modeTests, "testMoreThanMaxProps", componentUnderTest, stub);
   }
 
   function testRecursiveArray() {
     let stub = [1];
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/test/mochitest/test_reps_document.html
@@ -0,0 +1,54 @@
+
+<!DOCTYPE HTML>
+<html>
+<!--
+Test Document rep
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Rep test - Document</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript;version=1.8"></script>
+<script type="application/javascript;version=1.8">
+window.onload = Task.async(function* () {
+  let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
+  let { Document } = browserRequire("devtools/client/shared/components/reps/document");
+
+  try {
+    let gripStub = {
+      "type": "object",
+      "class": "HTMLDocument",
+      "actor": "server1.conn17.obj115",
+      "extensible": true,
+      "frozen": false,
+      "sealed": false,
+      "ownPropertyLength": 1,
+      "preview": {
+        "kind": "DOMNode",
+        "nodeType": 9,
+        "nodeName": "#document",
+        "location": "https://www.mozilla.org/en-US/firefox/new/"
+      }
+    };
+
+    // Test that correct rep is chosen
+    const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
+    is(renderedRep.type, Document.rep, `Rep correctly selects ${Document.rep.displayName}`);
+
+    // Test rendering
+    const renderedComponent = renderComponent(Document.rep, { object: gripStub });
+    is(renderedComponent.textContent, "/en-US/firefox/new/", "Document rep has expected text content");
+  } catch(e) {
+    ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+  } finally {
+    SimpleTest.finish();
+  }
+});
+</script>
+</pre>
+</body>
+</html>
--- a/devtools/client/shared/components/test/mochitest/test_reps_grip-array.html
+++ b/devtools/client/shared/components/test/mochitest/test_reps_grip-array.html
@@ -14,23 +14,28 @@ Test GripArray rep
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script type="application/javascript;version=1.8">
 window.onload = Task.async(function* () {
   let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
   let { GripArray } = browserRequire("devtools/client/shared/components/reps/grip-array");
 
   let componentUnderTest = GripArray;
+  const maxLength = {
+    short: 3,
+    long: 300
+  };
 
   try {
     yield testBasic();
 
     // Test property iterator
     yield testMaxProps();
-    yield testMoreThanMaxProps();
+    yield testMoreThanShortMaxProps();
+    yield testMoreThanLongMaxProps();
     yield testRecursiveArray();
   } catch(e) {
     ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
   } finally {
     SimpleTest.finish();
   }
 
   function testBasic() {
@@ -90,38 +95,67 @@ window.onload = Task.async(function* () 
         mode: "long",
         expectedOutput: defaultOutput,
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
-  function testMoreThanMaxProps() {
-    // Test array = `["test string"…] //301 items`
-    const testName = "testMoreThanMaxProps";
+  function testMoreThanShortMaxProps() {
+    // Test array = `["test string"…] //4 items`
+    const testName = "testMoreThanShortMaxProps";
 
-    const defaultOutput = `[${Array(3).fill("\"test string\"").join(", ")}, more…]`;
+    const defaultOutput = `[${Array(maxLength.short).fill("\"test string\"").join(", ")}, more…]`;
 
     const modeTests = [
       {
         mode: undefined,
         expectedOutput: defaultOutput,
       },
       {
         mode: "tiny",
-        expectedOutput: `[302]`,
+        expectedOutput: `[${maxLength.short + 1}]`,
       },
       {
         mode: "short",
         expectedOutput: defaultOutput,
       },
       {
         mode: "long",
-        expectedOutput: `[${Array(300).fill("\"test string\"").join(", ")}, more…]`,
+        expectedOutput: `[${Array(maxLength.short + 1).fill("\"test string\"").join(", ")}]`,
+      }
+    ];
+
+    testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
+  }
+
+  function testMoreThanLongMaxProps() {
+    // Test array = `["test string"…] //301 items`
+    const testName = "testMoreThanLongMaxProps";
+
+    const defaultShortOutput = `[${Array(maxLength.short).fill("\"test string\"").join(", ")}, more…]`;
+    const defaultLongOutput = `[${Array(maxLength.long).fill("\"test string\"").join(", ")}, more…]`;
+
+    const modeTests = [
+      {
+        mode: undefined,
+        expectedOutput: defaultShortOutput,
+      },
+      {
+        mode: "tiny",
+        expectedOutput: `[${maxLength.long + 1}]`,
+      },
+      {
+        mode: "short",
+        expectedOutput: defaultShortOutput,
+      },
+      {
+        mode: "long",
+        expectedOutput: defaultLongOutput
       }
     ];
 
     testRepRenderModes(modeTests, testName, componentUnderTest, getGripStub(testName));
   }
 
   function testRecursiveArray() {
     // @TODO This is not how this feature should actually work
@@ -195,39 +229,63 @@ window.onload = Task.async(function* () 
                 "frozen": false,
                 "sealed": false,
                 "ownPropertyLength": 0
               }
             ]
           }
         };
 
-      case "testMoreThanMaxProps":
-        let grip = {
+      case "testMoreThanShortMaxProps":
+        let shortArrayGrip = {
           "type": "object",
           "class": "Array",
           "actor": "server1.conn1.obj35",
           "extensible": true,
           "frozen": false,
           "sealed": false,
           "ownPropertyLength": 4,
           "preview": {
             "kind": "ArrayLike",
-            "length": 302,
+            "length": maxLength.short + 1,
             "items": []
           }
         };
 
-        // Generate 101 properties, which is more that the maximum
-        // limit in case of the 'long' mode.
-        for (let i = 0; i < 302; i++) {
-          grip.preview.items.push("test string");
+        // Generate array grip with length 4, which is more that the maximum
+        // limit in case of the 'short' mode.
+        for (let i = 0; i < maxLength.short + 1; i++) {
+          shortArrayGrip.preview.items.push("test string");
         }
 
-        return grip;
+        return shortArrayGrip;
+
+      case "testMoreThanLongMaxProps":
+        let longArrayGrip = {
+          "type": "object",
+          "class": "Array",
+          "actor": "server1.conn1.obj35",
+          "extensible": true,
+          "frozen": false,
+          "sealed": false,
+          "ownPropertyLength": 4,
+          "preview": {
+            "kind": "ArrayLike",
+            "length": maxLength.long + 1,
+            "items": []
+          }
+        };
+
+        // Generate array grip with length 301, which is more that the maximum
+        // limit in case of the 'long' mode.
+        for (let i = 0; i < maxLength.long + 1; i++) {
+          longArrayGrip.preview.items.push("test string");
+        }
+
+        return longArrayGrip;
 
       case "testRecursiveArray":
         return {
           "type": "object",
           "class": "Array",
           "actor": "server1.conn3.obj42",
           "extensible": true,
           "frozen": false,
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/components/test/mochitest/test_reps_regexp.html
@@ -0,0 +1,49 @@
+
+<!DOCTYPE HTML>
+<html>
+<!--
+Test RegExp rep
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Rep test - RegExp</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript;version=1.8"></script>
+<script type="application/javascript;version=1.8">
+window.onload = Task.async(function* () {
+  try {
+    let { Rep } = browserRequire("devtools/client/shared/components/reps/rep");
+    let { RegExp } = browserRequire("devtools/client/shared/components/reps/regexp");
+
+    let gripStub = {
+      "type": "object",
+      "class": "RegExp",
+      "actor": "server1.conn22.obj39",
+      "extensible": true,
+      "frozen": false,
+      "sealed": false,
+      "ownPropertyLength": 1,
+      "displayString": "/ab+c/i"
+    };
+
+    // Test that correct rep is chosen
+    const renderedRep = shallowRenderComponent(Rep, { object: gripStub });
+    is(renderedRep.type, RegExp.rep, `Rep correctly selects ${RegExp.rep.displayName}`);
+
+    // Test rendering
+    const renderedComponent = renderComponent(RegExp.rep, { object: gripStub });
+    is(renderedComponent.textContent, "/ab+c/i", "RegExp rep has expected text content");
+  } catch(e) {
+    ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+  } finally {
+    SimpleTest.finish();
+  }
+});
+</script>
+</pre>
+</body>
+</html>
--- a/devtools/client/shared/developer-toolbar.js
+++ b/devtools/client/shared/developer-toolbar.js
@@ -218,25 +218,20 @@ exports.CommandUtils = CommandUtils;
 /**
  * Due to a number of panel bugs we need a way to check if we are running on
  * Linux. See the comments for TooltipPanel and OutputPanel for further details.
  *
  * When bug 780102 is fixed all isLinux checks can be removed and we can revert
  * to using panels.
  */
 loader.lazyGetter(this, "isLinux", function () {
-  return OS == "Linux";
+  return Services.appinfo.OS == "Linux";
 });
 loader.lazyGetter(this, "isMac", function () {
-  return OS == "Darwin";
-});
-
-loader.lazyGetter(this, "OS", function () {
-  let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
-  return os;
+  return Services.appinfo.OS == "Darwin";
 });
 
 /**
  * A component to manage the global developer toolbar, which contains a GCLI
  * and buttons for various developer tools.
  * @param aChromeWindow The browser window to which this toolbar is attached
  */
 function DeveloperToolbar(aChromeWindow)
@@ -1005,17 +1000,17 @@ OutputPanel.prototype._resize = function
   // Set max panel width to match any content with a max of the width of the
   // browser window.
   let maxWidth = this._panel.ownerDocument.documentElement.clientWidth;
 
   // Adjust max width according to OS.
   // We'd like to put this in CSS but we can't:
   //   body { width: calc(min(-5px, max-content)); }
   //   #_panel { max-width: -5px; }
-  switch (OS) {
+  switch (Services.appinfo.OS) {
     case "Linux":
       maxWidth -= 5;
       break;
     case "Darwin":
       maxWidth -= 25;
       break;
     case "WINNT":
       maxWidth -= 5;
--- a/devtools/client/shared/shim/Services.js
+++ b/devtools/client/shared/shim/Services.js
@@ -460,16 +460,52 @@ PrefBranch.prototype = {
 
 const Services = {
   /**
    * An implementation of nsIPrefService that is based on local
    * storage.  Only the subset of nsIPrefService that is actually used
    * by devtools is implemented here.
    */
   prefs: new PrefBranch(null, "", ""),
+
+  /**
+   * An implementation of Services.appinfo that holds just the
+   * properties needed by devtools.
+   */
+  appinfo: {
+    get OS() {
+      const os = window.navigator.userAgent;
+      if (os) {
+        if (os.includes("Linux")) {
+          return "Linux";
+        } else if (os.includes("Windows")) {
+          return "WINNT";
+        } else if (os.includes("Mac")) {
+          return "Darwin";
+        }
+      }
+      return "Unknown";
+    },
+
+    // It's fine for this to be an approximation.
+    get name() {
+      return window.navigator.userAgent;
+    },
+
+    // It's fine for this to be an approximation.
+    get version() {
+      return window.navigator.appVersion;
+    },
+
+    // This is only used by telemetry, which is disabled for the
+    // content case.  So, being totally wrong is ok.
+    get is64Bit() {
+      return true;
+    },
+  },
 };
 
 /**
  * Create a new preference.  This is used during startup (see
  * devtools/client/preferences/devtools.js) to install the
  * default preferences.
  *
  * @param {String} name the name of the preference
--- a/devtools/client/shared/shim/test/mochitest.ini
+++ b/devtools/client/shared/shim/test/mochitest.ini
@@ -1,5 +1,6 @@
 [DEFAULT]
 support-files =
   prefs-wrapper.js
 
+[test_service_appinfo.html]
 [test_service_prefs.html]
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/shim/test/test_service_appinfo.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1265802
+-->
+<head>
+  <title>Test for Bug 1265802 - replace Services.appinfo</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css">
+
+  <script type="application/javascript;version=1.8">
+  "use strict";
+  var exports = {};
+  </script>
+
+  <script type="application/javascript;version=1.8"
+	  src="resource://devtools/client/shared/shim/Services.js"></script>
+</head>
+<body>
+<script type="application/javascript;version=1.8">
+"use strict";
+
+is(Services.appinfo.OS, SpecialPowers.Services.appinfo.OS,
+   "check that Services.appinfo.OS shim matches platform");
+</script>
+</body>
--- a/devtools/client/shared/telemetry.js
+++ b/devtools/client/shared/telemetry.js
@@ -51,19 +51,17 @@ this.Telemetry = function () {
   this.logOncePerBrowserVersion = this.logOncePerBrowserVersion.bind(this);
   this.destroy = this.destroy.bind(this);
 
   this._timers = new Map();
 };
 
 module.exports = Telemetry;
 
-var {Cc, Ci, Cu} = require("chrome");
 var Services = require("Services");
-var {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
 
 Telemetry.prototype = {
   _histograms: {
     toolbox: {
       histogram: "DEVTOOLS_TOOLBOX_OPENED_COUNT",
       userHistogram: "DEVTOOLS_TOOLBOX_OPENED_PER_USER_FLAG",
       timerHistogram: "DEVTOOLS_TOOLBOX_TIME_ACTIVE_SECONDS"
     },
@@ -92,21 +90,16 @@ Telemetry.prototype = {
       userHistogram: "DEVTOOLS_RULEVIEW_OPENED_PER_USER_FLAG",
       timerHistogram: "DEVTOOLS_RULEVIEW_TIME_ACTIVE_SECONDS"
     },
     computedview: {
       histogram: "DEVTOOLS_COMPUTEDVIEW_OPENED_COUNT",
       userHistogram: "DEVTOOLS_COMPUTEDVIEW_OPENED_PER_USER_FLAG",
       timerHistogram: "DEVTOOLS_COMPUTEDVIEW_TIME_ACTIVE_SECONDS"
     },
-    layoutview: {
-      histogram: "DEVTOOLS_LAYOUTVIEW_OPENED_COUNT",
-      userHistogram: "DEVTOOLS_LAYOUTVIEW_OPENED_PER_USER_FLAG",
-      timerHistogram: "DEVTOOLS_LAYOUTVIEW_TIME_ACTIVE_SECONDS"
-    },
     fontinspector: {
       histogram: "DEVTOOLS_FONTINSPECTOR_OPENED_COUNT",
       userHistogram: "DEVTOOLS_FONTINSPECTOR_OPENED_PER_USER_FLAG",
       timerHistogram: "DEVTOOLS_FONTINSPECTOR_TIME_ACTIVE_SECONDS"
     },
     animationinspector: {
       histogram: "DEVTOOLS_ANIMATIONINSPECTOR_OPENED_COUNT",
       userHistogram: "DEVTOOLS_ANIMATIONINSPECTOR_OPENED_PER_USER_FLAG",
@@ -357,17 +350,17 @@ Telemetry.prototype = {
   /**
    * Log info about usage once per browser version. This allows us to discover
    * how many individual users are using our tools for each browser version.
    *
    * @param  {String} perUserHistogram
    *         Histogram in which the data is to be stored.
    */
   logOncePerBrowserVersion: function (perUserHistogram, value) {
-    let currentVersion = appInfo.version;
+    let currentVersion = Services.appinfo.version;
     let latest = Services.prefs.getCharPref(TOOLS_OPENED_PREF);
     let latestObj = JSON.parse(latest);
 
     let lastVersionHistogramUpdated = latestObj[perUserHistogram];
 
     if (typeof lastVersionHistogramUpdated == "undefined" ||
         lastVersionHistogramUpdated !== currentVersion) {
       latestObj[perUserHistogram] = currentVersion;
@@ -378,12 +371,8 @@ Telemetry.prototype = {
   },
 
   destroy: function () {
     for (let histogramId of this._timers.keys()) {
       this.stopTimer(histogramId);
     }
   }
 };
-
-XPCOMUtils.defineLazyGetter(this, "appInfo", function () {
-  return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
-});
--- a/devtools/client/shared/test/browser_telemetry_sidebar.js
+++ b/devtools/client/shared/test/browser_telemetry_sidebar.js
@@ -23,17 +23,17 @@ add_task(function* () {
   gBrowser.removeCurrentTab();
 });
 
 function* testSidebar(toolbox) {
   info("Testing sidebar");
 
   let inspector = toolbox.getCurrentPanel();
   let sidebarTools = ["ruleview", "computedview", "fontinspector",
-                      "layoutview", "animationinspector"];
+                      "animationinspector"];
 
   // Concatenate the array with itself so that we can open each tool twice.
   sidebarTools.push.apply(sidebarTools, sidebarTools);
 
   return new Promise(resolve => {
     // See TOOL_DELAY for why we need setTimeout here
     setTimeout(function selectSidebarTab() {
       let tool = sidebarTools.pop();
--- a/devtools/client/shared/widgets/AbstractTreeItem.jsm
+++ b/devtools/client/shared/widgets/AbstractTreeItem.jsm
@@ -473,17 +473,58 @@ AbstractTreeItem.prototype = {
    * @param number delta
    *        The offset from this item to the target item.
    * @return nsIDOMNode
    *         The element displaying the target item at the specified offset.
    */
   _getSiblingAtDelta: function (delta) {
     let childNodes = this._containerNode.childNodes;
     let indexOfSelf = Array.indexOf(childNodes, this._targetNode);
-    return childNodes[indexOfSelf + delta];
+    if (indexOfSelf + delta >= 0) {
+      return childNodes[indexOfSelf + delta];
+    }
+    return undefined;
+  },
+
+  _getNodesPerPageSize: function() {
+    let childNodes = this._containerNode.childNodes;
+    let nodeHeight = this._getHeight(childNodes[childNodes.length - 1]);
+    let containerHeight = this.bounds.height;
+    return Math.ceil(containerHeight / nodeHeight);
+  },
+
+  _getHeight: function(elem) {
+    let win = this.document.defaultView;
+    let utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsIDOMWindowUtils);
+    return utils.getBoundsWithoutFlushing(elem).height;
+  },
+
+  /**
+   * Focuses the first item in this tree.
+   */
+  _focusFirstNode: function () {
+    let childNodes = this._containerNode.childNodes;
+    // The root node of the tree may be hidden in practice, so uses for-loop
+    // here to find the next visible node.
+    for (let i = 0; i < childNodes.length; i++) {
+      // The height will be 0 if an element is invisible.
+      if (this._getHeight(childNodes[i])) {
+        childNodes[i].focus();
+        return;
+      }
+    }
+  },
+
+  /**
+   * Focuses the last item in this tree.
+   */
+  _focusLastNode: function () {
+    let childNodes = this._containerNode.childNodes;
+    childNodes[childNodes.length - 1].focus();
   },
 
   /**
    * Focuses the next item in this tree.
    */
   _focusNextNode: function () {
     let nextElement = this._getSiblingAtDelta(1);
     if (nextElement) nextElement.focus(); // nsIDOMNode
@@ -565,16 +606,46 @@ AbstractTreeItem.prototype = {
 
       case e.DOM_VK_RIGHT:
         if (!this._expanded) {
           this.expand();
         } else {
           this._focusNextNode();
         }
         return;
+
+      case e.DOM_VK_PAGE_UP:
+        let pageUpElement =
+          this._getSiblingAtDelta(-this._getNodesPerPageSize());
+        // There's a chance that the root node is hidden. In this case, its
+        // height will be 0.
+        if (pageUpElement && this._getHeight(pageUpElement)) {
+          pageUpElement.focus();
+        } else {
+          this._focusFirstNode();
+        }
+        return;
+
+      case e.DOM_VK_PAGE_DOWN:
+        let pageDownElement =
+          this._getSiblingAtDelta(this._getNodesPerPageSize());
+        if (pageDownElement) {
+          pageDownElement.focus();
+        } else {
+          this._focusLastNode();
+        }
+        return;
+
+      case e.DOM_VK_HOME:
+        this._focusFirstNode();
+        return;
+
+      case e.DOM_VK_END:
+        this._focusLastNode();
+        return;
     }
   },
 
   /**
    * Handler for the "focus" event on the element displaying this tree item.
    */
   _onFocus: function (e) {
     this._rootItem.emit("focus", this);
--- a/devtools/client/shared/widgets/FlameGraph.js
+++ b/devtools/client/shared/widgets/FlameGraph.js
@@ -922,17 +922,19 @@ FlameGraph.prototype = {
   },
 
   /**
    * Listener for the "keydown" event on the graph's container.
    */
   _onKeyDown: function (e) {
     ViewHelpers.preventScrolling(e);
 
-    if (!this._keysPressed[e.keyCode]) {
+    const hasModifier = e.ctrlKey || e.shiftKey || e.altKey || e.metaKey;
+
+    if (!hasModifier && !this._keysPressed[e.keyCode]) {
       this._keysPressed[e.keyCode] = true;
       this._userInputStack++;
       this._shouldRedraw = true;
     }
   },
 
   /**
    * Listener for the "keyup" event on the graph's container.
--- a/devtools/client/shared/widgets/HTMLTooltip.js
+++ b/devtools/client/shared/widgets/HTMLTooltip.js
@@ -217,20 +217,21 @@ function HTMLTooltip(toolbox, {
   EventEmitter.decorate(this);
 
   this.doc = toolbox.doc;
   this.type = type;
   this.autofocus = autofocus;
   this.consumeOutsideClicks = consumeOutsideClicks;
   this.useXulWrapper = this._isXUL() && useXulWrapper;
 
-  this._position = null;
+  // The top window is used to attach click event listeners to close the tooltip if the
+  // user clicks on the content page.
+  this.topWindow = this._getTopWindow();
 
-  // Use the topmost window to listen for click events to close the tooltip
-  this.topWindow = this.doc.defaultView.top;
+  this._position = null;
 
   this._onClick = this._onClick.bind(this);
 
   this._toggle = new TooltipToggle(this);
   this.startTogglingOnHover = this._toggle.start.bind(this._toggle);
   this.stopTogglingOnHover = this._toggle.stop.bind(this._toggle);
 
   this.container = this._createContainer();
@@ -377,16 +378,18 @@ HTMLTooltip.prototype = {
     this.container.classList.add("tooltip-visible");
 
     // Keep a pointer on the focused element to refocus it when hiding the tooltip.
     this._focusedElement = this.doc.activeElement;
 
     this.doc.defaultView.clearTimeout(this.attachEventsTimer);
     this.attachEventsTimer = this.doc.defaultView.setTimeout(() => {
       this._maybeFocusTooltip();
+      // Updated the top window reference each time in case the host changes.
+      this.topWindow = this._getTopWindow();
       this.topWindow.addEventListener("click", this._onClick, true);
       this.emit("shown");
     }, 0);
   }),
 
   /**
    * Calculate the rect of the viewport that limits the tooltip dimensions. When using a
    * XUL panel wrapper, the viewport will be able to use the whole screen (excluding space
@@ -545,16 +548,20 @@ HTMLTooltip.prototype = {
     // http://stackoverflow.com/questions/1599660/which-html-elements-can-receive-focus .
     let focusableSelector = "a, button, iframe, input, select, textarea";
     let focusableElement = this.panel.querySelector(focusableSelector);
     if (this.autofocus && focusableElement) {
       focusableElement.focus();
     }
   },
 
+  _getTopWindow: function () {
+    return this.doc.defaultView.top;
+  },
+
   /**
    * Check if the tooltip's owner document is a XUL document.
    */
   _isXUL: function () {
     return this.doc.documentElement.namespaceURI === XUL_NS;
   },
 
   _createXulPanelWrapper: function () {
@@ -565,17 +572,17 @@ HTMLTooltip.prototype = {
     panel.setAttribute("animate", false);
     panel.setAttribute("consumeoutsideclicks", false);
     panel.setAttribute("noautofocus", true);
     panel.setAttribute("ignorekeys", true);
 
     // Use type="arrow" to prevent side effects (see Bug 1285206)
     panel.setAttribute("type", "arrow");
 
-    panel.setAttribute("level", "float");
+    panel.setAttribute("level", "top");
     panel.setAttribute("class", "tooltip-xul-wrapper");
 
     return panel;
   },
 
   _showXulWrapperAt: function (left, top) {
     let onPanelShown = listenOnce(this.xulPanelWrapper, "popupshown");
     this.xulPanelWrapper.openPopupAtScreen(left, top, false);
--- a/devtools/client/themes/computed.css
+++ b/devtools/client/themes/computed.css
@@ -27,21 +27,27 @@
   margin-right: 5px;
 
   /* Vertically center the 'Browser styles' checkbox in the
      Computed panel with its label. */
   display: flex;
   align-items: center;
 }
 
+#computedview-container {
+  overflow: auto;
+}
+
 #propertyContainer {
   -moz-user-select: text;
   overflow-y: auto;
   overflow-x: hidden;
   flex: auto;
+  border-top-width: 1px;
+  border-top-style: dotted;
 }
 
 .row-striped {
   background: var(--theme-body-background);
 }
 
 .property-view-hidden,
 .property-content-hidden {
@@ -54,17 +60,19 @@
   flex-wrap: wrap;
 }
 
 .property-name-container {
   width: 202px;
 }
 
 .property-value-container {
-  width: 168px;
+  display: flex;
+  flex: 1 1 168px;
+  overflow: hidden;
 }
 
 .property-name-container > *,
 .property-value-container > * {
   display: inline-block;
   vertical-align: middle;
 }
 
--- a/devtools/client/themes/firebug-theme.css
+++ b/devtools/client/themes/firebug-theme.css
@@ -15,18 +15,20 @@
 /* Remove filters on firebug specific images */
 
 .theme-firebug .devtools-tabbar .devtools-button::before,
 .theme-firebug .devtools-option-toolbarbutton > image,
 .theme-firebug .command-button-invertable::before,
 .theme-firebug #sources-toolbar image,
 .theme-firebug [id$="pane-toggle"] > image,
 .theme-firebug [id$="pane-toggle"]::before,
-.theme-firebug #global-toolbar .devtools-button::before,
+.theme-firebug .sidebar-toggle::before,
 .theme-firebug #element-picker::before,
+.theme-firebug #rewind-timeline::before,
+.theme-firebug #pause-resume-timeline::before,
 .theme-firebug #debugger-controls .toolbarbutton-icon,
 .theme-firebug #filter-button .toolbarbutton-icon {
   filter: none !important;
 }
 
 /* CodeMirror Color Syntax */
 
 .theme-firebug .cm-keyword {color: BlueViolet; font-weight: bold;}
--- a/devtools/client/themes/layout.css
+++ b/devtools/client/themes/layout.css
@@ -1,72 +1,40 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/ */
 
-#sidebar-panel-layoutview {
-  display: block;
-  overflow: auto;
-  height: 100%;
-}
-
-#layout-wrapper {
-  /* The sidebar-panel is not focusable, this wrapper will catch click events in
-     all the empty area around the layout-container */
-  height: 100%;
-}
-
 #layout-container {
   /* The view will grow bigger as the window gets resized, until 400px */
   max-width: 400px;
   margin: 0px auto;
   padding: 0;
-  /* "Contain" the absolutely positioned #layout-main element */
-  position: relative;
-}
-
-/* Header: contains the position and size of the element */
-
-#layout-header {
-  box-sizing: border-box;
-  width: 100%;
-  padding: 4px 14px;
-  display: -moz-box;
-  vertical-align: top;
-}
-
-#layout-header:dir(rtl) {
-  -moz-box-direction: reverse;
 }
 
-#layout-header > span {
-  display: -moz-box;
-}
+/* Header */
 
-#layout-element-size {
-  -moz-box-flex: 1;
+#layout-header,
+#layout-info {
+  display: flex;
+  align-items: center;
+  padding: 4px 17px;
 }
 
-#layout-element-size:dir(rtl) {
-  -moz-box-pack: end;
+#layout-geometry-editor {
+  visibility: hidden;
 }
 
-@media (max-height: 250px) {
-  #layout-header {
-    padding-top: 0;
-    padding-bottom: 0;
-    margin-top: 10px;
-    margin-bottom: 8px;
-  }
+#layout-geometry-editor::before {
+  background: url(images/geometry-editor.svg) no-repeat center center / 16px 16px;
 }
 
 /* Main: contains the box-model regions */
 
 #layout-main {
-  position: absolute;
+  position: relative;
   box-sizing: border-box;
   /* The regions are semi-transparent, so the white background is partly
      visible */
   background-color: white;
   color: var(--theme-selection-color);
   /* Make sure there is some space between the window's edges and the regions */
   margin: 0 14px 10px 14px;
   width: calc(100% - 2 * 14px);
@@ -311,17 +279,17 @@
     left: 16px;
   }
 
   .layout-border.layout-right {
     right: 17px;
   }
 }
 
-/* Legend, displayed inside regions */
+/* Legend: displayed inside regions */
 
 .layout-legend {
   position: absolute;
   margin: 5px 6px;
   z-index: 1;
 }
 
 .layout-legend[data-box="margin"] {
@@ -351,28 +319,18 @@
 }
 
 /* Make sure the content size doesn't appear as editable like the other sizes */
 
 .layout-size > span {
   cursor: default;
 }
 
-/* Hide all values when the view is inactive */
+/* Layout info: contains the position and size of the element */
 
-#layout-container.inactive > #layout-header > #layout-element-position,
-#layout-container.inactive > #layout-header > #layout-element-size,
-#layout-container.inactive > #layout-main > p {
-   visibility: hidden;
+#layout-element-size {
+  flex: 1;
 }
 
 #layout-position-group {
   display: flex;
   align-items: center;
 }
-
-#layout-geometry-editor {
-  visibility: hidden;
-}
-
-#layout-geometry-editor::before {
-  background: url(images/geometry-editor.svg) no-repeat center center / 16px 16px;
-}
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -40,17 +40,16 @@
 #ruleview-toolbar-container {
   display: flex;
   flex-direction: column;
   height: auto;
 }
 
 #ruleview-toolbar {
   display: flex;
-  height: 23px;
 }
 
 #ruleview-toolbar > .devtools-searchbox:first-child {
   padding-inline-start: 0px;
 }
 
 #ruleview-command-toolbar {
   display: flex;
@@ -251,21 +250,19 @@
 .theme-firebug .ruleview-overridden .ruleview-propertyname,
 .theme-firebug .ruleview-overridden .ruleview-propertyvalue {
   text-decoration: line-through;
 }
 
 .theme-firebug .ruleview-enableproperty:not([checked]) ~ .ruleview-namecontainer,
 .theme-firebug .ruleview-enableproperty:not([checked]) ~ .ruleview-namecontainer *,
 .theme-firebug .ruleview-enableproperty:not([checked]) ~ .ruleview-propertyvaluecontainer,
-.theme-firebug .ruleview-enableproperty:not([checked]) ~ .ruleview-propertyvaluecontainer * {
-  color: #CCCCCC;
-}
-
-.theme-firebug .ruleview-enableproperty:not([checked]) ~ .ruleview-computedlist * {
+.theme-firebug .ruleview-enableproperty:not([checked]) ~ .ruleview-propertyvaluecontainer *,
+.theme-firebug .ruleview-overridden > * > .ruleview-computed:not(.ruleview-overridden),
+.theme-firebug .ruleview-overridden > * > .ruleview-computed:not(.ruleview-overridden) * {
   color: #CCCCCC;
 }
 
 .ruleview-rule + .ruleview-rule {
   border-top-width: 1px;
   border-top-style: dotted;
 }
 
--- a/devtools/client/themes/toolbars.css
+++ b/devtools/client/themes/toolbars.css
@@ -385,16 +385,17 @@
 .devtools-filterinput .textbox-input::-moz-placeholder {
   font-style: normal;
 }
 
 /* Searchbox is a div container element for a search input element */
 .devtools-searchbox {
   display: flex;
   flex: 1;
+  height: 23px;
   position: relative;
   padding: 0 3px;
 }
 
 /* The spacing is accomplished with a padding on the searchbox */
 .devtools-searchbox > .devtools-textinput,
 .devtools-searchbox > .devtools-searchinput,
 .devtools-searchbox > .devtools-filterinput {
--- a/devtools/server/actors/inspector.js
+++ b/devtools/server/actors/inspector.js
@@ -71,16 +71,17 @@ const {
   isAnonymous,
   isNativeAnonymous,
   isXBLAnonymous,
   isShadowAnonymous,
   getFrameElement
 } = require("devtools/shared/layout/utils");
 const {getLayoutChangesObserver, releaseLayoutChangesObserver} =
   require("devtools/server/actors/layout");
+const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
 
 loader.lazyRequireGetter(this, "CSS", "CSS");
 
 const {EventParsers} = require("devtools/shared/event-parsers");
 const {nodeSpec, nodeListSpec, walkerSpec, inspectorSpec} = require("devtools/shared/specs/inspector");
 
 const FONT_FAMILY_PREVIEW_TEXT = "The quick brown fox jumps over the lazy dog";
 const FONT_FAMILY_PREVIEW_TEXT_SIZE = 20;
@@ -2763,23 +2764,23 @@ function isNodeDead(node) {
 }
 
 /**
  * Wrapper for inDeepTreeWalker.  Adds filtering to the traversal methods.
  * See inDeepTreeWalker for more information about the methods.
  *
  * @param {DOMNode} node
  * @param {Window} rootWin
- * @param {Int} whatToShow See Ci.nsIDOMNodeFilter / inIDeepTreeWalker for
+ * @param {Int} whatToShow See nodeFilterConstants / inIDeepTreeWalker for
  * options.
  * @param {Function} filter A custom filter function Taking in a DOMNode
  *        and returning an Int. See WalkerActor.nodeFilter for an example.
  */
 function DocumentWalker(node, rootWin,
-    whatToShow = Ci.nsIDOMNodeFilter.SHOW_ALL,
+    whatToShow = nodeFilterConstants.SHOW_ALL,
     filter = standardTreeWalkerFilter) {
   if (!rootWin.location) {
     throw new Error("Got an invalid root window in DocumentWalker");
   }
 
   this.walker = Cc["@mozilla.org/inspector/deep-tree-walker;1"]
     .createInstance(Ci.inIDeepTreeWalker);
   this.walker.showAnonymousContent = true;
@@ -2788,17 +2789,17 @@ function DocumentWalker(node, rootWin,
   this.walker.init(rootWin.document, whatToShow);
   this.filter = filter;
 
   // Make sure that the walker knows about the initial node (which could
   // be skipped due to a filter).  Note that simply calling parentNode()
   // causes currentNode to be updated.
   this.walker.currentNode = node;
   while (node &&
-         this.filter(node) === Ci.nsIDOMNodeFilter.FILTER_SKIP) {
+         this.filter(node) === nodeFilterConstants.FILTER_SKIP) {
     node = this.walker.parentNode();
   }
 }
 
 DocumentWalker.prototype = {
   get node() {
     return this.walker.node;
   },
@@ -2819,64 +2820,64 @@ DocumentWalker.prototype = {
   nextNode: function () {
     let node = this.walker.currentNode;
     if (!node) {
       return null;
     }
 
     let nextNode = this.walker.nextNode();
     while (nextNode &&
-           this.filter(nextNode) === Ci.nsIDOMNodeFilter.FILTER_SKIP) {
+           this.filter(nextNode) === nodeFilterConstants.FILTER_SKIP) {
       nextNode = this.walker.nextNode();
     }
 
     return nextNode;
   },
 
   firstChild: function () {
     let node = this.walker.currentNode;
     if (!node) {
       return null;
     }
 
     let firstChild = this.walker.firstChild();
     while (firstChild &&
-           this.filter(firstChild) === Ci.nsIDOMNodeFilter.FILTER_SKIP) {
+           this.filter(firstChild) === nodeFilterConstants.FILTER_SKIP) {
       firstChild = this.walker.nextSibling();
     }
 
     return firstChild;
   },
 
   lastChild: function () {
     let node = this.walker.currentNode;
     if (!node) {
       return null;
     }
 
     let lastChild = this.walker.lastChild();
     while (lastChild &&
-           this.filter(lastChild) === Ci.nsIDOMNodeFilter.FILTER_SKIP) {
+           this.filter(lastChild) === nodeFilterConstants.FILTER_SKIP) {
       lastChild = this.walker.previousSibling();
     }
 
     return lastChild;
   },
 
   previousSibling: function () {
     let node = this.walker.previousSibling();
-    while (node && this.filter(node) === Ci.nsIDOMNodeFilter.FILTER_SKIP) {
+    while (node && this.filter(node) === nodeFilterConstants.FILTER_SKIP) {
       node = this.walker.previousSibling();
     }
     return node;
   },
 
   nextSibling: function () {
     let node = this.walker.nextSibling();
-    while (node && this.filter(node) === Ci.nsIDOMNodeFilter.FILTER_SKIP) {
+    while (node && this.filter(node) === nodeFilterConstants.FILTER_SKIP) {
       node = this.walker.nextSibling();
     }
     return node;
   }
 };
 
 function isInXULDocument(el) {
   let doc = nodeDocument(el);
@@ -2890,49 +2891,49 @@ function isInXULDocument(el) {
  * content with the exception of ::before and ::after and anonymous content
  * in XUL document (needed to show all elements in the browser toolbox).
  */
 function standardTreeWalkerFilter(node) {
   // ::before and ::after are native anonymous content, but we always
   // want to show them
   if (node.nodeName === "_moz_generated_content_before" ||
       node.nodeName === "_moz_generated_content_after") {
-    return Ci.nsIDOMNodeFilter.FILTER_ACCEPT;
+    return nodeFilterConstants.FILTER_ACCEPT;
   }
 
   // Ignore empty whitespace text nodes.
   if (node.nodeType == Ci.nsIDOMNode.TEXT_NODE &&
       !/[^\s]/.exec(node.nodeValue)) {
-    return Ci.nsIDOMNodeFilter.FILTER_SKIP;
+    return nodeFilterConstants.FILTER_SKIP;
   }
 
   // Ignore all native and XBL anonymous content inside a non-XUL document
   if (!isInXULDocument(node) && (isXBLAnonymous(node) ||
                                   isNativeAnonymous(node))) {
     // Note: this will skip inspecting the contents of feedSubscribeLine since
     // that's XUL content injected in an HTML document, but we need to because
     // this also skips many other elements that need to be skipped - like form
     // controls, scrollbars, video controls, etc (see bug 1187482).
-    return Ci.nsIDOMNodeFilter.FILTER_SKIP;
+    return nodeFilterConstants.FILTER_SKIP;
   }
 
-  return Ci.nsIDOMNodeFilter.FILTER_ACCEPT;
+  return nodeFilterConstants.FILTER_ACCEPT;
 }
 
 /**
  * This DeepTreeWalker filter is like standardTreeWalkerFilter except that
  * it also includes all anonymous content (like internal form controls).
  */
 function allAnonymousContentTreeWalkerFilter(node) {
   // Ignore empty whitespace text nodes.
   if (node.nodeType == Ci.nsIDOMNode.TEXT_NODE &&
       !/[^\s]/.exec(node.nodeValue)) {
-    return Ci.nsIDOMNodeFilter.FILTER_SKIP;
+    return nodeFilterConstants.FILTER_SKIP;
   }
-  return Ci.nsIDOMNodeFilter.FILTER_ACCEPT;
+  return nodeFilterConstants.FILTER_ACCEPT;
 }
 
 /**
  * Returns a promise that is settled once the given HTMLImageElement has
  * finished loading.
  *
  * @param {HTMLImageElement} image - The image element.
  * @param {Number} timeout - Maximum amount of time the image is allowed to load
--- a/devtools/server/tests/mochitest/test_inspector-anonymous.html
+++ b/devtools/server/tests/mochitest/test_inspector-anonymous.html
@@ -7,21 +7,22 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>Test for Bug 777674</title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
   <script type="application/javascript;version=1.8">
 window.onload = function() {
-  const Ci = Components.interfaces;
   const {InspectorFront} =
     require("devtools/shared/fronts/inspector");
   const {_documentWalker} =
     require("devtools/server/actors/inspector");
+  const nodeFilterConstants =
+    require("devtools/shared/dom-node-filter-constants");
   const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.webcomponents.enabled", true]
   ]});
   SimpleTest.waitForExplicitFinish();
 
   let gWalker = null;
@@ -71,19 +72,19 @@ window.onload = function() {
   addAsyncTest(function* testNativeAnonymousStartingNode() {
     info ("Tests attaching an element that a walker can't see.");
 
     let serverConnection = gWalker.conn._transport._serverConnection;
     let serverWalker = serverConnection.getActor(gWalker.actorID);
     let docwalker = new _documentWalker(
       gInspectee.querySelector("select"),
       gInspectee.defaultView,
-      Ci.nsIDOMNodeFilter.SHOW_ALL,
+      nodeFilterConstants.SHOW_ALL,
       () => {
-        return Ci.nsIDOMNodeFilter.FILTER_ACCEPT
+        return nodeFilterConstants.FILTER_ACCEPT
       }
     );
     let scrollbar = docwalker.lastChild();
     is (scrollbar.tagName, "scrollbar", "An anonymous child has been fetched");
 
     let node = yield serverWalker.attachElement(scrollbar);
 
     ok (node, "A response has arrived");
new file mode 100644
--- /dev/null
+++ b/devtools/shared/dom-node-filter-constants.js
@@ -0,0 +1,21 @@
+"use strict";
+
+module.exports = {
+  FILTER_ACCEPT: 1,
+  FILTER_REJECT: 2,
+  FILTER_SKIP: 3,
+
+  SHOW_ALL: 0xFFFFFFFF,
+  SHOW_ELEMENT: 0x00000001,
+  SHOW_ATTRIBUTE: 0x00000002,
+  SHOW_TEXT: 0x00000004,
+  SHOW_CDATA_SECTION: 0x00000008,
+  SHOW_ENTITY_REFERENCE: 0x00000010,
+  SHOW_ENTITY: 0x00000020,
+  SHOW_PROCESSING_INSTRUCTION: 0x00000040,
+  SHOW_COMMENT: 0x00000080,
+  SHOW_DOCUMENT: 0x00000100,
+  SHOW_DOCUMENT_TYPE: 0x00000200,
+  SHOW_DOCUMENT_FRAGMENT: 0x00000400,
+  SHOW_NOTATION: 0x00000800
+};
--- a/devtools/shared/inspector/css-logic.js
+++ b/devtools/shared/inspector/css-logic.js
@@ -35,17 +35,16 @@
  * CssLogic uses the standard DOM API, and the Gecko inIDOMUtils API to access
  * styling information in the page, and present this to the user in a way that
  * helps them understand:
  * - why their expectations may not have been fulfilled
  * - how browsers process CSS
  * @constructor
  */
 
-const { Cc, Ci } = require("chrome");
 const Services = require("Services");
 
 // This should be ok because none of the functions that use this should be used
 // on the worker thread, where Cu is not available.
 loader.lazyRequireGetter(this, "CSS", "CSS");
 
 loader.lazyRequireGetter(this, "CSSLexer", "devtools/shared/css-lexer");
 
@@ -139,17 +138,17 @@ const TAB_CHARS = "\t";
  * Prettify minified CSS text.
  * This prettifies CSS code where there is no indentation in usual places while
  * keeping original indentation as-is elsewhere.
  * @param string text The CSS source to prettify.
  * @return string Prettified CSS source
  */
 function prettifyCSS(text, ruleCount) {
   if (prettifyCSS.LINE_SEPARATOR == null) {
-    let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
+    let os = Services.appinfo.OS;
     prettifyCSS.LINE_SEPARATOR = (os === "WINNT" ? "\r\n" : "\n");
   }
 
   // remove initial and terminating HTML comments and surrounding whitespace
   text = text.replace(/(?:^\s*<!--[\r\n]*)|(?:\s*-->\s*$)/g, "");
   let originalText = text;
   text = text.trim();
 
--- a/devtools/shared/layout/utils.js
+++ b/devtools/shared/layout/utils.js
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Ci, Cc } = require("chrome");
 const { memoize } = require("sdk/lang/functional");
+const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
 
 loader.lazyRequireGetter(this, "setIgnoreLayoutChanges", "devtools/server/actors/layout", true);
 exports.setIgnoreLayoutChanges = (...args) =>
   this.setIgnoreLayoutChanges(...args);
 
 /**
  * Returns the `DOMWindowUtils` for the window given.
  *
@@ -372,17 +373,17 @@ function safelyGetContentWindow(frame) {
   if (frame.contentWindow) {
     return frame.contentWindow;
   }
 
   let walker = Cc["@mozilla.org/inspector/deep-tree-walker;1"]
                .createInstance(Ci.inIDeepTreeWalker);
   walker.showSubDocuments = true;
   walker.showDocumentsAsNodes = true;
-  walker.init(frame, Ci.nsIDOMNodeFilter.SHOW_ALL);
+  walker.init(frame, nodeFilterConstants.SHOW_ALL);
   walker.currentNode = frame;
 
   let document = walker.nextNode();
   if (!document || !document.defaultView) {
     throw new Error("Couldn't get the content window inside frame " + frame);
   }
 
   return document.defaultView;
--- a/devtools/shared/moz.build
+++ b/devtools/shared/moz.build
@@ -44,16 +44,17 @@ DevToolsModules(
     'content-observer.js',
     'css-lexer.js',
     'css-parsing-utils.js',
     'css-properties-db.js',
     'defer.js',
     'deprecated-sync-thenables.js',
     'DevToolsUtils.js',
     'dom-node-constants.js',
+    'dom-node-filter-constants.js',
     'event-emitter.js',
     'event-parsers.js',
     'indentation.js',
     'Loader.jsm',
     'Parser.jsm',
     'path.js',
     'protocol.js',
     'system.js',
--- a/devtools/shared/system.js
+++ b/devtools/shared/system.js
@@ -326,15 +326,14 @@ function getSetting(name) {
       handleError: (error) => deferred.reject(error),
     });
   } else {
     deferred.reject(new Error("No settings service"));
   }
   return deferred.promise;
 }
 
-exports.is64Bit = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).is64Bit;
 exports.getSystemInfo = Task.async(getSystemInfo);
 exports.getAppIniString = getAppIniString;
 exports.getSetting = getSetting;
 exports.getScreenDimensions = getScreenDimensions;
 exports.getOSCPU = getOSCPU;
 exports.constants = AppConstants;
--- a/dom/browser-element/mochitest/chrome.ini
+++ b/dom/browser-element/mochitest/chrome.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || e10s
+skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || e10s || toolkit == 'android' # Bug 1287720: takes too long on android
 
 support-files =
   audio.ogg
   async.js
   browserElementTestHelpers.js
   browserElement_ActiveStateChange.js
   browserElement_AudioChannelSeeking.js
   browserElement_AudioChannelMutedByDefault.js
--- a/dom/contacts/tests/chrome.ini
+++ b/dom/contacts/tests/chrome.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-
+skip-if = toolkit == 'android' # Bug 1287455: takes too long to complete on Android
 support-files =
   shared.js
   file_contacts_basics.html
   file_contacts_basics2.html
   file_contacts_blobs.html
   file_contacts_events.html
   file_contacts_getall.html
   file_contacts_getall2.html
@@ -15,21 +15,21 @@ support-files =
   test_migration_chrome.js
   file_migration.html
 
 # renaming with "_a_" to execure before others, since we hardcode open of 
 # database and this messes up with mozContacts when done after mozContacts
 # did opened the database. those should really be xpcshell and not chrome
 # mochitests maybe ...
 [test_contacts_a_shutdown.xul]
-skip-if = os == "android" || buildapp == 'b2g'
+skip-if = buildapp == 'b2g'
 [test_contacts_a_upgrade.xul]
-skip-if = os == "android" || buildapp == 'b2g'
+skip-if = buildapp == 'b2g'
 [test_contacts_a_cache.xul]
-skip-if = os == "android" || buildapp == 'b2g'
+skip-if = buildapp == 'b2g'
 [test_contacts_basics.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_basics2.html]
 skip-if = (toolkit == 'gonk' && debug) || (os == 'win' && os_version == '5.1') #debug-only failure, bug 967258 on XP
 [test_contacts_blobs.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_events.html]
 [test_contacts_getall.html]
@@ -37,9 +37,8 @@ skip-if = (toolkit == 'gonk' && debug) #
 [test_contacts_getall2.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_contacts_international.html]
 [test_contacts_substringmatching.html]
 [test_contacts_substringmatchingVE.html]
 [test_contacts_substringmatchingCL.html]
 [test_migration.html]
   support-files +=
-  skip-if = os == "android"
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -69,16 +69,17 @@
 #include "mozilla/dom/MediaSource.h"
 #include "MediaMetadataManager.h"
 #include "MediaSourceDecoder.h"
 #include "MediaStreamListener.h"
 #include "DOMMediaStream.h"
 #include "AudioStreamTrack.h"
 #include "VideoStreamTrack.h"
 #include "MediaTrackList.h"
+#include "MediaStreamError.h"
 
 #include "AudioChannelService.h"
 
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/WakeLock.h"
 
 #include "mozilla/dom/AudioTrack.h"
 #include "mozilla/dom/AudioTrackList.h"
@@ -1025,17 +1026,16 @@ void HTMLMediaElement::NoSupportedMediaS
   NS_ASSERTION(mNetworkState == NETWORK_LOADING,
                "Not loading during source selection?");
 
   mError = new MediaError(this, nsIDOMMediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
   ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE);
   DispatchAsyncEvent(NS_LITERAL_STRING("error"));
   ChangeDelayLoadStatus(false);
   UpdateAudioChannelPlayingState();
-  OpenUnsupportedMediaWithExtenalAppIfNeeded();
 }
 
 typedef void (HTMLMediaElement::*SyncSectionFn)();
 
 // Runs a "synchronous section", a function that must run once the event loop
 // has reached a "stable state". See:
 // http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section
 class nsSyncSection : public nsMediaEvent
@@ -2116,23 +2116,25 @@ public:
     return MediaSourceEnum::Other;
   }
 
   CORSMode GetCORSMode() const override
   {
     return mElement->GetCORSMode();
   }
 
-  already_AddRefed<Promise>
+  already_AddRefed<PledgeVoid>
   ApplyConstraints(nsPIDOMWindowInner* aWindow,
-                   const dom::MediaTrackConstraints& aConstraints,
-                   ErrorResult &aRv) override
+                   const dom::MediaTrackConstraints& aConstraints) override
   {
-    NS_ERROR("ApplyConstraints not implemented for media element capture");
-    return nullptr;
+    RefPtr<PledgeVoid> p = new PledgeVoid();
+    p->Reject(new dom::MediaStreamError(aWindow,
+                                        NS_LITERAL_STRING("OverconstrainedError"),
+                                        NS_LITERAL_STRING("")));
+    return p.forget();
   }
 
   void Stop() override
   {
     NS_ERROR("We're reporting remote=true to not be stoppable. "
              "Stop() should not be called.");
   }
 
@@ -2690,20 +2692,16 @@ HTMLMediaElement::PlayInternal(bool aCal
 
   // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
   // and our preload status.
   AddRemoveSelfReference();
   UpdatePreloadAction();
   UpdateSrcMediaStreamPlaying();
   UpdateAudioChannelPlayingState();
 
-  // The check here is to handle the case that the media element starts playing
-  // after it loaded fail. eg. preload the data before playing.
-  OpenUnsupportedMediaWithExtenalAppIfNeeded();
-
   return NS_OK;
 }
 
 NS_IMETHODIMP HTMLMediaElement::Play()
 {
   return PlayInternal(/* aCallerIsChrome = */ true);
 }
 
@@ -3194,52 +3192,54 @@ nsresult HTMLMediaElement::InitializeDec
 }
 
 nsresult HTMLMediaElement::InitializeDecoderForChannel(nsIChannel* aChannel,
                                                        nsIStreamListener** aListener)
 {
   NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
   NS_ASSERTION(mDecoder == nullptr, "Shouldn't have a decoder");
 
-  aChannel->GetContentType(mMimeType);
-  NS_ASSERTION(!mMimeType.IsEmpty(), "We should have the Content-Type.");
+  nsAutoCString mimeType;
+
+  aChannel->GetContentType(mimeType);
+  NS_ASSERTION(!mimeType.IsEmpty(), "We should have the Content-Type.");
 
   DecoderDoctorDiagnostics diagnostics;
   RefPtr<MediaDecoder> decoder =
-    DecoderTraits::CreateDecoder(mMimeType, this, &diagnostics);
+    DecoderTraits::CreateDecoder(mimeType, this, &diagnostics);
   diagnostics.StoreFormatDiagnostics(OwnerDoc(),
-                                     NS_ConvertASCIItoUTF16(mMimeType),
+                                     NS_ConvertASCIItoUTF16(mimeType),
                                      decoder != nullptr,
                                      __func__);
   if (!decoder) {
     nsAutoString src;
     GetCurrentSrc(src);
-    NS_ConvertUTF8toUTF16 mimeUTF16(mMimeType);
+    NS_ConvertUTF8toUTF16 mimeUTF16(mimeType);
     const char16_t* params[] = { mimeUTF16.get(), src.get() };
     ReportLoadError("MediaLoadUnsupportedMimeType", params, ArrayLength(params));
     return NS_ERROR_FAILURE;
   }
 
-  LOG(LogLevel::Debug, ("%p Created decoder %p for type %s", this, decoder.get(), mMimeType.get()));
+  LOG(LogLevel::Debug, ("%p Created decoder %p for type %s", this, decoder.get(), mimeType.get()));
 
   RefPtr<MediaResource> resource =
     MediaResource::Create(decoder->GetResourceCallback(), aChannel);
 
   if (!resource)
     return NS_ERROR_OUT_OF_MEMORY;
 
   if (mChannelLoader) {
     mChannelLoader->Done();
     mChannelLoader = nullptr;
   }
 
   // We postpone the |FinishDecoderSetup| function call until we get
   // |OnConnected| signal from MediaStreamController which is held by
   // RtspMediaResource.
-  if (DecoderTraits::DecoderWaitsForOnConnected(mMimeType)) {
+  if (DecoderTraits::DecoderWaitsForOnConnected(mimeType)) {
     decoder->SetResource(resource);
     SetDecoder(decoder);
     if (aListener) {
       *aListener = nullptr;
     }
     return NS_OK;
   } else {
     return FinishDecoderSetup(decoder, resource, aListener);
@@ -4709,22 +4709,20 @@ void HTMLMediaElement::SuspendOrResumeEl
   LOG(LogLevel::Debug, ("%p SuspendOrResumeElement(pause=%d, suspendEvents=%d) hidden=%d",
       this, aPauseElement, aSuspendEvents, OwnerDoc()->Hidden()));
 
   if (aPauseElement != mPausedForInactiveDocumentOrChannel) {
     mPausedForInactiveDocumentOrChannel = aPauseElement;
     UpdateSrcMediaStreamPlaying();
     UpdateAudioChannelPlayingState();
     if (aPauseElement) {
-      if (mMediaSource) {
-        ReportTelemetry();
+      ReportTelemetry();
 #ifdef MOZ_EME
-        ReportEMETelemetry();
+      ReportEMETelemetry();
 #endif
-      }
 
 #ifdef MOZ_EME
       // For EME content, force destruction of the CDM client (and CDM
       // instance if this is the last client for that CDM instance) and
       // the CDM's decoder. This ensures the CDM gets reliable and prompt
       // shutdown notifications, as it may have book-keeping it needs
       // to do on shutdown.
       if (mMediaKeys) {
@@ -5927,53 +5925,16 @@ HTMLMediaElement::IsAudible() const
   // Silent audio track.
   if (!mIsAudioTrackAudible) {
     return false;
   }
 
   return true;
 }
 
-bool
-HTMLMediaElement::HaveFailedWithSourceNotSupportedError() const
-{
-  if (!mError) {
-    return false;
-  }
-
-  uint16_t errorCode;
-  mError->GetCode(&errorCode);
-  return (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE &&
-          errorCode == nsIDOMMediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
-}
-
-void
-HTMLMediaElement::OpenUnsupportedMediaWithExtenalAppIfNeeded()
-{
-  if (!Preferences::GetBool("media.openUnsupportedTypeWithExternalApp")) {
-    return;
-  }
-
-  if (!HaveFailedWithSourceNotSupportedError()) {
-    return;
-  }
-
-  // If media doesn't start playing, we don't need to open it.
-  if (mPaused) {
-    return;
-  }
-
-  LOG(LogLevel::Debug, ("Open unsupported type \'%s\' with external apps.",
-      mMimeType.get()));
-  nsContentUtils::DispatchTrustedEvent(OwnerDoc(), static_cast<nsIContent*>(this),
-                                       NS_LITERAL_STRING("OpenMediaWithExternalApp"),
-                                       true,
-                                       true);
-}
-
 static const char* VisibilityString(Visibility aVisibility) {
   switch(aVisibility) {
     case Visibility::UNTRACKED: {
       return "UNTRACKED";
     }
     case Visibility::NONVISIBLE: {
       return "NONVISIBLE";
     }
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1188,19 +1188,16 @@ protected:
   void ResumeFromAudioChannelBlocked();
 
   bool IsSuspendedByAudioChannel() const;
   void SetAudioChannelSuspended(SuspendTypes aSuspend);
 
   bool IsAllowedToPlay();
 
   bool IsAudible() const;
-  bool HaveFailedWithSourceNotSupportedError() const;
-
-  void OpenUnsupportedMediaWithExtenalAppIfNeeded();
 
   class nsAsyncEventRunner;
   using nsGenericHTMLElement::DispatchEvent;
   // For nsAsyncEventRunner.
   nsresult DispatchEvent(const nsAString& aName);
 
   // The current decoder. Load() has been called on this decoder.
   // At most one of mDecoder and mSrcStream can be non-null.
@@ -1631,16 +1628,14 @@ private:
   // be seeked even before the media is loaded.
   double mDefaultPlaybackStartPosition;
 
   // True if the audio track is not silent.
   bool mIsAudioTrackAudible;
 
   // True if media element is audible for users.
   bool mAudible;
-
-  nsAutoCString mMimeType;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_HTMLMediaElement_h
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -8941,17 +8941,17 @@ public:
     return result.forget();
   }
 
   void
   NoteFinishedMaintenance(Maintenance* aMaintenance)
   {
     AssertIsOnBackgroundThread();
     MOZ_ASSERT(aMaintenance);
-    MOZ_ASSERT(mCurrentMaintenance = aMaintenance);
+    MOZ_ASSERT(mCurrentMaintenance == aMaintenance);
 
     mCurrentMaintenance = nullptr;
     ProcessMaintenanceQueue();
   }
 
   nsThreadPool*
   GetOrCreateThreadPool();
 
--- a/dom/media/AccurateSeekTask.cpp
+++ b/dom/media/AccurateSeekTask.cpp
@@ -319,16 +319,17 @@ AccurateSeekTask::OnAudioDecoded(MediaDa
 
   AdjustFastSeekIfNeeded(audio);
 
   if (mTarget.IsFast()) {
     // Non-precise seek; we can stop the seek at the first sample.
     mSeekedAudioData = audio;
     mDoneAudioSeeking = true;
   } else if (NS_FAILED(DropAudioUpToSeekTarget(audio))) {
+    CancelCallbacks();
     RejectIfExist(__func__);
     return;
   }
 
   if (!mDoneAudioSeeking) {
     RequestAudioData();
     return;
   }
@@ -346,16 +347,17 @@ AccurateSeekTask::OnNotDecoded(MediaData
 
   // Ignore pending requests from video-only seek.
   if (aType == MediaData::AUDIO_DATA && mTarget.IsVideoOnly()) {
     return;
   }
 
   if (aReason == MediaDecoderReader::DECODE_ERROR) {
     // If this is a decode error, delegate to the generic error path.
+    CancelCallbacks();
     RejectIfExist(__func__);
     return;
   }
 
   // If the decoder is waiting for data, we tell it to call us back when the
   // data arrives.
   if (aReason == MediaDecoderReader::WAITING_FOR_DATA) {
     mReader->WaitForData(aType);
@@ -410,16 +412,17 @@ AccurateSeekTask::OnVideoDecoded(MediaDa
 
   AdjustFastSeekIfNeeded(video);
 
   if (mTarget.IsFast()) {
     // Non-precise seek. We can stop the seek at the first sample.
     mSeekedVideoData = video;
     mDoneVideoSeeking = true;
   } else if (NS_FAILED(DropVideoUpToSeekTarget(video.get()))) {
+    CancelCallbacks();
     RejectIfExist(__func__);
     return;
   }
 
   if (!mDoneVideoSeeking) {
     RequestVideoData();
     return;
   }
@@ -470,14 +473,14 @@ AccurateSeekTask::SetCallbacks()
     }
   });
 }
 
 void
 AccurateSeekTask::CancelCallbacks()
 {
   AssertOwnerThread();
-  mAudioCallback.Disconnect();
-  mVideoCallback.Disconnect();
-  mAudioWaitCallback.Disconnect();
-  mVideoWaitCallback.Disconnect();
+  mAudioCallback.DisconnectIfExists();
+  mVideoCallback.DisconnectIfExists();
+  mAudioWaitCallback.DisconnectIfExists();
+  mVideoWaitCallback.DisconnectIfExists();
 }
 } // namespace mozilla
--- a/dom/media/AudioStreamTrack.h
+++ b/dom/media/AudioStreamTrack.h
@@ -11,18 +11,19 @@
 
 namespace mozilla {
 namespace dom {
 
 class AudioStreamTrack : public MediaStreamTrack {
 public:
   AudioStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
                    TrackID aInputTrackID,
-                   MediaStreamTrackSource* aSource)
-    : MediaStreamTrack(aStream, aTrackID, aInputTrackID, aSource) {}
+                   MediaStreamTrackSource* aSource,
+                   const MediaTrackConstraints& aConstraints = MediaTrackConstraints())
+    : MediaStreamTrack(aStream, aTrackID, aInputTrackID, aSource, aConstraints) {}
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   AudioStreamTrack* AsAudioStreamTrack() override { return this; }
 
   const AudioStreamTrack* AsAudioStreamTrack() const override { return this; }
 
   // WebIDL
@@ -30,16 +31,17 @@ public:
 
 protected:
   already_AddRefed<MediaStreamTrack> CloneInternal(DOMMediaStream* aOwningStream,
                                                    TrackID aTrackID) override
   {
     return do_AddRef(new AudioStreamTrack(aOwningStream,
                                           aTrackID,
                                           mInputTrackID,
-                                          mSource));
+                                          mSource,
+                                          mConstraints));
   }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* AUDIOSTREAMTRACK_H_ */
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -162,17 +162,22 @@ void InitBrandName()
 
 cubeb* GetCubebContextUnlocked()
 {
   sMutex.AssertCurrentThreadOwns();
   if (sCubebContext) {
     return sCubebContext;
   }
 
-  NS_WARN_IF_FALSE(sBrandName, "Could not get brandName?");
+  if (!sBrandName && NS_IsMainThread()) {
+    InitBrandName();
+  } else {
+    NS_WARN_IF_FALSE(sBrandName,
+        "Did not initialize sbrandName, and not on the main thread?");
+  }
 
   DebugOnly<int> rv = cubeb_init(&sCubebContext, sBrandName);
   NS_WARN_IF_FALSE(rv == CUBEB_OK, "Could not get a cubeb context.");
 
   return sCubebContext;
 }
 
 void ReportCubebBackendUsed()
--- a/dom/media/DOMMediaStream.cpp
+++ b/dom/media/DOMMediaStream.cpp
@@ -974,30 +974,31 @@ bool
 DOMMediaStream::RemovePrincipalChangeObserver(
   PrincipalChangeObserver<DOMMediaStream>* aObserver)
 {
   return mPrincipalChangeObservers.RemoveElement(aObserver);
 }
 
 MediaStreamTrack*
 DOMMediaStream::CreateDOMTrack(TrackID aTrackID, MediaSegment::Type aType,
-                               MediaStreamTrackSource* aSource)
+                               MediaStreamTrackSource* aSource,
+                               const MediaTrackConstraints& aConstraints)
 {
   MOZ_RELEASE_ASSERT(mInputStream);
   MOZ_RELEASE_ASSERT(mOwnedStream);
 
   MOZ_ASSERT(FindOwnedDOMTrack(GetInputStream(), aTrackID) == nullptr);
 
   MediaStreamTrack* track;
   switch (aType) {
   case MediaSegment::AUDIO:
-    track = new AudioStreamTrack(this, aTrackID, aTrackID, aSource);
+    track = new AudioStreamTrack(this, aTrackID, aTrackID, aSource, aConstraints);
     break;
   case MediaSegment::VIDEO:
-    track = new VideoStreamTrack(this, aTrackID, aTrackID, aSource);
+    track = new VideoStreamTrack(this, aTrackID, aTrackID, aSource, aConstraints);
     break;
   default:
     MOZ_CRASH("Unhandled track type");
   }
 
   LOG(LogLevel::Debug, ("DOMMediaStream %p Created new track %p with ID %u", this, track, aTrackID));
 
   mOwnedTracks.AppendElement(
--- a/dom/media/DOMMediaStream.h
+++ b/dom/media/DOMMediaStream.h
@@ -9,16 +9,17 @@
 #include "ImageContainer.h"
 
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 #include "StreamTracks.h"
 #include "nsIDOMWindow.h"
 #include "nsIPrincipal.h"
+#include "MediaTrackConstraints.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "PrincipalChangeObserver.h"
 
 // X11 has a #define for CurrentTime. Unbelievable :-(.
 // See dom/media/webaudio/AudioContext.h for more fun!
 #ifdef CurrentTime
 #undef CurrentTime
 #endif
@@ -43,17 +44,16 @@ class MediaStreamTrack;
 class MediaStreamTrackSource;
 class AudioStreamTrack;
 class VideoStreamTrack;
 class AudioTrack;
 class VideoTrack;
 class AudioTrackList;
 class VideoTrackList;
 class MediaTrackListListener;
-struct MediaTrackConstraints;
 } // namespace dom
 
 namespace layers {
 class ImageContainer;
 class OverlayImage;
 } // namespace layers
 
 namespace media {
@@ -517,17 +517,18 @@ public:
    * Creates a MediaStreamTrack, adds it to mTracks, raises "addtrack" and
    * returns it.
    *
    * Note that "addtrack" is raised synchronously and only has an effect if
    * this MediaStream is already exposed to script. For spec compliance this is
    * to be called from an async task.
    */
   MediaStreamTrack* CreateDOMTrack(TrackID aTrackID, MediaSegment::Type aType,
-                                   MediaStreamTrackSource* aSource);
+      MediaStreamTrackSource* aSource,
+      const MediaTrackConstraints& aConstraints = MediaTrackConstraints());
 
   /**
    * Creates a MediaStreamTrack cloned from aTrack, adds it to mTracks and
    * returns it.
    * aCloneTrackID is the TrackID the new track will get in mOwnedStream.
    */
   already_AddRefed<MediaStreamTrack> CloneDOMTrack(MediaStreamTrack& aTrack,
                                                    TrackID aCloneTrackID);
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -621,16 +621,19 @@ MediaDecoder::Shutdown()
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mShuttingDown) {
     return;
   }
 
   mShuttingDown = true;
 
+  // Unwatch all watch targets to prevent further notifications.
+  mWatchManager.Shutdown();
+
   mResourceCallback->Disconnect();
 
 #ifdef MOZ_EME
   mCDMProxyPromiseHolder.RejectIfExists(true, __func__);
 #endif
 
   // This changes the decoder state to SHUTDOWN and does other things
   // necessary to unblock the state machine thread if it's blocked, so
@@ -638,18 +641,16 @@ MediaDecoder::Shutdown()
   if (mDecoderStateMachine) {
     mTimedMetadataListener.Disconnect();
     mMetadataLoadedListener.Disconnect();
     mFirstFrameLoadedListener.Disconnect();
     mOnPlaybackEvent.Disconnect();
     mOnSeekingStart.Disconnect();
     mOnMediaNotSeekable.Disconnect();
 
-    mWatchManager.Unwatch(mIsAudioDataAudible, &MediaDecoder::NotifyAudibleStateChanged);
-
     mDecoderStateMachine->BeginShutdown()
       ->Then(AbstractThread::MainThread(), __func__, this,
              &MediaDecoder::FinishShutdown,
              &MediaDecoder::FinishShutdown);
   } else {
     // Ensure we always unregister asynchronously in order not to disrupt
     // the hashtable iterating in MediaShutdownManager::Shutdown().
     RefPtr<MediaDecoder> self = this;
@@ -1576,38 +1577,52 @@ MediaDecoder::SetPlaybackRate(double aPl
 void
 MediaDecoder::SetPreservesPitch(bool aPreservesPitch)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mPreservesPitch = aPreservesPitch;
 }
 
 void
+MediaDecoder::ConnectMirrors(MediaDecoderStateMachine* aObject)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aObject);
+  mStateMachineDuration.Connect(aObject->CanonicalDuration());
+  mBuffered.Connect(aObject->CanonicalBuffered());
+  mStateMachineIsShutdown.Connect(aObject->CanonicalIsShutdown());
+  mNextFrameStatus.Connect(aObject->CanonicalNextFrameStatus());
+  mCurrentPosition.Connect(aObject->CanonicalCurrentPosition());
+  mPlaybackPosition.Connect(aObject->CanonicalPlaybackOffset());
+  mIsAudioDataAudible.Connect(aObject->CanonicalIsAudioDataAudible());
+}
+
+void
+MediaDecoder::DisconnectMirrors()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  mStateMachineDuration.DisconnectIfConnected();
+  mBuffered.DisconnectIfConnected();
+  mStateMachineIsShutdown.DisconnectIfConnected();
+  mNextFrameStatus.DisconnectIfConnected();
+  mCurrentPosition.DisconnectIfConnected();
+  mPlaybackPosition.DisconnectIfConnected();
+  mIsAudioDataAudible.DisconnectIfConnected();
+}
+
+void
 MediaDecoder::SetStateMachine(MediaDecoderStateMachine* aStateMachine)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT_IF(aStateMachine, !mDecoderStateMachine);
   mDecoderStateMachine = aStateMachine;
-
-  if (mDecoderStateMachine) {
-    mStateMachineDuration.Connect(mDecoderStateMachine->CanonicalDuration());
-    mBuffered.Connect(mDecoderStateMachine->CanonicalBuffered());
-    mStateMachineIsShutdown.Connect(mDecoderStateMachine->CanonicalIsShutdown());
-    mNextFrameStatus.Connect(mDecoderStateMachine->CanonicalNextFrameStatus());
-    mCurrentPosition.Connect(mDecoderStateMachine->CanonicalCurrentPosition());
-    mPlaybackPosition.Connect(mDecoderStateMachine->CanonicalPlaybackOffset());
-    mIsAudioDataAudible.Connect(mDecoderStateMachine->CanonicalIsAudioDataAudible());
+  if (aStateMachine) {
+    ConnectMirrors(aStateMachine);
   } else {
-    mStateMachineDuration.DisconnectIfConnected();
-    mBuffered.DisconnectIfConnected();
-    mStateMachineIsShutdown.DisconnectIfConnected();
-    mNextFrameStatus.DisconnectIfConnected();
-    mCurrentPosition.DisconnectIfConnected();
-    mPlaybackPosition.DisconnectIfConnected();
-    mIsAudioDataAudible.DisconnectIfConnected();
+    DisconnectMirrors();
   }
 }
 
 ImageContainer*
 MediaDecoder::GetImageContainer()
 {
   return mVideoFrameContainer ? mVideoFrameContainer->GetImageContainer() : nullptr;
 }
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -598,16 +598,19 @@ private:
 
   void OnMediaNotSeekable()
   {
     SetMediaSeekable(false);
   }
 
   void FinishShutdown();
 
+  void ConnectMirrors(MediaDecoderStateMachine* aObject);
+  void DisconnectMirrors();
+
   MediaEventProducer<void> mDataArrivedEvent;
 
   // The state machine object for handling the decoding. It is safe to
   // call methods of this object from other threads. Its internal data
   // is synchronised on a monitor. The lifetime of this object is
   // after mPlayState is LOADING and before mPlayState is SHUTDOWN. It
   // is safe to access it during this period.
   //
--- a/dom/media/MediaDecoderReaderWrapper.h
+++ b/dom/media/MediaDecoderReaderWrapper.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MediaDecoderReaderWrapper_h_
 #define MediaDecoderReaderWrapper_h_
 
 #include "mozilla/AbstractThread.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/Variant.h"
 #include "nsISupportsImpl.h"
 
 #include "MediaDecoderReader.h"
 #include "MediaEventSource.h"
 
 namespace mozilla {
 
 class StartTimeRendezvous;
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1137,26 +1137,19 @@ MediaDecoderStateMachine::SetDormant(boo
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (IsShutdown()) {
     return;
   }
 
   if (mMetadataRequest.Exists()) {
-    if (mPendingDormant && mPendingDormant.ref() != aDormant && !aDormant) {
-      // We already have a dormant request pending; the new request would have
-      // resumed from dormant, we can just cancel any pending dormant requests.
-      mPendingDormant.reset();
-    } else {
-      mPendingDormant = Some(aDormant);
-    }
+    mPendingDormant = aDormant;
     return;
   }
-  mPendingDormant.reset();
 
   DECODER_LOG("SetDormant=%d", aDormant);
 
   if (aDormant) {
     if (mState == DECODER_STATE_SEEKING) {
       if (mQueuedSeek.Exists()) {
         // Keep latest seek target
       } else if (mCurrentSeek.Exists()) {
@@ -1198,17 +1191,17 @@ MediaDecoderStateMachine::SetDormant(boo
 
     // Note that we do not wait for the decode task queue to go idle before
     // queuing the ReleaseMediaResources task - instead, we disconnect promises,
     // reset state, and put a ResetDecode in the decode task queue. Any tasks
     // that run after ResetDecode are supposed to run with a clean slate. We rely
     // on that in other places (i.e. seeking), so it seems reasonable to rely on
     // it here as well.
     mReader->ReleaseMediaResources();
-  } else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) {
+  } else if (mState == DECODER_STATE_DORMANT) {
     mDecodingFirstFrame = true;
     SetState(DECODER_STATE_DECODING_METADATA);
     ReadMetadata();
   }
 }
 
 RefPtr<ShutdownPromise>
 MediaDecoderStateMachine::Shutdown()
@@ -1693,24 +1686,16 @@ MediaDecoderStateMachine::OnSeekTaskReso
     StopPrerollingAudio();
   }
 
   if (aValue.mIsVideoQueueFinished) {
     VideoQueue().Finish();
     StopPrerollingVideo();
   }
 
-  if (aValue.mNeedToStopPrerollingAudio) {
-    StopPrerollingAudio();
-  }
-
-  if (aValue.mNeedToStopPrerollingVideo) {
-    StopPrerollingVideo();
-  }
-
   SeekCompleted();
 }
 
 void
 MediaDecoderStateMachine::OnSeekTaskRejected(SeekTaskRejectValue aValue)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState == DECODER_STATE_SEEKING);
@@ -1722,24 +1707,16 @@ MediaDecoderStateMachine::OnSeekTaskReje
     StopPrerollingAudio();
   }
 
   if (aValue.mIsVideoQueueFinished) {
     VideoQueue().Finish();
     StopPrerollingVideo();
   }
 
-  if (aValue.mNeedToStopPrerollingAudio) {
-    StopPrerollingAudio();
-  }
-
-  if (aValue.mNeedToStopPrerollingVideo) {
-    StopPrerollingVideo();
-  }
-
   DecodeError();
 
   DiscardSeekTaskIfExist();
 }
 
 void
 MediaDecoderStateMachine::DiscardSeekTaskIfExist()
 {
@@ -1987,17 +1964,18 @@ MediaDecoderStateMachine::DecodeError()
 void
 MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA);
   mMetadataRequest.Complete();
 
   if (mPendingDormant) {
-    SetDormant(mPendingDormant.ref());
+    mPendingDormant = false;
+    SetDormant(true);
     return;
   }
 
   // Set mode to PLAYBACK after reading metadata.
   mResource->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
   mInfo = aMetadata->mInfo;
   mMetadataTags = aMetadata->mTags.forget();
   RefPtr<MediaDecoderStateMachine> self = this;
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -92,16 +92,17 @@ hardware (via AudioStream).
 #include "MediaDecoder.h"
 #include "MediaDecoderReader.h"
 #include "MediaDecoderOwner.h"
 #include "MediaEventSource.h"
 #include "MediaMetadataManager.h"
 #include "MediaStatistics.h"
 #include "MediaTimer.h"
 #include "ImageContainer.h"
+#include "SeekJob.h"
 #include "SeekTask.h"
 
 namespace mozilla {
 
 namespace media {
 class MediaSink;
 }
 
@@ -851,18 +852,20 @@ private:
   // the state machine thread. Synchronised via decoder monitor.
   // When data is being sent to a MediaStream, this is true when all data has
   // been written to the MediaStream.
   Watchable<bool> mAudioCompleted;
 
   // True if all video frames are already rendered.
   Watchable<bool> mVideoCompleted;
 
-  // Set if MDSM receives dormant request during reading metadata.
-  Maybe<bool> mPendingDormant;
+  // True if we need to enter dormant state after reading metadata. Note that
+  // we can't enter dormant state until reading metadata is done for some
+  // limitations of the reader.
+  bool mPendingDormant = false;
 
   // Flag whether we notify metadata before decoding the first frame or after.
   //
   // Note that the odd semantics here are designed to replicate the current
   // behavior where we notify the decoder each time we come out of dormant, but
   // send suppressed event visibility for those cases. This code can probably be
   // simplified.
   bool mNotifyMetadataBeforeFirstFrame;
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -42,16 +42,17 @@
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/dom/GetUserMediaRequestBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/Base64.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/media/MediaChild.h"
+#include "mozilla/media/MediaTaskUtils.h"
 #include "MediaTrackConstraints.h"
 #include "VideoUtils.h"
 #include "Latency.h"
 #include "nsProxyRelease.h"
 #include "nsNullPrincipal.h"
 #include "nsVariant.h"
 
 // For snprintf
@@ -350,16 +351,23 @@ public:
   bool CapturingBrowser()
   {
     MOZ_ASSERT(NS_IsMainThread());
     return mVideoDevice && !mStopped &&
            mVideoDevice->GetSource()->IsAvailable() &&
            mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
   }
 
+  void GetSettings(dom::MediaTrackSettings& aOutSettings)
+  {
+    if (mVideoDevice) {
+      mVideoDevice->GetSource()->GetSettings(aOutSettings);
+    }
+  }
+
   // implement in .cpp to avoid circular dependency with MediaOperationTask
   // Can be invoked from EITHER MainThread or MSG thread
   void Stop();
 
   void
   AudioConfig(bool aEchoOn, uint32_t aEcho,
               bool aAgcOn, uint32_t aAGC,
               bool aNoiseOn, uint32_t aNoise,
@@ -580,21 +588,21 @@ public:
         break;
 
       case MEDIA_STOP:
       case MEDIA_STOP_TRACK:
         {
           NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
           if (mAudioDevice) {
             mAudioDevice->GetSource()->Stop(source, kAudioTrack);
-            mAudioDevice->GetSource()->Deallocate();
+            mAudioDevice->Deallocate();
           }
           if (mVideoDevice) {
             mVideoDevice->GetSource()->Stop(source, kVideoTrack);
-            mVideoDevice->GetSource()->Deallocate();
+            mVideoDevice->Deallocate();
           }
           if (mType == MEDIA_STOP) {
             source->EndAllTrackAndFinish();
           }
 
           nsIRunnable *event =
             new GetUserMediaNotificationEvent(mListener,
                                               mType == MEDIA_STOP ?
@@ -780,31 +788,30 @@ MediaDevice::FitnessDistance(nsString aN
     params.mIdeal.Construct();
     params.mIdeal.Value().SetAsStringSequence() = aConstraint.GetAsStringSequence();
     return FitnessDistance(aN, params);
   } else {
     return FitnessDistance(aN, aConstraint.GetAsConstrainDOMStringParameters());
   }
 }
 
-// Reminder: add handling for new constraints both here and in GetSources below!
-
 uint32_t
 MediaDevice::GetBestFitnessDistance(
-    const nsTArray<const MediaTrackConstraintSet*>& aConstraintSets)
+    const nsTArray<const NormalizedConstraintSet*>& aConstraintSets)
 {
   nsString mediaSource;
   GetMediaSource(mediaSource);
 
   // This code is reused for audio, where the mediaSource constraint does
   // not currently have a function, but because it defaults to "camera" in
   // webidl, we ignore it for audio here.
   if (!mediaSource.EqualsASCII("microphone")) {
     for (const auto& constraint : aConstraintSets) {
-      if (mediaSource != constraint->mMediaSource) {
+      if (constraint->mMediaSource.mIdeal.find(mediaSource) ==
+          constraint->mMediaSource.mIdeal.end()) {
         return UINT32_MAX;
       }
     }
   }
   // Forward request to underlying object to interrogate per-mode capabilities.
   // Pass in device's origin-specific id for deviceId constraint comparison.
   nsString id;
   GetId(id);
@@ -880,36 +887,34 @@ VideoDevice::GetSource()
 }
 
 AudioDevice::Source*
 AudioDevice::GetSource()
 {
   return static_cast<Source*>(&*mSource);
 }
 
-nsresult VideoDevice::Allocate(const dom::MediaTrackConstraints &aConstraints,
-                               const MediaEnginePrefs &aPrefs,
-                               const nsACString& aOrigin) {
-  return GetSource()->Allocate(aConstraints, aPrefs, mID, aOrigin);
-}
-
-nsresult AudioDevice::Allocate(const dom::MediaTrackConstraints &aConstraints,
+nsresult MediaDevice::Allocate(const dom::MediaTrackConstraints &aConstraints,
                                const MediaEnginePrefs &aPrefs,
-                               const nsACString& aOrigin) {
-  return GetSource()->Allocate(aConstraints, aPrefs, mID, aOrigin);
+                               const nsACString& aOrigin,
+                               const char** aOutBadConstraint) {
+  return GetSource()->Allocate(aConstraints, aPrefs, mID, aOrigin,
+                               getter_AddRefs(mAllocationHandle),
+                               aOutBadConstraint);
 }
 
-nsresult VideoDevice::Restart(const dom::MediaTrackConstraints &aConstraints,
-                              const MediaEnginePrefs &aPrefs) {
-  return GetSource()->Restart(aConstraints, aPrefs, mID);
+nsresult MediaDevice::Restart(const dom::MediaTrackConstraints &aConstraints,
+                              const MediaEnginePrefs &aPrefs,
+                              const char** aOutBadConstraint) {
+  return GetSource()->Restart(mAllocationHandle, aConstraints, aPrefs, mID,
+                              aOutBadConstraint);
 }
 
-nsresult AudioDevice::Restart(const dom::MediaTrackConstraints &aConstraints,
-                              const MediaEnginePrefs &aPrefs) {
-  return GetSource()->Restart(aConstraints, aPrefs, mID);
+nsresult MediaDevice::Deallocate() {
+  return GetSource()->Deallocate(mAllocationHandle);
 }
 
 void
 MediaOperationTask::ReturnCallbackError(nsresult rv, const char* errorLog)
 {
   MM_LOG(("%s , rv=%d", errorLog, rv));
   NS_DispatchToMainThread(do_AddRef(new ReleaseMediaOperationResource(mStream.forget(),
     mOnTracksAvailableCallback.forget())));
@@ -921,16 +926,28 @@ MediaOperationTask::ReturnCallbackError(
     NS_LITERAL_STRING("InternalError"), log);
   NS_DispatchToMainThread(do_AddRef(
     new ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>(onSuccess,
                                                                  mOnFailure,
                                                                  *error,
                                                                  mWindowID)));
 }
 
+static bool
+IsOn(const OwningBooleanOrMediaTrackConstraints &aUnion) {
+  return !aUnion.IsBoolean() || aUnion.GetAsBoolean();
+}
+
+static const MediaTrackConstraints&
+GetInvariant(const OwningBooleanOrMediaTrackConstraints &aUnion) {
+  static const MediaTrackConstraints empty;
+  return aUnion.IsMediaTrackConstraints() ?
+      aUnion.GetAsMediaTrackConstraints() : empty;
+}
+
 /**
  * This class is only needed since fake tracks are added dynamically.
  * Instead of refactoring to add them explicitly we let the DOMMediaStream
  * query us for the source as they become available.
  * Since they are used only for testing the API surface, we make them very
  * simple.
  */
 class FakeTrackSourceGetter : public MediaStreamTrackSourceGetter
@@ -985,20 +1002,22 @@ class GetUserMediaStreamRunnable : publi
 {
 public:
   GetUserMediaStreamRunnable(
     nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aOnSuccess,
     nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
     uint64_t aWindowID,
     GetUserMediaCallbackMediaStreamListener* aListener,
     const nsCString& aOrigin,
+    const MediaStreamConstraints& aConstraints,
     AudioDevice* aAudioDevice,
     VideoDevice* aVideoDevice,
     PeerIdentity* aPeerIdentity)
-    : mAudioDevice(aAudioDevice)
+    : mConstraints(aConstraints)
+    , mAudioDevice(aAudioDevice)
     , mVideoDevice(aVideoDevice)
     , mWindowID(aWindowID)
     , mListener(aListener)
     , mOrigin(aOrigin)
     , mPeerIdentity(aPeerIdentity)
     , mManager(MediaManager::GetInstance())
   {
     mOnSuccess.swap(aOnSuccess);
@@ -1104,44 +1123,37 @@ public:
           return mSource;
         }
 
         const PeerIdentity* GetPeerIdentity() const override
         {
           return mPeerIdentity;
         }
 
-        already_AddRefed<Promise>
+        already_AddRefed<PledgeVoid>
         ApplyConstraints(nsPIDOMWindowInner* aWindow,
-                         const MediaTrackConstraints& aConstraints,
-                         ErrorResult &aRv) override
+                         const MediaTrackConstraints& aConstraints) override
         {
-          nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(aWindow);
-          RefPtr<Promise> promise = Promise::Create(go, aRv);
-
-          if (sInShutdown) {
-            RefPtr<MediaStreamError> error = new MediaStreamError(aWindow,
-                NS_LITERAL_STRING("AbortError"),
-                NS_LITERAL_STRING("In shutdown"));
-            promise->MaybeReject(error);
-            return promise.forget();
+          if (sInShutdown || !mListener) {
+            // Track has been stopped, or we are in shutdown. In either case
+            // there's no observable outcome, so pretend we succeeded.
+            RefPtr<PledgeVoid> p = new PledgeVoid();
+            p->Resolve(false);
+            return p.forget();
           }
-
-          typedef media::Pledge<bool, MediaStreamError*> PledgeVoid;
-
-          RefPtr<PledgeVoid> p =
-            mListener->ApplyConstraintsToTrack(aWindow, mTrackID, aConstraints);
-          p->Then([promise](bool& aDummy) mutable {
-            promise->MaybeResolve(false);
-          }, [promise](MediaStreamError*& reason) mutable {
-            promise->MaybeReject(reason);
-          });
-          return promise.forget();
+          return mListener->ApplyConstraintsToTrack(aWindow, mTrackID, aConstraints);
         }
 
+        void
+        GetSettings(dom::MediaTrackSettings& aOutSettings) override
+        {
+          if (mListener) {
+            mListener->GetSettings(aOutSettings);
+          }
+        }
 
         void Stop() override
         {
           if (mListener) {
             mListener->StopTrack(mTrackID);
             mListener = nullptr;
           }
         }
@@ -1172,27 +1184,31 @@ public:
       if (mAudioDevice) {
         nsString audioDeviceName;
         mAudioDevice->GetName(audioDeviceName);
         const MediaSourceEnum source =
           mAudioDevice->GetSource()->GetMediaSource();
         RefPtr<MediaStreamTrackSource> audioSource =
           new LocalTrackSource(principal, audioDeviceName, mListener, source,
                                kAudioTrack, mPeerIdentity);
-        domStream->CreateDOMTrack(kAudioTrack, MediaSegment::AUDIO, audioSource);
+        MOZ_ASSERT(IsOn(mConstraints.mAudio));
+        domStream->CreateDOMTrack(kAudioTrack, MediaSegment::AUDIO, audioSource,
+                                  GetInvariant(mConstraints.mAudio));
       }
       if (mVideoDevice) {
         nsString videoDeviceName;
         mVideoDevice->GetName(videoDeviceName);
         const MediaSourceEnum source =
           mVideoDevice->GetSource()->GetMediaSource();
         RefPtr<MediaStreamTrackSource> videoSource =
           new LocalTrackSource(principal, videoDeviceName, mListener, source,
                                kVideoTrack, mPeerIdentity);
-        domStream->CreateDOMTrack(kVideoTrack, MediaSegment::VIDEO, videoSource);
+        MOZ_ASSERT(IsOn(mConstraints.mVideo));
+        domStream->CreateDOMTrack(kVideoTrack, MediaSegment::VIDEO, videoSource,
+                                  GetInvariant(mConstraints.mVideo));
       }
       stream = domStream->GetInputStream()->AsSourceStream();
     }
 
     if (!domStream || sInShutdown) {
       nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget();
       LOG(("Returning error for getUserMedia() - no stream"));
 
@@ -1236,37 +1252,26 @@ public:
       RefPtr<Pledge<nsCString>> p = media::GetOriginKey(mOrigin, false, true);
     }
     return NS_OK;
   }
 
 private:
   nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
+  MediaStreamConstraints mConstraints;
   RefPtr<AudioDevice> mAudioDevice;
   RefPtr<VideoDevice> mVideoDevice;
   uint64_t mWindowID;
   RefPtr<GetUserMediaCallbackMediaStreamListener> mListener;
   nsCString mOrigin;
   RefPtr<PeerIdentity> mPeerIdentity;
   RefPtr<MediaManager> mManager; // get ref to this when creating the runnable
 };
 
-static bool
-IsOn(const OwningBooleanOrMediaTrackConstraints &aUnion) {
-  return !aUnion.IsBoolean() || aUnion.GetAsBoolean();
-}
-
-static const MediaTrackConstraints&
-GetInvariant(const OwningBooleanOrMediaTrackConstraints &aUnion) {
-  static const MediaTrackConstraints empty;
-  return aUnion.IsMediaTrackConstraints() ?
-      aUnion.GetAsMediaTrackConstraints() : empty;
-}
-
 // Source getter returning full list
 
 template<class DeviceType>
 static void
 GetSources(MediaEngine *engine, MediaSourceEnum aSrcType,
            void (MediaEngine::* aEnumerate)(MediaSourceEnum,
                nsTArray<RefPtr<typename DeviceType::Source> >*),
            nsTArray<RefPtr<DeviceType>>& aResult,
@@ -1337,21 +1342,21 @@ MediaManager::SelectSettings(
     }
     sources.Clear();
     const char* badConstraint = nullptr;
     bool needVideo = IsOn(aConstraints.mVideo);
     bool needAudio = IsOn(aConstraints.mAudio);
 
     if (needVideo && videos.Length()) {
       badConstraint = MediaConstraintsHelper::SelectSettings(
-          GetInvariant(aConstraints.mVideo), videos);
+          NormalizedConstraints(GetInvariant(aConstraints.mVideo)), videos);
     }
     if (!badConstraint && needAudio && audios.Length()) {
       badConstraint = MediaConstraintsHelper::SelectSettings(
-          GetInvariant(aConstraints.mAudio), audios);
+          NormalizedConstraints(GetInvariant(aConstraints.mAudio)), audios);
     }
     if (!badConstraint &&
         !needVideo == !videos.Length() &&
         !needAudio == !audios.Length()) {
       for (auto& video : videos) {
         sources.AppendElement(video);
       }
       for (auto& audio : audios) {
@@ -1400,18 +1405,20 @@ public:
     , mSourceSet(aSourceSet)
     , mManager(MediaManager::GetInstance())
   {}
 
   ~GetUserMediaTask() {
   }
 
   void
-  Fail(const nsAString& aName, const nsAString& aMessage = EmptyString()) {
-    RefPtr<MediaMgrError> error = new MediaMgrError(aName, aMessage);
+  Fail(const nsAString& aName,
+       const nsAString& aMessage = EmptyString(),
+       const nsAString& aConstraint = EmptyString()) {
+    RefPtr<MediaMgrError> error = new MediaMgrError(aName, aMessage, aConstraint);
     RefPtr<ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>> runnable =
       new ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>(mOnSuccess,
                                                                    mOnFailure,
                                                                    *error,
                                                                    mWindowID);
     // These should be empty now
     MOZ_ASSERT(!mOnSuccess);
     MOZ_ASSERT(!mOnFailure);
@@ -1428,49 +1435,75 @@ public:
     MOZ_ASSERT(mOnSuccess);
     MOZ_ASSERT(mOnFailure);
     MOZ_ASSERT(mDeviceChosen);
 
     // Allocate a video or audio device and return a MediaStream via
     // a GetUserMediaStreamRunnable.
 
     nsresult rv;
+    const char* errorMsg = nullptr;
+    const char* badConstraint = nullptr;
 
     if (mAudioDevice) {
-      rv = mAudioDevice->Allocate(GetInvariant(mConstraints.mAudio),
-                                  mPrefs, mOrigin);
+      auto& constraints = GetInvariant(mConstraints.mAudio);
+      rv = mAudioDevice->Allocate(constraints, mPrefs, mOrigin, &badConstraint);
       if (NS_FAILED(rv)) {
-        LOG(("Failed to allocate audiosource %d",rv));
-        Fail(NS_LITERAL_STRING("NotReadableError"),
-             NS_LITERAL_STRING("Failed to allocate audiosource"));
-        return NS_OK;
+        errorMsg = "Failed to allocate audiosource";
+        if (rv == NS_ERROR_NOT_AVAILABLE && !badConstraint) {
+          nsTArray<RefPtr<AudioDevice>> audios;
+          audios.AppendElement(mAudioDevice);
+          badConstraint = MediaConstraintsHelper::SelectSettings(
+              NormalizedConstraints(constraints), audios);
+        }
       }
     }
-    if (mVideoDevice) {
-      rv = mVideoDevice->Allocate(GetInvariant(mConstraints.mVideo),
-                                  mPrefs, mOrigin);
+    if (!errorMsg && mVideoDevice) {
+      auto& constraints = GetInvariant(mConstraints.mVideo);
+      rv = mVideoDevice->Allocate(constraints, mPrefs, mOrigin, &badConstraint);
       if (NS_FAILED(rv)) {
-        LOG(("Failed to allocate videosource %d\n",rv));
+        errorMsg = "Failed to allocate videosource";
+        if (rv == NS_ERROR_NOT_AVAILABLE && !badConstraint) {
+          nsTArray<RefPtr<VideoDevice>> videos;
+          videos.AppendElement(mVideoDevice);
+          badConstraint = MediaConstraintsHelper::SelectSettings(
+              NormalizedConstraints(constraints), videos);
+        }
         if (mAudioDevice) {
-          mAudioDevice->GetSource()->Deallocate();
+          mAudioDevice->Deallocate();
         }
-        Fail(NS_LITERAL_STRING("NotReadableError"),
-             NS_LITERAL_STRING("Failed to allocate videosource"));
-        return NS_OK;
       }
     }
+    if (errorMsg) {
+      LOG(("%s %d", errorMsg, rv));
+      switch (rv) {
+        case NS_ERROR_NOT_AVAILABLE: {
+          MOZ_ASSERT(badConstraint);
+          Fail(NS_LITERAL_STRING("OverconstrainedError"),
+               NS_LITERAL_STRING(""),
+               NS_ConvertUTF8toUTF16(badConstraint));
+          break;
+        }
+        default:
+          Fail(NS_LITERAL_STRING("NotReadableError"),
+               NS_ConvertUTF8toUTF16(errorMsg));
+          break;
+      }
+      return NS_OK;
+    }
     PeerIdentity* peerIdentity = nullptr;
     if (!mConstraints.mPeerIdentity.IsEmpty()) {
       peerIdentity = new PeerIdentity(mConstraints.mPeerIdentity);
     }
 
     NS_DispatchToMainThread(do_AddRef(
         new GetUserMediaStreamRunnable(mOnSuccess, mOnFailure, mWindowID,
-                                       mListener, mOrigin, mAudioDevice,
-                                       mVideoDevice, peerIdentity)));
+                                       mListener, mOrigin, mConstraints,
+                                       mAudioDevice, mVideoDevice,
+                                       peerIdentity)));
     MOZ_ASSERT(!mOnSuccess);
     MOZ_ASSERT(!mOnFailure);
     return NS_OK;
   }
 
   nsresult
   Denied(const nsAString& aName,
          const nsAString& aMessage = EmptyString())
@@ -3377,24 +3410,24 @@ GetUserMediaCallbackMediaStreamListener:
                                     dom::AudioChannel::Normal);
     graph->UnregisterCaptureStreamForWindow(mWindowID);
     mStream->Destroy();
   }
 }
 
 // ApplyConstraints for track
 
-already_AddRefed<media::Pledge<bool, dom::MediaStreamError*>>
+auto
 GetUserMediaCallbackMediaStreamListener::ApplyConstraintsToTrack(
     nsPIDOMWindowInner* aWindow,
     TrackID aTrackID,
-    const MediaTrackConstraints& aConstraints)
+    const MediaTrackConstraints& aConstraints) -> already_AddRefed<PledgeVoid>
 {
   MOZ_ASSERT(NS_IsMainThread());
-  RefPtr<media::Pledge<bool, dom::MediaStreamError*>> p = new media::Pledge<bool, dom::MediaStreamError*>();
+  RefPtr<PledgeVoid> p = new PledgeVoid();
 
   // XXX to support multiple tracks of a type in a stream, this should key off
   // the TrackID and not just the type
   RefPtr<AudioDevice> audioDevice =
     aTrackID == kAudioTrack ? mAudioDevice.get() : nullptr;
   RefPtr<VideoDevice> videoDevice =
     aTrackID == kVideoTrack ? mVideoDevice.get() : nullptr;
 
@@ -3414,40 +3447,40 @@ GetUserMediaCallbackMediaStreamListener:
                                       audioDevice, videoDevice,
                                       aConstraints]() mutable {
     MOZ_ASSERT(MediaManager::IsInMediaThread());
     RefPtr<MediaManager> mgr = MediaManager::GetInstance();
     const char* badConstraint = nullptr;
     nsresult rv = NS_OK;
 
     if (audioDevice) {
-      rv = audioDevice->Restart(aConstraints, mgr->mPrefs);
-      if (rv == NS_ERROR_NOT_AVAILABLE) {
+      rv = audioDevice->Restart(aConstraints, mgr->mPrefs, &badConstraint);
+      if (rv == NS_ERROR_NOT_AVAILABLE && !badConstraint) {
         nsTArray<RefPtr<AudioDevice>> audios;
         audios.AppendElement(audioDevice);
-        badConstraint = MediaConstraintsHelper::SelectSettings(aConstraints,
-                                                               audios);
+        badConstraint = MediaConstraintsHelper::SelectSettings(
+            NormalizedConstraints(aConstraints), audios);
       }
     } else {
-      rv = videoDevice->Restart(aConstraints, mgr->mPrefs);
-      if (rv == NS_ERROR_NOT_AVAILABLE) {
+      rv = videoDevice->Restart(aConstraints, mgr->mPrefs, &badConstraint);
+      if (rv == NS_ERROR_NOT_AVAILABLE && !badConstraint) {
         nsTArray<RefPtr<VideoDevice>> videos;
         videos.AppendElement(videoDevice);
-        badConstraint = MediaConstraintsHelper::SelectSettings(aConstraints,
-                                                               videos);
+        badConstraint = MediaConstraintsHelper::SelectSettings(
+            NormalizedConstraints(aConstraints), videos);
       }
     }
     NS_DispatchToMainThread(NewRunnableFrom([id, windowId, rv,
                                              badConstraint]() mutable {
       MOZ_ASSERT(NS_IsMainThread());
       RefPtr<MediaManager> mgr = MediaManager_GetInstance();
       if (!mgr) {
         return NS_OK;
       }
-      RefPtr<media::Pledge<bool, dom::MediaStreamError*>> p = mgr->mOutstandingVoidPledges.Remove(id);
+      RefPtr<PledgeVoid> p = mgr->mOutstandingVoidPledges.Remove(id);
       if (p) {
         if (NS_SUCCEEDED(rv)) {
           p->Resolve(false);
         } else {
           auto* window = nsGlobalWindow::GetInnerWindowWithId(windowId);
           if (window) {
             if (rv == NS_ERROR_NOT_AVAILABLE) {
               nsString constraint;
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -58,72 +58,75 @@ class GetUserMediaCallbackMediaStreamLis
 class GetUserMediaTask;
 
 extern LogModule* GetMediaManagerLog();
 #define MM_LOG(msg) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, msg)
 
 class MediaDevice : public nsIMediaDevice
 {
 public:
+  typedef MediaEngineSource Source;
+
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEDIADEVICE
 
   void SetId(const nsAString& aID);
   virtual uint32_t GetBestFitnessDistance(
-      const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets);
+      const nsTArray<const NormalizedConstraintSet*>& aConstraintSets);
+  virtual Source* GetSource() = 0;
+  nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
+                    const MediaEnginePrefs &aPrefs,
+                    const nsACString& aOrigin,
+                    const char** aOutBadConstraint);
+  nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
+                   const MediaEnginePrefs &aPrefs,
+                   const char** aOutBadConstraint);
+  nsresult Deallocate();
 protected:
   virtual ~MediaDevice() {}
   explicit MediaDevice(MediaEngineSource* aSource, bool aIsVideo);
+
   static uint32_t FitnessDistance(nsString aN,
     const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aConstraint);
 private:
   static bool StringsContain(const dom::OwningStringOrStringSequence& aStrings,
                              nsString aN);
   static uint32_t FitnessDistance(nsString aN,
       const dom::ConstrainDOMStringParameters& aParams);
 protected:
   nsString mName;
   nsString mID;
   dom::MediaSourceEnum mMediaSource;
   RefPtr<MediaEngineSource> mSource;
+  RefPtr<MediaEngineSource::BaseAllocationHandle> mAllocationHandle;
 public:
   dom::MediaSourceEnum GetMediaSource() {
     return mMediaSource;
   }
   bool mIsVideo;
 };
 
 class VideoDevice : public MediaDevice
 {
 public:
   typedef MediaEngineVideoSource Source;
 
   explicit VideoDevice(Source* aSource);
-  NS_IMETHOD GetType(nsAString& aType);
-  Source* GetSource();
-  nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
-                    const MediaEnginePrefs &aPrefs,
-                    const nsACString& aOrigin);
-  nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
-                   const MediaEnginePrefs &aPrefs);
+  NS_IMETHOD GetType(nsAString& aType) override;
+  Source* GetSource() override;
 };
 
 class AudioDevice : public MediaDevice
 {
 public:
   typedef MediaEngineAudioSource Source;
 
   explicit AudioDevice(Source* aSource);
-  NS_IMETHOD GetType(nsAString& aType);
-  Source* GetSource();
-  nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
-                    const MediaEnginePrefs &aPrefs,
-                    const nsACString& aOrigin);
-  nsresult Restart(const dom::MediaTrackConstraints &aConstraints,
-                   const MediaEnginePrefs &aPrefs);
+  NS_IMETHOD GetType(nsAString& aType) override;
+  Source* GetSource() override;
 };
 
 class GetUserMediaNotificationEvent: public Runnable
 {
   public:
     enum GetUserMediaStatus {
       STARTING,
       STOPPING,
@@ -251,16 +254,17 @@ public:
 
   MediaEnginePrefs mPrefs;
 
   typedef nsTArray<RefPtr<MediaDevice>> SourceSet;
   static bool IsPrivateBrowsing(nsPIDOMWindowInner* window);
 private:
   typedef media::Pledge<SourceSet*, dom::MediaStreamError*> PledgeSourceSet;
   typedef media::Pledge<const char*, dom::MediaStreamError*> PledgeChar;
+  typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
 
   static bool IsPrivileged();
   static bool IsLoop(nsIURI* aDocURI);
   static nsresult GenerateUUID(nsAString& aResult);
   static nsresult AnonymizeId(nsAString& aId, const nsACString& aOriginKey);
 public: // TODO: make private once we upgrade to GCC 4.8+ on linux.
   static void AnonymizeDevices(SourceSet& aDevices, const nsACString& aOriginKey);
   static already_AddRefed<nsIWritableVariant> ToJSArray(SourceSet& aDevices);
@@ -316,17 +320,17 @@ private:
 
   // ONLY accessed from MediaManagerThread
   RefPtr<MediaEngine> mBackend;
 
   static StaticRefPtr<MediaManager> sSingleton;
 
   media::CoatCheck<PledgeSourceSet> mOutstandingPledges;
   media::CoatCheck<PledgeChar> mOutstandingCharPledges;
-  media::CoatCheck<media::Pledge<bool, dom::MediaStreamError*>> mOutstandingVoidPledges;
+  media::CoatCheck<PledgeVoid> mOutstandingVoidPledges;
 #if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
   RefPtr<nsDOMCameraManager> mCameraManager;
 #endif
 public:
   media::CoatCheck<media::Pledge<nsCString>> mGetOriginKeyPledges;
   UniquePtr<media::Parent<media::NonE10s>> mNonE10sParent;
 };
 
--- a/dom/media/MediaStreamTrack.cpp
+++ b/dom/media/MediaStreamTrack.cpp
@@ -5,16 +5,19 @@
 
 #include "MediaStreamTrack.h"
 
 #include "DOMMediaStream.h"
 #include "MediaStreamGraph.h"
 #include "nsIUUIDGenerator.h"
 #include "nsServiceManagerUtils.h"
 #include "MediaStreamListener.h"
+#include "systemservices/MediaUtils.h"
+
+#include "mozilla/dom/Promise.h"
 
 #ifdef LOG
 #undef LOG
 #endif
 
 static PRLogModuleInfo* gMediaStreamTrackLog;
 #define LOG(type, msg) MOZ_LOG(gMediaStreamTrackLog, type, msg)
 
@@ -33,31 +36,26 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Me
   tmp->Destroy();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaStreamTrackSource)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-already_AddRefed<Promise>
-MediaStreamTrackSource::ApplyConstraints(nsPIDOMWindowInner* aWindow,
-                                         const dom::MediaTrackConstraints& aConstraints,
-                                         ErrorResult &aRv)
+auto
+MediaStreamTrackSource::ApplyConstraints(
+    nsPIDOMWindowInner* aWindow,
+    const dom::MediaTrackConstraints& aConstraints) -> already_AddRefed<PledgeVoid>
 {
-  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(aWindow);
-  RefPtr<Promise> promise = Promise::Create(go, aRv);
-  MOZ_RELEASE_ASSERT(!aRv.Failed());
-
-  promise->MaybeReject(new MediaStreamError(
-    aWindow,
-    NS_LITERAL_STRING("OverconstrainedError"),
-    NS_LITERAL_STRING(""),
-    NS_LITERAL_STRING("")));
-  return promise.forget();
+  RefPtr<PledgeVoid> p = new PledgeVoid();
+  p->Reject(new MediaStreamError(aWindow,
+                                 NS_LITERAL_STRING("OverconstrainedError"),
+                                 NS_LITERAL_STRING("")));
+  return p.forget();
 }
 
 /**
  * PrincipalHandleListener monitors changes in PrincipalHandle of the media flowing
  * through the MediaStreamGraph.
  *
  * When the main thread principal for a MediaStreamTrack changes, its principal
  * will be set to the combination of the previous principal and the new one.
@@ -107,22 +105,24 @@ public:
 
 protected:
   // These fields may only be accessed on the main thread
   MediaStreamTrack* mTrack;
 };
 
 MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
                                    TrackID aInputTrackID,
-                                   MediaStreamTrackSource* aSource)
+                                   MediaStreamTrackSource* aSource,
+                                   const MediaTrackConstraints& aConstraints)
   : mOwningStream(aStream), mTrackID(aTrackID),
     mInputTrackID(aInputTrackID), mSource(aSource),
     mPrincipal(aSource->GetPrincipal()),
     mReadyState(MediaStreamTrackState::Live),
-    mEnabled(true), mRemote(aSource->IsRemote())
+    mEnabled(true), mRemote(aSource->IsRemote()),
+    mConstraints(aConstraints)
 {
 
   if (!gMediaStreamTrackLog) {
     gMediaStreamTrackLog = PR_NewLogModule("MediaStreamTrack");
   }
 
   GetSource().RegisterSink(this);
 
@@ -245,30 +245,62 @@ MediaStreamTrack::Stop()
   DOMMediaStream::TrackPort* port = mOwningStream->FindOwnedTrackPort(*this);
   MOZ_ASSERT(port, "A MediaStreamTrack must exist in its owning DOMMediaStream");
   RefPtr<Pledge<bool>> p = port->BlockSourceTrackId(mInputTrackID, BlockingMode::CREATION);
   Unused << p;
 
   mReadyState = MediaStreamTrackState::Ended;
 }
 
+void
+MediaStreamTrack::GetConstraints(dom::MediaTrackConstraints& aResult)
+{
+  aResult = mConstraints;
+}
+
+void
+MediaStreamTrack::GetSettings(dom::MediaTrackSettings& aResult)
+{
+  GetSource().GetSettings(aResult);
+}
+
 already_AddRefed<Promise>
 MediaStreamTrack::ApplyConstraints(const MediaTrackConstraints& aConstraints,
                                    ErrorResult &aRv)
 {
   if (MOZ_LOG_TEST(gMediaStreamTrackLog, LogLevel::Info)) {
     nsString str;
     aConstraints.ToJSON(str);
 
     LOG(LogLevel::Info, ("MediaStreamTrack %p ApplyConstraints() with "
                          "constraints %s", this, NS_ConvertUTF16toUTF8(str).get()));
   }
 
+  typedef media::Pledge<bool, MediaStreamError*> PledgeVoid;
+
   nsPIDOMWindowInner* window = mOwningStream->GetParentObject();
-  return GetSource().ApplyConstraints(window, aConstraints, aRv);
+
+  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
+  RefPtr<Promise> promise = Promise::Create(go, aRv);
+
+  // Forward constraints to the source.
+  //
+  // After GetSource().ApplyConstraints succeeds (after it's been to media-thread
+  // and back), and no sooner, do we set mConstraints to the newly applied values.
+
+  // Keep a reference to this, to make sure it's still here when we get back.
+  RefPtr<MediaStreamTrack> that = this;
+  RefPtr<PledgeVoid> p = GetSource().ApplyConstraints(window, aConstraints);
+  p->Then([this, that, promise, aConstraints](bool& aDummy) mutable {
+    mConstraints = aConstraints;
+    promise->MaybeResolve(false);
+  }, [promise](MediaStreamError*& reason) mutable {
+    promise->MaybeReject(reason);
+  });
+  return promise.forget();
 }
 
 MediaStreamGraph*
 MediaStreamTrack::Graph()
 {
   return GetOwnedStream()->Graph();
 }
 
--- a/dom/media/MediaStreamTrack.h
+++ b/dom/media/MediaStreamTrack.h
@@ -9,16 +9,19 @@
 #include "mozilla/DOMEventTargetHelper.h"
 #include "nsError.h"
 #include "nsID.h"
 #include "nsIPrincipal.h"
 #include "StreamTracks.h"
 #include "MediaTrackConstraints.h"
 #include "mozilla/CORSMode.h"
 #include "PrincipalChangeObserver.h"
+#include "mozilla/dom/MediaStreamTrackBinding.h"
+#include "mozilla/dom/MediaTrackSettingsBinding.h"
+#include "mozilla/media/MediaUtils.h"
 
 namespace mozilla {
 
 class DOMMediaStream;
 class MediaEnginePhotoCallback;
 class MediaInputPort;
 class MediaStream;
 class MediaStreamGraph;
@@ -31,16 +34,17 @@ class PeerIdentity;
 class ProcessedMediaStream;
 class RemoteSourceStreamInfo;
 class SourceStreamInfo;
 
 namespace dom {
 
 class AudioStreamTrack;
 class VideoStreamTrack;
+class MediaStreamError;
 
 /**
  * Common interface through which a MediaStreamTrack can communicate with its
  * producer on the main thread.
  *
  * Kept alive by a strong ref in all MediaStreamTracks (original and clones)
  * sharing this source.
  */
@@ -115,24 +119,31 @@ public:
 
   /**
    * Forwards a photo request to backends that support it. Other backends return
    * NS_ERROR_NOT_IMPLEMENTED to indicate that a MediaStreamGraph-based fallback
    * should be used.
    */
   virtual nsresult TakePhoto(MediaEnginePhotoCallback*) const { return NS_ERROR_NOT_IMPLEMENTED; }
 
+  typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
+
   /**
    * We provide a fallback solution to ApplyConstraints() here.
    * Sources that support ApplyConstraints() will have to override it.
    */
-  virtual already_AddRefed<Promise>
+  virtual already_AddRefed<PledgeVoid>
   ApplyConstraints(nsPIDOMWindowInner* aWindow,
-                   const dom::MediaTrackConstraints& aConstraints,
-                   ErrorResult &aRv);
+                   const dom::MediaTrackConstraints& aConstraints);
+
+  /**
+   * Same for GetSettings (no-op).
+   */
+  virtual void
+  GetSettings(dom::MediaTrackSettings& aResult) {};
 
   /**
    * Called by the source interface when all registered sinks have unregistered.
    */
   virtual void Stop() = 0;
 
   /**
    * Called by each MediaStreamTrack clone on initialization.
@@ -203,16 +214,19 @@ public:
                                        const MediaSourceEnum aMediaSource =
                                          MediaSourceEnum::Other)
     : MediaStreamTrackSource(aPrincipal, true, nsString())
     , mMediaSource(aMediaSource)
   {}
 
   MediaSourceEnum GetMediaSource() const override { return mMediaSource; }
 
+  void
+  GetSettings(dom::MediaTrackSettings& aResult) override {}
+
   void Stop() override {}
 
 protected:
   ~BasicUnstoppableTrackSource() {}
 
   const MediaSourceEnum mMediaSource;
 };
 
@@ -235,18 +249,19 @@ class MediaStreamTrack : public DOMEvent
   class PrincipalHandleListener;
 
 public:
   /**
    * aTrackID is the MediaStreamGraph track ID for the track in the
    * MediaStream owned by aStream.
    */
   MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
-                   TrackID aInputTrackID,
-                   MediaStreamTrackSource* aSource);
+      TrackID aInputTrackID,
+      MediaStreamTrackSource* aSource,
+      const MediaTrackConstraints& aConstraints = MediaTrackConstraints());
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamTrack,
                                            DOMEventTargetHelper)
 
   nsPIDOMWindowInner* GetParentObject() const;
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override = 0;
 
@@ -258,16 +273,19 @@ public:
 
   // WebIDL
   virtual void GetKind(nsAString& aKind) = 0;
   void GetId(nsAString& aID) const;
   void GetLabel(nsAString& aLabel) { GetSource().GetLabel(aLabel); }
   bool Enabled() { return mEnabled; }
   void SetEnabled(bool aEnabled);
   void Stop();
+  void GetConstraints(dom::MediaTrackConstraints& aResult);
+  void GetSettings(dom::MediaTrackSettings& aResult);
+
   already_AddRefed<Promise>
   ApplyConstraints(const dom::MediaTrackConstraints& aConstraints, ErrorResult &aRv);
   already_AddRefed<MediaStreamTrack> Clone();
   MediaStreamTrackState ReadyState() { return mReadyState; }
 
   IMPL_EVENT_HANDLER(ended)
 
   /**
@@ -421,14 +439,15 @@ protected:
   // Keep tracking MediaStreamTrackListener and DirectMediaStreamTrackListener,
   // so we can remove them in |Destory|.
   nsTArray<RefPtr<MediaStreamTrackListener>> mTrackListeners;
   nsTArray<RefPtr<DirectMediaStreamTrackListener>> mDirectTrackListeners;
   nsString mID;
   MediaStreamTrackState mReadyState;
   bool mEnabled;
   const bool mRemote;
+  dom::MediaTrackConstraints mConstraints;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* MEDIASTREAMTRACK_H_ */
--- a/dom/media/SeekTask.cpp
+++ b/dom/media/SeekTask.cpp
@@ -16,18 +16,16 @@ SeekTask::SeekTask(const void* aDecoderI
                    const SeekTarget& aTarget)
   : mDecoderID(aDecoderID)
   , mOwnerThread(aThread)
   , mReader(aReader)
   , mTarget(aTarget)
   , mIsDiscarded(false)
   , mIsAudioQueueFinished(false)
   , mIsVideoQueueFinished(false)
-  , mNeedToStopPrerollingAudio(false)
-  , mNeedToStopPrerollingVideo(false)
 {
   AssertOwnerThread();
 }
 
 SeekTask::~SeekTask()
 {
   AssertOwnerThread();
   MOZ_ASSERT(mIsDiscarded);
@@ -38,32 +36,28 @@ SeekTask::Resolve(const char* aCallSite)
 {
   AssertOwnerThread();
 
   SeekTaskResolveValue val;
   val.mSeekedAudioData = mSeekedAudioData;
   val.mSeekedVideoData = mSeekedVideoData;
   val.mIsAudioQueueFinished = mIsAudioQueueFinished;
   val.mIsVideoQueueFinished = mIsVideoQueueFinished;
-  val.mNeedToStopPrerollingAudio = mNeedToStopPrerollingAudio;
-  val.mNeedToStopPrerollingVideo = mNeedToStopPrerollingVideo;
 
   mSeekTaskPromise.Resolve(val, aCallSite);
 }
 
 void
 SeekTask::RejectIfExist(const char* aCallSite)
 {
   AssertOwnerThread();
 
   SeekTaskRejectValue val;
   val.mIsAudioQueueFinished = mIsAudioQueueFinished;
   val.mIsVideoQueueFinished = mIsVideoQueueFinished;
-  val.mNeedToStopPrerollingAudio = mNeedToStopPrerollingAudio;
-  val.mNeedToStopPrerollingVideo = mNeedToStopPrerollingVideo;
 
   mSeekTaskPromise.RejectIfExists(val, aCallSite);
 }
 
 void
 SeekTask::AssertOwnerThread() const
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
--- a/dom/media/SeekTask.h
+++ b/dom/media/SeekTask.h
@@ -4,45 +4,40 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef SEEK_TASK_H
 #define SEEK_TASK_H
 
 #include "mozilla/MozPromise.h"
 #include "MediaCallbackID.h"
-#include "SeekJob.h"
+#include "SeekTarget.h"
 
 namespace mozilla {
 
 class AbstractThread;
 class MediaData;
 class MediaDecoderReaderWrapper;
-class MediaInfo;
 
 namespace media {
 class TimeUnit;
 }
 
 struct SeekTaskResolveValue
 {
   RefPtr<MediaData> mSeekedAudioData;
   RefPtr<MediaData> mSeekedVideoData;
   bool mIsAudioQueueFinished;
   bool mIsVideoQueueFinished;
-  bool mNeedToStopPrerollingAudio;
-  bool mNeedToStopPrerollingVideo;
 };
 
 struct SeekTaskRejectValue
 {
   bool mIsAudioQueueFinished;
   bool mIsVideoQueueFinished;
-  bool mNeedToStopPrerollingAudio;
-  bool mNeedToStopPrerollingVideo;
 };
 
 class SeekTask {
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SeekTask)
 
 public:
   static const bool IsExclusive = true;
@@ -90,15 +85,13 @@ protected:
 
   /*
    * Information which are going to be returned to MDSM.
    */
   RefPtr<MediaData> mSeekedAudioData;
   RefPtr<MediaData> mSeekedVideoData;
   bool mIsAudioQueueFinished;
   bool mIsVideoQueueFinished;
-  bool mNeedToStopPrerollingAudio;
-  bool mNeedToStopPrerollingVideo;
 };
 
 } // namespace mozilla
 
 #endif /* SEEK_TASK_H */
--- a/dom/media/VideoStreamTrack.h
+++ b/dom/media/VideoStreamTrack.h
@@ -11,18 +11,19 @@
 
 namespace mozilla {
 namespace dom {
 
 class VideoStreamTrack : public MediaStreamTrack {
 public:
   VideoStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
                    TrackID aInputTrackID,
-                   MediaStreamTrackSource* aSource)
-    : MediaStreamTrack(aStream, aTrackID, aInputTrackID, aSource) {}
+                   MediaStreamTrackSource* aSource,
+                   const MediaTrackConstraints& aConstraints = MediaTrackConstraints())
+    : MediaStreamTrack(aStream, aTrackID, aInputTrackID, aSource, aConstraints) {}
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   VideoStreamTrack* AsVideoStreamTrack() override { return this; }
 
   const VideoStreamTrack* AsVideoStreamTrack() const override { return this; }
 
   // WebIDL
@@ -30,16 +31,17 @@ public:
 
 protected:
   already_AddRefed<MediaStreamTrack> CloneInternal(DOMMediaStream* aOwningStream,
                                                    TrackID aTrackID) override
   {
     return do_AddRef(new VideoStreamTrack(aOwningStream,
                                           aTrackID,
                                           mInputTrackID,
-                                          mSource));
+                                          mSource,
+                                          mConstraints));
   }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* VIDEOSTREAMTRACK_H_ */
--- a/dom/media/platforms/apple/AppleDecoderModule.cpp
+++ b/dom/media/platforms/apple/AppleDecoderModule.cpp
@@ -2,32 +2,29 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AppleATDecoder.h"
 #include "AppleCMLinker.h"
 #include "AppleDecoderModule.h"
-#include "AppleVDADecoder.h"
-#include "AppleVDALinker.h"
 #include "AppleVTDecoder.h"
 #include "AppleVTLinker.h"
 #include "MacIOSurfaceImage.h"
 #include "MediaPrefs.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Logging.h"
 
 namespace mozilla {
 
 bool AppleDecoderModule::sInitialized = false;
 bool AppleDecoderModule::sIsCoreMediaAvailable = false;
 bool AppleDecoderModule::sIsVTAvailable = false;
 bool AppleDecoderModule::sIsVTHWAvailable = false;
-bool AppleDecoderModule::sIsVDAAvailable = false;
 bool AppleDecoderModule::sCanUseHardwareVideoDecoder = true;
 
 AppleDecoderModule::AppleDecoderModule()
 {
 }
 
 AppleDecoderModule::~AppleDecoderModule()
 {
@@ -40,19 +37,16 @@ AppleDecoderModule::Init()
   if (sInitialized) {
     return;
   }
 
   // Ensure IOSurface framework is loaded.
   MacIOSurfaceLib::LoadLibrary();
   const bool loaded = MacIOSurfaceLib::isInit();
 
-  // dlopen VideoDecodeAcceleration.framework if it's available.
-  sIsVDAAvailable = loaded && AppleVDALinker::Link();
-
   // dlopen CoreMedia.framework if it's available.
   sIsCoreMediaAvailable = AppleCMLinker::Link();
   // dlopen VideoToolbox.framework if it's available.
   // We must link both CM and VideoToolbox framework to allow for proper
   // paired Link/Unlink calls
   bool haveVideoToolbox = loaded && AppleVTLinker::Link();
   sIsVTAvailable = sIsCoreMediaAvailable && haveVideoToolbox;
 
@@ -62,46 +56,30 @@ AppleDecoderModule::Init()
     gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding();
 
   sInitialized = true;
 }
 
 nsresult
 AppleDecoderModule::Startup()
 {
-  if (!sInitialized || (!sIsVDAAvailable && !sIsVTAvailable)) {
+  if (!sInitialized || !sIsVTAvailable) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 already_AddRefed<MediaDataDecoder>
 AppleDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
 {
-  RefPtr<MediaDataDecoder> decoder;
-
-  if (sIsVDAAvailable && (!sIsVTHWAvailable || MediaPrefs::AppleForceVDA())) {
-    decoder =
-      AppleVDADecoder::CreateVDADecoder(aParams.VideoConfig(),
-                                        aParams.mTaskQueue,
-                                        aParams.mCallback,
-                                        aParams.mImageContainer);
-    if (decoder) {
-      return decoder.forget();
-    }
-  }
-  // We fallback here if VDA isn't available, or is available but isn't
-  // supported by the current platform.
-  if (sIsVTAvailable) {
-    decoder =
-      new AppleVTDecoder(aParams.VideoConfig(),
-                         aParams.mTaskQueue,
-                         aParams.mCallback,
-                         aParams.mImageContainer);
-  }
+  RefPtr<MediaDataDecoder> decoder =
+    new AppleVTDecoder(aParams.VideoConfig(),
+                       aParams.mTaskQueue,
+                       aParams.mCallback,
+                       aParams.mImageContainer);
   return decoder.forget();
 }
 
 already_AddRefed<MediaDataDecoder>
 AppleDecoderModule::CreateAudioDecoder(const CreateDecoderParams& aParams)
 {
   RefPtr<MediaDataDecoder> decoder =
     new AppleATDecoder(aParams.AudioConfig(),
@@ -112,19 +90,18 @@ AppleDecoderModule::CreateAudioDecoder(c
 
 bool
 AppleDecoderModule::SupportsMimeType(const nsACString& aMimeType,
                                      DecoderDoctorDiagnostics* aDiagnostics) const
 {
   return (sIsCoreMediaAvailable &&
           (aMimeType.EqualsLiteral("audio/mpeg") ||
            aMimeType.EqualsLiteral("audio/mp4a-latm"))) ||
-    ((sIsVTAvailable || sIsVDAAvailable) &&
-     (aMimeType.EqualsLiteral("video/mp4") ||
-      aMimeType.EqualsLiteral("video/avc")));
+    (sIsVTAvailable && (aMimeType.EqualsLiteral("video/mp4") ||
+                        aMimeType.EqualsLiteral("video/avc")));
 }
 
 PlatformDecoderModule::ConversionRequired
 AppleDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const
 {
   if (aConfig.IsVideo()) {
     return kNeedAVCC;
   } else {
--- a/dom/media/platforms/apple/AppleDecoderModule.h
+++ b/dom/media/platforms/apple/AppleDecoderModule.h
@@ -36,14 +36,13 @@ public:
 
   static bool sCanUseHardwareVideoDecoder;
 
 private:
   static bool sInitialized;
   static bool sIsCoreMediaAvailable;
   static bool sIsVTAvailable;
   static bool sIsVTHWAvailable;
-  static bool sIsVDAAvailable;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_AppleDecoderModule_h
deleted file mode 100644
--- a/dom/media/platforms/apple/AppleVDADecoder.cpp
+++ /dev/null
@@ -1,700 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include <CoreFoundation/CFString.h>
-
-#include "AppleDecoderModule.h"
-#include "AppleUtils.h"
-#include "AppleVDADecoder.h"
-#include "AppleVDALinker.h"
-#include "MediaInfo.h"
-#include "mp4_demuxer/H264.h"
-#include "MP4Decoder.h"
-#include "MediaData.h"
-#include "mozilla/ArrayUtils.h"
-#include "mozilla/SyncRunnable.h"
-#include "nsThreadUtils.h"
-#include "mozilla/Logging.h"
-#include "VideoUtils.h"
-#include <algorithm>
-#include "gfxPlatform.h"
-
-#ifndef MOZ_WIDGET_UIKIT
-#include "MacIOSurfaceImage.h"
-#endif
-
-#define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
-//#define LOG_MEDIA_SHA1
-
-namespace mozilla {
-
-static uint32_t ComputeMaxRefFrames(const MediaByteBuffer* aExtraData)
-{
-  uint32_t maxRefFrames = 4;
-  // Retrieve video dimensions from H264 SPS NAL.
-  mp4_demuxer::SPSData spsdata;
-  if (mp4_demuxer::H264::DecodeSPSFromExtraData(aExtraData, spsdata)) {
-    // max_num_ref_frames determines the size of the sliding window
-    // we need to queue that many frames in order to guarantee proper
-    // pts frames ordering. Use a minimum of 4 to ensure proper playback of
-    // non compliant videos.
-    maxRefFrames =
-      std::min(std::max(maxRefFrames, spsdata.max_num_ref_frames + 1), 16u);
-  }
-  return maxRefFrames;
-}
-
-AppleVDADecoder::AppleVDADecoder(const VideoInfo& aConfig,
-                                 TaskQueue* aTaskQueue,
-                                 MediaDataDecoderCallback* aCallback,
-                                 layers::ImageContainer* aImageContainer)
-  : mExtraData(aConfig.mExtraData)
-  , mCallback(aCallback)
-  , mPictureWidth(aConfig.mImage.width)
-  , mPictureHeight(aConfig.mImage.height)
-  , mDisplayWidth(aConfig.mDisplay.width)
-  , mDisplayHeight(aConfig.mDisplay.height)
-  , mQueuedSamples(0)
-  , mTaskQueue(aTaskQueue)
-  , mDecoder(nullptr)
-  , mMaxRefFrames(ComputeMaxRefFrames(aConfig.mExtraData))
-  , mImageContainer(aImageContainer)
-  , mInputIncoming(0)
-  , mIsShutDown(false)
-#ifdef MOZ_WIDGET_UIKIT
-  , mUseSoftwareImages(true)
-#else
-  , mUseSoftwareImages(false)
-#endif
-  , mMonitor("AppleVideoDecoder")
-  , mIsFlushing(false)
-{
-  MOZ_COUNT_CTOR(AppleVDADecoder);
-  // TODO: Verify aConfig.mime_type.
-
-  LOG("Creating AppleVDADecoder for %dx%d (%dx%d) h.264 video",
-      mPictureWidth,
-      mPictureHeight,
-      mDisplayWidth,
-      mDisplayHeight
-     );
-}
-
-AppleVDADecoder::~AppleVDADecoder()
-{
-  MOZ_COUNT_DTOR(AppleVDADecoder);
-}
-
-RefPtr<MediaDataDecoder::InitPromise>
-AppleVDADecoder::Init()
-{
-  return InitPromise::CreateAndResolve(TrackType::kVideoTrack, __func__);
-}
-
-nsresult
-AppleVDADecoder::Shutdown()
-{
-  MOZ_DIAGNOSTIC_ASSERT(!mIsShutDown);
-  mIsShutDown = true;
-  if (mTaskQueue) {
-    nsCOMPtr<nsIRunnable> runnable =
-      NewRunnableMethod(this, &AppleVDADecoder::ProcessShutdown);
-    mTaskQueue->Dispatch(runnable.forget());
-  } else {
-    ProcessShutdown();
-  }
-  return NS_OK;
-}
-
-void
-AppleVDADecoder::ProcessShutdown()
-{
-  if (mDecoder) {
-    LOG("%s: cleaning up decoder %p", __func__, mDecoder);
-    VDADecoderDestroy(mDecoder);
-    mDecoder = nullptr;
-  }
-}
-
-nsresult
-AppleVDADecoder::Input(MediaRawData* aSample)
-{
-  MOZ_ASSERT(mCallback->OnReaderTaskQueue());
-
-  LOG("mp4 input sample %p pts %lld duration %lld us%s %d bytes",
-      aSample,
-      aSample->mTime,
-      aSample->mDuration,
-      aSample->mKeyframe ? " keyframe" : "",
-      aSample->Size());
-
-  mInputIncoming++;
-
-  mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>(
-    this, &AppleVDADecoder::ProcessDecode, aSample));
-  return NS_OK;
-}
-
-nsresult
-AppleVDADecoder::Flush()
-{
-  MOZ_ASSERT(mCallback->OnReaderTaskQueue());
-  mIsFlushing = true;
-  nsCOMPtr<nsIRunnable> runnable =
-    NewRunnableMethod(this, &AppleVDADecoder::ProcessFlush);
-  SyncRunnable::DispatchToThread(mTaskQueue, runnable);
-  mIsFlushing = false;
-  // All ProcessDecode() tasks should be done.
-  MOZ_ASSERT(mInputIncoming == 0);
-
-  mSeekTargetThreshold.reset();
-
-  return NS_OK;
-}
-
-nsresult
-AppleVDADecoder::Drain()
-{
-  MOZ_ASSERT(mCallback->OnReaderTaskQueue());
-  nsCOMPtr<nsIRunnable> runnable =
-    NewRunnableMethod(this, &AppleVDADecoder::ProcessDrain);
-  mTaskQueue->Dispatch(runnable.forget());
-  return NS_OK;
-}
-
-void
-AppleVDADecoder::ProcessFlush()
-{
-  AssertOnTaskQueueThread();
-
-  OSStatus rv = VDADecoderFlush(mDecoder, 0 /*dont emit*/);
-  if (rv != noErr) {
-    LOG("AppleVDADecoder::Flush failed waiting for platform decoder "
-        "with error:%d.", rv);
-  }
-  ClearReorderedFrames();
-}
-
-void
-AppleVDADecoder::ProcessDrain()
-{
-  AssertOnTaskQueueThread();
-
-  OSStatus rv = VDADecoderFlush(mDecoder, kVDADecoderFlush_EmitFrames);
-  if (rv != noErr) {
-    LOG("AppleVDADecoder::Drain failed waiting for platform decoder "
-        "with error:%d.", rv);
-  }
-  DrainReorderedFrames();
-  mCallback->DrainComplete();
-}
-
-//
-// Implementation details.
-//
-
-// Callback passed to the VideoToolbox decoder for returning data.
-// This needs to be static because the API takes a C-style pair of
-// function and userdata pointers. This validates parameters and
-// forwards the decoded image back to an object method.
-static void
-PlatformCallback(void* decompressionOutputRefCon,
-                 CFDictionaryRef frameInfo,
-                 OSStatus status,
-                 VDADecodeInfoFlags infoFlags,
-                 CVImageBufferRef image)
-{
-  LOG("AppleVDADecoder[%s] status %d flags %d retainCount %ld",
-      __func__, status, infoFlags, CFGetRetainCount(frameInfo));
-
-  // Validate our arguments.
-  // According to Apple's TN2267
-  // The output callback is still called for all flushed frames,
-  // but no image buffers will be returned.
-  // FIXME: Distinguish between errors and empty flushed frames.
-  if (status != noErr || !image) {
-    NS_WARNING("AppleVDADecoder decoder returned no data");
-    image = nullptr;
-  } else if (infoFlags & kVDADecodeInfo_FrameDropped) {
-    NS_WARNING("  ...frame dropped...");
-    image = nullptr;
-  } else {
-    MOZ_ASSERT(image || CFGetTypeID(image) == CVPixelBufferGetTypeID(),
-               "AppleVDADecoder returned an unexpected image type");
-  }
-
-  AppleVDADecoder* decoder =
-    static_cast<AppleVDADecoder*>(decompressionOutputRefCon);
-
-  AutoCFRelease<CFNumberRef> ptsref =
-    (CFNumberRef)CFDictionaryGetValue(frameInfo, CFSTR("FRAME_PTS"));
-  AutoCFRelease<CFNumberRef> dtsref =
-    (CFNumberRef)CFDictionaryGetValue(frameInfo, CFSTR("FRAME_DTS"));
-  AutoCFRelease<CFNumberRef> durref =
-    (CFNumberRef)CFDictionaryGetValue(frameInfo, CFSTR("FRAME_DURATION"));
-  AutoCFRelease<CFNumberRef> boref =
-    (CFNumberRef)CFDictionaryGetValue(frameInfo, CFSTR("FRAME_OFFSET"));
-  AutoCFRelease<CFNumberRef> kfref =
-    (CFNumberRef)CFDictionaryGetValue(frameInfo, CFSTR("FRAME_KEYFRAME"));
-
-  int64_t dts;
-  int64_t pts;
-  int64_t duration;
-  int64_t byte_offset;
-  char is_sync_point;
-
-  CFNumberGetValue(ptsref, kCFNumberSInt64Type, &pts);
-  CFNumberGetValue(dtsref, kCFNumberSInt64Type, &dts);
-  CFNumberGetValue(durref, kCFNumberSInt64Type, &duration);
-  CFNumberGetValue(boref, kCFNumberSInt64Type, &byte_offset);
-  CFNumberGetValue(kfref, kCFNumberSInt8Type, &is_sync_point);
-
-  AppleVDADecoder::AppleFrameRef frameRef(
-      media::TimeUnit::FromMicroseconds(dts),
-      media::TimeUnit::FromMicroseconds(pts),
-      media::TimeUnit::FromMicroseconds(duration),
-      byte_offset,
-      is_sync_point == 1);
-
-  decoder->OutputFrame(image, frameRef);
-}
-
-AppleVDADecoder::AppleFrameRef*
-AppleVDADecoder::CreateAppleFrameRef(const MediaRawData* aSample)
-{
-  MOZ_ASSERT(aSample);
-  return new AppleFrameRef(*aSample);
-}
-
-void
-AppleVDADecoder::DrainReorderedFrames()
-{
-  MonitorAutoLock mon(mMonitor);
-  while (!mReorderQueue.IsEmpty()) {
-    mCallback->Output(mReorderQueue.Pop().get());
-  }
-  mQueuedSamples = 0;
-}
-
-void
-AppleVDADecoder::ClearReorderedFrames()
-{
-  MonitorAutoLock mon(mMonitor);
-  while (!mReorderQueue.IsEmpty()) {
-    mReorderQueue.Pop();
-  }
-  mQueuedSamples = 0;
-}
-
-void
-AppleVDADecoder::SetSeekThreshold(const media::TimeUnit& aTime)
-{
-  LOG("SetSeekThreshold %lld", aTime.ToMicroseconds());
-  mSeekTargetThreshold = Some(aTime);
-}
-
-// Copy and return a decoded frame.
-nsresult
-AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
-                             AppleVDADecoder::AppleFrameRef aFrameRef)
-{
-  if (mIsShutDown || mIsFlushing) {
-    // We are in the process of flushing or shutting down; ignore frame.
-    return NS_OK;
-  }
-
-  LOG("mp4 output frame %lld dts %lld pts %lld duration %lld us%s",
-      aFrameRef.byte_offset,
-      aFrameRef.decode_timestamp.ToMicroseconds(),
-      aFrameRef.composition_timestamp.ToMicroseconds(),
-      aFrameRef.duration.ToMicroseconds(),
-      aFrameRef.is_sync_point ? " keyframe" : ""
-  );
-
-  if (mQueuedSamples > mMaxRefFrames) {
-    // We had stopped requesting more input because we had received too much at
-    // the time. We can ask for more once again.
-    mCallback->InputExhausted();
-  }
-  MOZ_ASSERT(mQueuedSamples);
-  mQueuedSamples--;
-
-  if (!aImage) {
-    // Image was dropped by decoder.
-    return NS_OK;
-  }
-
-  bool useNullSample = false;
-  if (mSeekTargetThreshold.isSome()) {
-    if ((aFrameRef.composition_timestamp + aFrameRef.duration) < mSeekTargetThreshold.ref()) {
-      useNullSample = true;
-    } else {
-      mSeekTargetThreshold.reset();
-    }
-  }
-
-  // Where our resulting image will end up.
-  RefPtr<MediaData> data;
-  // Bounds.
-  VideoInfo info;
-  info.mDisplay = nsIntSize(mDisplayWidth, mDisplayHeight);
-  gfx::IntRect visible = gfx::IntRect(0,
-                                      0,
-                                      mPictureWidth,
-                                      mPictureHeight);
-
-  if (useNullSample) {
-    data = new NullData(aFrameRef.byte_offset,
-                        aFrameRef.composition_timestamp.ToMicroseconds(),
-                        aFrameRef.duration.ToMicroseconds());
-  } else if (mUseSoftwareImages) {
-    size_t width = CVPixelBufferGetWidth(aImage);
-    size_t height = CVPixelBufferGetHeight(aImage);
-    DebugOnly<size_t> planes = CVPixelBufferGetPlaneCount(aImage);
-    MOZ_ASSERT(planes == 2, "Likely not NV12 format and it must be.");
-
-    VideoData::YCbCrBuffer buffer;
-
-    // Lock the returned image data.
-    CVReturn rv = CVPixelBufferLockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly);
-    if (rv != kCVReturnSuccess) {
-      NS_ERROR("error locking pixel data");
-      mCallback->Error(MediaDataDecoderError::DECODE_ERROR);
-      return NS_ERROR_FAILURE;
-    }
-    // Y plane.
-    buffer.mPlanes[0].mData =
-      static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(aImage, 0));
-    buffer.mPlanes[0].mStride = CVPixelBufferGetBytesPerRowOfPlane(aImage, 0);
-    buffer.mPlanes[0].mWidth = width;
-    buffer.mPlanes[0].mHeight = height;
-    buffer.mPlanes[0].mOffset = 0;
-    buffer.mPlanes[0].mSkip = 0;
-    // Cb plane.
-    buffer.mPlanes[1].mData =
-      static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(aImage, 1));
-    buffer.mPlanes[1].mStride = CVPixelBufferGetBytesPerRowOfPlane(aImage, 1);
-    buffer.mPlanes[1].mWidth = (width+1) / 2;
-    buffer.mPlanes[1].mHeight = (height+1) / 2;
-    buffer.mPlanes[1].mOffset = 0;
-    buffer.mPlanes[1].mSkip = 1;
-    // Cr plane.
-    buffer.mPlanes[2].mData =
-      static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(aImage, 1));
-    buffer.mPlanes[2].mStride = CVPixelBufferGetBytesPerRowOfPlane(aImage, 1);
-    buffer.mPlanes[2].mWidth = (width+1) / 2;
-    buffer.mPlanes[2].mHeight = (height+1) / 2;
-    buffer.mPlanes[2].mOffset = 1;
-    buffer.mPlanes[2].mSkip = 1;
-
-    // Copy the image data into our own format.
-    data =
-      VideoData::Create(info,
-                        mImageContainer,
-                        nullptr,
-                        aFrameRef.byte_offset,
-                        aFrameRef.composition_timestamp.ToMicroseconds(),
-                        aFrameRef.duration.ToMicroseconds(),
-                        buffer,
-                        aFrameRef.is_sync_point,
-                        aFrameRef.decode_timestamp.ToMicroseconds(),
-                        visible);
-    // Unlock the returned image data.
-    CVPixelBufferUnlockBaseAddress(aImage, kCVPixelBufferLock_ReadOnly);
-  } else {
-#ifndef MOZ_WIDGET_UIKIT
-    IOSurfacePtr surface = MacIOSurfaceLib::CVPixelBufferGetIOSurface(aImage);
-    MOZ_ASSERT(surface, "Decoder didn't return an IOSurface backed buffer");
-
-    RefPtr<MacIOSurface> macSurface = new MacIOSurface(surface);
-
-    RefPtr<layers::Image> image = new MacIOSurfaceImage(macSurface);
-
-    data =
-      VideoData::CreateFromImage(info,
-                                 mImageContainer,
-                                 aFrameRef.byte_offset,
-                                 aFrameRef.composition_timestamp.ToMicroseconds(),
-                                 aFrameRef.duration.ToMicroseconds(),
-                                 image.forget(),
-                                 aFrameRef.is_sync_point,
-                                 aFrameRef.decode_timestamp.ToMicroseconds(),
-                                 visible);
-#else
-    MOZ_ASSERT_UNREACHABLE("No MacIOSurface on iOS");
-#endif
-  }
-
-  if (!data) {
-    NS_ERROR("Couldn't create VideoData for frame");
-    mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
-    return NS_ERROR_FAILURE;
-  }
-
-  // Frames come out in DTS order but we need to output them
-  // in composition order.
-  MonitorAutoLock mon(mMonitor);
-  mReorderQueue.Push(data);
-  while (mReorderQueue.Length() > mMaxRefFrames) {
-    mCallback->Output(mReorderQueue.Pop().get());
-  }
-  LOG("%llu decoded frames queued",
-      static_cast<unsigned long long>(mReorderQueue.Length()));
-
-  return NS_OK;
-}
-
-nsresult
-AppleVDADecoder::ProcessDecode(MediaRawData* aSample)
-{
-  AssertOnTaskQueueThread();
-
-  mInputIncoming--;
-
-  if (mIsFlushing) {
-    return NS_OK;
-  }
-
-  auto rv = DoDecode(aSample);
-  // Ask for more data.
-  if (NS_SUCCEEDED(rv) && !mInputIncoming && mQueuedSamples <= mMaxRefFrames) {
-    LOG("%s task queue empty; requesting more data", GetDescriptionName());
-    mCallback->InputExhausted();
-  }
-
-  return rv;
-}
-
-nsresult
-AppleVDADecoder::DoDecode(MediaRawData* aSample)
-{
-  AssertOnTaskQueueThread();
-
-  AutoCFRelease<CFDataRef> block =
-    CFDataCreate(kCFAllocatorDefault, aSample->Data(), aSample->Size());
-  if (!block) {
-    NS_ERROR("Couldn't create CFData");
-    return NS_ERROR_FAILURE;
-  }
-
-  AutoCFRelease<CFNumberRef> pts =
-    CFNumberCreate(kCFAllocatorDefault,
-                   kCFNumberSInt64Type,
-                   &aSample->mTime);
-  AutoCFRelease<CFNumberRef> dts =
-    CFNumberCreate(kCFAllocatorDefault,
-                   kCFNumberSInt64Type,
-                   &aSample->mTimecode);
-  AutoCFRelease<CFNumberRef> duration =
-    CFNumberCreate(kCFAllocatorDefault,
-                   kCFNumberSInt64Type,
-                   &aSample->mDuration);
-  AutoCFRelease<CFNumberRef> byte_offset =
-    CFNumberCreate(kCFAllocatorDefault,
-                   kCFNumberSInt64Type,
-                   &aSample->mOffset);
-  char keyframe = aSample->mKeyframe ? 1 : 0;
-  AutoCFRelease<CFNumberRef> cfkeyframe =
-    CFNumberCreate(kCFAllocatorDefault,
-                   kCFNumberSInt8Type,
-                   &keyframe);
-
-  const void* keys[] = { CFSTR("FRAME_PTS"),
-                         CFSTR("FRAME_DTS"),
-                         CFSTR("FRAME_DURATION"),
-                         CFSTR("FRAME_OFFSET"),
-                         CFSTR("FRAME_KEYFRAME") };
-  const void* values[] = { pts,
-                           dts,
-                           duration,
-                           byte_offset,
-                           cfkeyframe };
-  static_assert(ArrayLength(keys) == ArrayLength(values),
-                "Non matching keys/values array size");
-
-  AutoCFRelease<CFDictionaryRef> frameInfo =
-    CFDictionaryCreate(kCFAllocatorDefault,
-                       keys,
-                       values,
-                       ArrayLength(keys),
-                       &kCFTypeDictionaryKeyCallBacks,
-                       &kCFTypeDictionaryValueCallBacks);
-
-  mQueuedSamples++;
-
-  OSStatus rv = VDADecoderDecode(mDecoder,
-                                 0,
-                                 block,
-                                 frameInfo);
-
-  if (rv != noErr) {
-    NS_WARNING("AppleVDADecoder: Couldn't pass frame to decoder");
-    mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
-    return NS_ERROR_FAILURE;
-  }
-
-  return NS_OK;
-}
-
-nsresult