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 (current diff)
parent 305797 6f5fc76550ed13a9cdde280276dc618f6aae5276 (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/accessible/base/nsCoreUtils.cpp
+++ b/accessible/base/nsCoreUtils.cpp
@@ -149,18 +149,23 @@ nsCoreUtils::DispatchMouseEvent(EventMes
   aPresShell->HandleEventWithTarget(&event, aFrame, aContent, &status);
 }
 
 void
 nsCoreUtils::DispatchTouchEvent(EventMessage aMessage, int32_t aX, int32_t aY,
                                 nsIContent* aContent, nsIFrame* aFrame,
                                 nsIPresShell* aPresShell, nsIWidget* aRootWidget)
 {
-  if (!dom::TouchEvent::PrefEnabled())
+  nsIDocShell* docShell = nullptr;
+  if (aPresShell->GetDocument()) {
+    docShell = aPresShell->GetDocument()->GetDocShell();
+  }
+  if (!dom::TouchEvent::PrefEnabled(docShell)) {
     return;
+  }
 
   WidgetTouchEvent event(true, aMessage, aRootWidget);
 
   event.mTime = PR_IntervalNow();
 
   // XXX: Touch has an identifier of -1 to hint that it is synthesized.
   RefPtr<dom::Touch> t = new dom::Touch(-1, LayoutDeviceIntPoint(aX, aY),
                                         LayoutDeviceIntPoint(1, 1), 0.0f, 1.0f);
--- a/accessible/generic/OuterDocAccessible.cpp
+++ b/accessible/generic/OuterDocAccessible.cpp
@@ -110,18 +110,19 @@ OuterDocAccessible::Shutdown()
   }
 
   AccessibleWrap::Shutdown();
 }
 
 bool
 OuterDocAccessible::InsertChildAt(uint32_t aIdx, Accessible* aAccessible)
 {
-  NS_ASSERTION(aAccessible->IsDoc(),
-               "OuterDocAccessible should only have document child!");
+  MOZ_RELEASE_ASSERT(aAccessible->IsDoc(),
+                     "OuterDocAccessible can have a document child only!");
+
   // We keep showing the old document for a bit after creating the new one,
   // and while building the new DOM and frame tree. That's done on purpose
   // to avoid weird flashes of default background color.
   // The old viewer will be destroyed after the new one is created.
   // For a11y, it should be safe to shut down the old document now.
   if (mChildren.Length())
     mChildren[0]->Shutdown();
 
@@ -159,16 +160,24 @@ OuterDocAccessible::RemoveChild(Accessib
   bool wasRemoved = AccessibleWrap::RemoveChild(child);
 
   NS_ASSERTION(!mChildren.Length(),
                "This child document of outerdoc accessible wasn't removed!");
 
   return wasRemoved;
 }
 
+bool
+OuterDocAccessible::IsAcceptableChild(nsIContent* aEl) const
+{
+  // outer document accessible doesn't not participate in ordinal tree
+  // mutations.
+  return false;
+}
+
 ProxyAccessible*
 OuterDocAccessible::RemoteChildDoc() const
 {
   dom::TabParent* tab = dom::TabParent::GetFrom(GetContent());
   if (!tab)
     return nullptr;
 
   return tab->GetTopLevelDocAccessible();
--- a/accessible/generic/OuterDocAccessible.h
+++ b/accessible/generic/OuterDocAccessible.h
@@ -33,16 +33,17 @@ public:
   // Accessible
   virtual void Shutdown() override;
   virtual mozilla::a11y::role NativeRole() override;
   virtual Accessible* ChildAtPoint(int32_t aX, int32_t aY,
                                    EWhichChildAtPoint aWhichChild) override;
 
   virtual bool InsertChildAt(uint32_t aIdx, Accessible* aChild) override;
   virtual bool RemoveChild(Accessible* aAccessible) override;
+  virtual bool IsAcceptableChild(nsIContent* aEl) const override;
 
 protected:
   virtual ~OuterDocAccessible() override;
 };
 
 inline OuterDocAccessible*
 Accessible::AsOuterDoc()
 {
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -41,17 +41,18 @@ class ProxyAccessible
 {
 public:
 
   ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
                   DocAccessibleParent* aDoc, role aRole, uint32_t aInterfaces) :
      mParent(aParent), mDoc(aDoc), mWrapper(0), mID(aID), mRole(aRole),
      mOuterDoc(false), mIsDoc(false),
      mHasValue(aInterfaces & Interfaces::VALUE),
-     mIsHyperLink(aInterfaces & Interfaces::HYPERLINK)
+     mIsHyperLink(aInterfaces & Interfaces::HYPERLINK),
+     mIsHyperText(aInterfaces & Interfaces::HYPERTEXT)
   {
     MOZ_COUNT_CTOR(ProxyAccessible);
   }
   ~ProxyAccessible()
   {
     MOZ_COUNT_DTOR(ProxyAccessible);
     MOZ_ASSERT(!mWrapper);
   }
@@ -417,36 +418,37 @@ public:
    */
   bool IsDoc() const { return mIsDoc; }
   DocAccessibleParent* AsDoc() const { return IsDoc() ? mDoc : nullptr; }
 
 protected:
   explicit ProxyAccessible(DocAccessibleParent* aThisAsDoc) :
     mParent(nullptr), mDoc(aThisAsDoc), mWrapper(0), mID(0),
     mRole(roles::DOCUMENT), mOuterDoc(false), mIsDoc(true), mHasValue(false),
-    mIsHyperLink(false)
+    mIsHyperLink(false), mIsHyperText(false)
   { MOZ_COUNT_CTOR(ProxyAccessible); }
 
 protected:
   ProxyAccessible* mParent;
 
 private:
   nsTArray<ProxyAccessible*> mChildren;
   DocAccessibleParent* mDoc;
   uintptr_t mWrapper;
   uint64_t mID;
 protected:
   // XXX DocAccessibleParent gets to change this to change the role of
   // documents.
-  role mRole : 28;
+  role mRole : 27;
 private:
   bool mOuterDoc : 1;
 
 public:
   const bool mIsDoc: 1;
   const bool mHasValue: 1;
   const bool mIsHyperLink: 1;
+  const bool mIsHyperText: 1;
 };
 
 }
 }
 
 #endif
--- a/accessible/tests/browser/browser.ini
+++ b/accessible/tests/browser/browser.ini
@@ -16,17 +16,16 @@ support-files =
 # Caching tests
 [browser_caching_attributes.js]
 [browser_caching_description.js]
 [browser_caching_name.js]
 skip-if = e10s
 [browser_caching_relations.js]
 [browser_caching_states.js]
 [browser_caching_value.js]
-skip-if = e10s # Bug 1276721: QueryInterface is not working with proxies.
 
 # Events tests
 [browser_events_caretmove.js]
 [browser_events_hide.js]
 [browser_events_show.js]
 [browser_events_statechange.js]
 [browser_events_textchange.js]
 
--- a/accessible/xpcom/xpcAccessibleDocument.cpp
+++ b/accessible/xpcom/xpcAccessibleDocument.cpp
@@ -217,16 +217,24 @@ xpcAccessibleDocument::GetXPCAccessible(
   if (aProxy->mHasValue) {
     interfaces |= eValue;
   }
   
   if (aProxy->mIsHyperLink) {
     interfaces |= eHyperLink;
   }
 
+  if (aProxy->mIsHyperText) {
+    interfaces |= eText;
+    acc = new xpcAccessibleHyperText(aProxy, interfaces);
+    mCache.Put(aProxy, acc);
+
+    return acc;
+  }
+
   acc = new xpcAccessibleGeneric(aProxy, interfaces);
   mCache.Put(aProxy, acc);
 
   return acc;
 }
 
 void
 xpcAccessibleDocument::Shutdown()
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -27,16 +27,17 @@ builtin(include, build/autoconf/zlib.m4)
 builtin(include, build/autoconf/linux.m4)dnl
 builtin(include, build/autoconf/winsdk.m4)dnl
 builtin(include, build/autoconf/icu.m4)dnl
 builtin(include, build/autoconf/ffi.m4)dnl
 builtin(include, build/autoconf/clang-plugin.m4)dnl
 builtin(include, build/autoconf/alloc.m4)dnl
 builtin(include, build/autoconf/ios.m4)dnl
 builtin(include, build/autoconf/jemalloc.m4)dnl
+builtin(include, build/autoconf/sanitize.m4)dnl
 
 MOZ_PROG_CHECKMSYS()
 
 # Read the user's .mozconfig script.  We can't do this in
 # configure.in: autoconf puts the argument parsing code above anything
 # expanded from configure.in, and we need to get the configure options
 # from .mozconfig in place before that argument parsing code.
 MOZ_READ_MOZCONFIG(.)
--- a/addon-sdk/source/lib/sdk/event/utils.js
+++ b/addon-sdk/source/lib/sdk/event/utils.js
@@ -239,19 +239,26 @@ Input.prototype[end] = Input.end;
 // while also allowing the objects to be reclaimed.  It means, however,
 // input.value cannot be accessed long after the event was dispatched.
 const WeakValueGetterSetter = {
   get: function() {
     return this._weakValue ? this._weakValue.get() : this._simpleValue
   },
   set: function(v) {
     if (v && typeof v === "object") {
-      this._weakValue = Cu.getWeakReference(v)
-      this._simpleValue = undefined;
-      return;
+      try {
+        // Try to set a weak reference.  This can throw for some values.
+        // For example, if the value is a native object that does not
+        // implement nsISupportsWeakReference.
+        this._weakValue = Cu.getWeakReference(v)
+        this._simpleValue = undefined;
+        return;
+      } catch (e) {
+        // Do nothing.  Fall through to setting _simpleValue below.
+      }
     }
     this._simpleValue = v;
     this._weakValue = undefined;
   },
 }
 Object.defineProperty(Input.prototype, "value", WeakValueGetterSetter);
 
 exports.Input = Input;
--- a/b2g/chrome/content/content.css
+++ b/b2g/chrome/content/content.css
@@ -292,17 +292,17 @@ input[type="checkbox"][disabled]:hover:a
   border:1px solid rgba(125,125,125,0.4) !important;
 }
 
 select[disabled] > button {
   opacity: 0.6;
   padding: 1px 7px 1px 7px;
 }
 
-*:-moz-any-link:active,
+*:any-link:active,
 *[role=button]:active,
 button:active,
 option:active,
 select:active,
 label:active {
   background-color: rgba(141, 184, 216, 0.5);
 }
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1366,18 +1366,20 @@ pref("privacy.trackingprotection.ui.enab
 pref("privacy.trackingprotection.ui.enabled", false);
 #endif
 pref("privacy.trackingprotection.introCount", 0);
 pref("privacy.trackingprotection.introURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/tracking-protection/start/");
 
 // Enable Contextual Identity Containers
 #ifdef NIGHTLY_BUILD
 pref("privacy.userContext.enabled", true);
+pref("privacy.userContext.ui.enabled", true);
 #else
 pref("privacy.userContext.enabled", false);
+pref("privacy.userContext.ui.enabled", false);
 #endif
 
 #ifndef RELEASE_BUILD
 // At the moment, autostart.2 is used, while autostart.1 is unused.
 // We leave it here set to false to reset users' defaults and allow
 // us to change everybody to true in the future, when desired.
 pref("browser.tabs.remote.autostart.1", false);
 pref("browser.tabs.remote.autostart.2", true);
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -49,16 +49,27 @@ var gPrivacyPane = {
    * Initialize autocomplete to ensure prefs are in sync.
    */
   _initAutocomplete: function () {
     Components.classes["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
               .getService(Components.interfaces.mozIPlacesAutoComplete);
   },
 
   /**
+   * Show the Containers UI depending on the privacy.userContext.ui.enabled pref.
+   */
+  _initBrowserContainers: function () {
+    if (!Services.prefs.getBoolPref("privacy.userContext.ui.enabled")) {
+      return;
+    }
+
+    document.getElementById("browserContainersbox").hidden = false;
+  },
+
+  /**
    * Sets up the UI for the number of days of history to keep, and updates the
    * label of the "Clear Now..." button.
    */
   init: function ()
   {
     function setEventListener(aId, aEventType, aCallback)
     {
       document.getElementById(aId)
@@ -68,16 +79,17 @@ var gPrivacyPane = {
     this._updateSanitizeSettingsButton();
     this.initializeHistoryMode();
     this.updateHistoryModePane();
     this.updatePrivacyMicroControls();
     this.initAutoStartPrivateBrowsingReverter();
     this._initTrackingProtection();
     this._initTrackingProtectionPBM();
     this._initAutocomplete();
+    this._initBrowserContainers();
 
     setEventListener("privacy.sanitize.sanitizeOnShutdown", "change",
                      gPrivacyPane._updateSanitizeSettingsButton);
     setEventListener("browser.privatebrowsing.autostart", "change",
                      gPrivacyPane.updatePrivacyMicroControls);
     setEventListener("historyMode", "command", function () {
       gPrivacyPane.updateHistoryModePane();
       gPrivacyPane.updateHistoryModePrefs();
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -69,16 +69,21 @@
   <preference id="privacy.sanitize.timeSpan"
               name="privacy.sanitize.timeSpan"
               type="int"/>
   <!-- Private Browsing -->
   <preference id="browser.privatebrowsing.autostart"
               name="browser.privatebrowsing.autostart"
               type="bool"/>
 
+  <!-- Containers -->
+  <preference id="privacy.userContext.enabled"
+              name="privacy.userContext.enabled"
+              type="bool"/>
+
 </preferences>
 
 <hbox id="header-privacy"
       class="header"
       hidden="true"
       data-category="panePrivacy">
   <label class="header-name" flex="1">&panePrivacy.title;</label>
   <button class="help-button"
@@ -278,8 +283,19 @@
             preference="browser.urlbar.suggest.bookmark"/>
   <checkbox id="openpageSuggestion" label="&locbar.openpage.label;"
             accesskey="&locbar.openpage.accesskey;"
             preference="browser.urlbar.suggest.openpage"/>
   <label class="text-link" onclick="if (event.button == 0) gotoPref('search')">
     &suggestionSettings.label;
   </label>
 </groupbox>
+
+<!-- Containers -->
+<groupbox id="browserContainersGroup" data-category="panePrivacy" hidden="true">
+  <vbox id="browserContainersbox" hidden="true">
+    <caption><label>&browserContainersHeader.label;</label></caption>
+    <checkbox id="browserContainersCheckbox"
+              label="&browserContainersEnabled.label;"
+              accesskey="&browserContainersEnabled.accesskey;"
+              preference="privacy.userContext.enabled"/>
+  </vbox>
+</groupbox>
copy from browser/config/tooltool-manifests/linux64/asan.manifest
copy to browser/config/tooltool-manifests/linux64/msan.manifest
--- a/browser/locales/en-US/chrome/browser/preferences/privacy.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/privacy.dtd
@@ -99,8 +99,12 @@
 <!ENTITY  rememberSearchForm.label       "Remember search and form history">
 <!ENTITY  rememberSearchForm.accesskey   "f">
 
 <!ENTITY  clearOnClose.label             "Clear history when &brandShortName; closes">
 <!ENTITY  clearOnClose.accesskey         "r">
 
 <!ENTITY  clearOnCloseSettings.label     "Settings…">
 <!ENTITY  clearOnCloseSettings.accesskey "t">
+
+<!ENTITY  browserContainersHeader.label         "Container Tabs">
+<!ENTITY  browserContainersEnabled.label        "Enable Container Tabs">
+<!ENTITY  browserContainersEnabled.accesskey    "a">
--- a/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -11,21 +11,20 @@ ICU_LIB_NAMES=
 MOZ_SYSTEM_ICU=
 MOZ_ARG_WITH_BOOL(system-icu,
 [  --with-system-icu
                           Use system ICU (located with pkgconfig)],
     MOZ_SYSTEM_ICU=1)
 
 if test -n "$MOZ_SYSTEM_ICU"; then
     PKG_CHECK_MODULES(MOZ_ICU, icu-i18n >= 50.1)
-else
-    MOZ_ICU_INCLUDES="/intl/icu/source/common /intl/icu/source/i18n"
+    CFLAGS="$CFLAGS $MOZ_ICU_CFLAGS"
+    CXXFLAGS="$CXXFLAGS $MOZ_ICU_CFLAGS"
 fi
 
-AC_SUBST_LIST(MOZ_ICU_INCLUDES)
 AC_SUBST(MOZ_SYSTEM_ICU)
 
 MOZ_ARG_WITH_STRING(intl-api,
 [  --with-intl-api, --with-intl-api=build, --without-intl-api
     Determine the status of the ECMAScript Internationalization API.  The first
     (or lack of any of these) builds and exposes the API.  The second builds it
     but doesn't use ICU or expose the API to script.  The third doesn't build
     ICU at all.],
new file mode 100644
--- /dev/null
+++ b/build/autoconf/sanitize.m4
@@ -0,0 +1,85 @@
+dnl This Source Code Form is subject to the terms of the Mozilla Public
+dnl License, v. 2.0. If a copy of the MPL was not distributed with this
+dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+AC_DEFUN([MOZ_CONFIG_SANITIZE], [
+
+dnl ========================================================
+dnl = Use Address Sanitizer
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(address-sanitizer,
+[  --enable-address-sanitizer       Enable Address Sanitizer (default=no)],
+    MOZ_ASAN=1,
+    MOZ_ASAN= )
+if test -n "$MOZ_ASAN"; then
+    MOZ_LLVM_HACKS=1
+    if test -n "$CLANG_CL"; then
+        # Look for clang_rt.asan_dynamic-i386.dll
+        MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-i386.dll
+        # We use MOZ_PATH_PROG in order to get a Windows style path.
+        MOZ_PATH_PROG(MOZ_CLANG_RT_ASAN_LIB_PATH, $MOZ_CLANG_RT_ASAN_LIB)
+        if test -z "$MOZ_CLANG_RT_ASAN_LIB_PATH"; then
+            AC_MSG_ERROR([Couldn't find $MOZ_CLANG_RT_ASAN_LIB.  It should be available in the same location as clang-cl.])
+        fi
+        AC_SUBST(MOZ_CLANG_RT_ASAN_LIB_PATH)
+    fi
+    CFLAGS="-fsanitize=address -Dxmalloc=myxmalloc -fPIC $CFLAGS"
+    CXXFLAGS="-fsanitize=address -Dxmalloc=myxmalloc -fPIC $CXXFLAGS"
+    LDFLAGS="-fsanitize=address $LDFLAGS"
+    AC_DEFINE(MOZ_ASAN)
+    MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
+fi
+AC_SUBST(MOZ_ASAN)
+
+dnl ========================================================
+dnl = Use Memory Sanitizer
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(memory-sanitizer,
+[  --enable-memory-sanitizer       Enable Memory Sanitizer (default=no)],
+    MOZ_MSAN=1,
+    MOZ_MSAN= )
+if test -n "$MOZ_MSAN"; then
+    MOZ_LLVM_HACKS=1
+    CFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $CFLAGS"
+    CXXFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $CXXFLAGS"
+    LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $LDFLAGS"
+    AC_DEFINE(MOZ_MSAN)
+    MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
+fi
+AC_SUBST(MOZ_MSAN)
+
+dnl ========================================================
+dnl = Use Thread Sanitizer
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(thread-sanitizer,
+[  --enable-thread-sanitizer       Enable Thread Sanitizer (default=no)],
+   MOZ_TSAN=1,
+   MOZ_TSAN= )
+if test -n "$MOZ_TSAN"; then
+    MOZ_LLVM_HACKS=1
+    CFLAGS="-fsanitize=thread $CFLAGS"
+    CXXFLAGS="-fsanitize=thread $CXXFLAGS"
+    LDFLAGS="-fsanitize=thread $LDFLAGS"
+    AC_DEFINE(MOZ_TSAN)
+    MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
+fi
+AC_SUBST(MOZ_TSAN)
+
+# The LLVM symbolizer is used by all sanitizers
+AC_SUBST(LLVM_SYMBOLIZER)
+
+dnl ========================================================
+dnl = Enable hacks required for LLVM instrumentations
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(llvm-hacks,
+[  --enable-llvm-hacks       Enable workarounds required for several LLVM instrumentations (default=no)],
+    MOZ_LLVM_HACKS=1,
+    MOZ_LLVM_HACKS= )
+if test -n "$MOZ_LLVM_HACKS"; then
+    MOZ_NO_WLZDEFS=1
+    MOZ_CFLAGS_NSS=1
+fi
+AC_SUBST(MOZ_NO_WLZDEFS)
+AC_SUBST(MOZ_CFLAGS_NSS)
+
+])
--- a/build/unix/mozconfig.asan
+++ b/build/unix/mozconfig.asan
@@ -2,22 +2,16 @@ MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/build/mozconfig.common"
 
 # Use Clang as specified in manifest
 export CC="$topsrcdir/clang/bin/clang -fgnu89-inline"
 export CXX="$topsrcdir/clang/bin/clang++"
 export LLVM_SYMBOLIZER="$topsrcdir/clang/bin/llvm-symbolizer"
 
-# Mandatory flags for ASan
-export ASANFLAGS="-fsanitize=address -Dxmalloc=myxmalloc -fPIC"
-export CFLAGS="$ASANFLAGS"
-export CXXFLAGS="$ASANFLAGS"
-export LDFLAGS="-fsanitize=address"
-
 # Use a newer binutils, from the tooltool gcc package, if it's there
 if [ -e "$topsrcdir/gcc/bin/ld" ]; then
     export CC="$CC -B $topsrcdir/gcc/bin"
     export CXX="$CXX -B $topsrcdir/gcc/bin"
 fi
 
 # Enable ASan specific code and build workarounds
 ac_add_options --enable-address-sanitizer
--- a/build/unix/mozconfig.tsan
+++ b/build/unix/mozconfig.tsan
@@ -2,31 +2,26 @@ MOZ_AUTOMATION_L10N_CHECK=0
 
 . "$topsrcdir/build/mozconfig.common"
 
 # Use Clang as specified in manifest
 export CC="$topsrcdir/clang/bin/clang"
 export CXX="$topsrcdir/clang/bin/clang++"
 export LLVM_SYMBOLIZER="$topsrcdir/clang/bin/llvm-symbolizer"
 
-# Mandatory flag for TSan
-export CFLAGS="-fsanitize=thread"
-export CXXFLAGS="-fsanitize=thread"
-export LDFLAGS="-fsanitize=thread"
-
 # Use a newer binutils, from the tooltool gcc package, if it's there
 if [ -e "$topsrcdir/gcc/bin/ld" ]; then
     export CC="$CC -B $topsrcdir/gcc/bin"
     export CXX="$CXX -B $topsrcdir/gcc/bin"
 fi
 
 # Enable TSan specific code and build workarounds
 ac_add_options --enable-thread-sanitizer
 
-# The ThreadSanitizer is not compatible with sanboxing
+# The ThreadSanitizer is not compatible with sandboxing
 # (see bug 1182565)
 ac_add_options --disable-sandbox
 
 # These are required by TSan
 ac_add_options --disable-jemalloc
 ac_add_options --disable-crashreporter
 ac_add_options --disable-elf-hack
 ac_add_options --enable-pie
--- a/config/external/icu/common/moz.build
+++ b/config/external/icu/common/moz.build
@@ -12,12 +12,12 @@ if CONFIG['OS_TARGET'] == 'Android' and 
         'gabi++'
     ]
 
 DEFINES['U_COMMON_IMPLEMENTATION'] = True
 # This normally gets defined in the SDK but our WINVER is too low.
 #FIXME: should probably stop including mozilla-config.h
 DEFINES['LOCALE_SNAME'] = 0x5c
 
-LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
+LOCAL_INCLUDES += ['/intl/icu/source/i18n']
 
 include('../defs.mozbuild')
 include('sources.mozbuild')
--- a/config/external/icu/common/sources.mozbuild
+++ b/config/external/icu/common/sources.mozbuild
@@ -174,8 +174,97 @@ SOURCES += [
    '/intl/icu/source/common/utrie2_builder.cpp',
    '/intl/icu/source/common/uts46.cpp',
    '/intl/icu/source/common/utypes.c',
    '/intl/icu/source/common/uvector.cpp',
    '/intl/icu/source/common/uvectr32.cpp',
    '/intl/icu/source/common/uvectr64.cpp',
    '/intl/icu/source/common/wintz.c',
 ]
+
+EXPORTS.unicode += [
+   '/intl/icu/source/common/unicode/appendable.h',
+   '/intl/icu/source/common/unicode/brkiter.h',
+   '/intl/icu/source/common/unicode/bytestream.h',
+   '/intl/icu/source/common/unicode/bytestrie.h',
+   '/intl/icu/source/common/unicode/bytestriebuilder.h',
+   '/intl/icu/source/common/unicode/caniter.h',
+   '/intl/icu/source/common/unicode/chariter.h',
+   '/intl/icu/source/common/unicode/dbbi.h',
+   '/intl/icu/source/common/unicode/docmain.h',
+   '/intl/icu/source/common/unicode/dtintrv.h',
+   '/intl/icu/source/common/unicode/enumset.h',
+   '/intl/icu/source/common/unicode/errorcode.h',
+   '/intl/icu/source/common/unicode/filteredbrk.h',
+   '/intl/icu/source/common/unicode/icudataver.h',
+   '/intl/icu/source/common/unicode/icuplug.h',
+   '/intl/icu/source/common/unicode/idna.h',
+   '/intl/icu/source/common/unicode/listformatter.h',
+   '/intl/icu/source/common/unicode/localpointer.h',
+   '/intl/icu/source/common/unicode/locid.h',
+   '/intl/icu/source/common/unicode/messagepattern.h',
+   '/intl/icu/source/common/unicode/normalizer2.h',
+   '/intl/icu/source/common/unicode/normlzr.h',
+   '/intl/icu/source/common/unicode/parseerr.h',
+   '/intl/icu/source/common/unicode/parsepos.h',
+   '/intl/icu/source/common/unicode/platform.h',
+   '/intl/icu/source/common/unicode/ptypes.h',
+   '/intl/icu/source/common/unicode/putil.h',
+   '/intl/icu/source/common/unicode/rbbi.h',
+   '/intl/icu/source/common/unicode/rep.h',
+   '/intl/icu/source/common/unicode/resbund.h',
+   '/intl/icu/source/common/unicode/schriter.h',
+   '/intl/icu/source/common/unicode/std_string.h',
+   '/intl/icu/source/common/unicode/strenum.h',
+   '/intl/icu/source/common/unicode/stringpiece.h',
+   '/intl/icu/source/common/unicode/stringtriebuilder.h',
+   '/intl/icu/source/common/unicode/symtable.h',
+   '/intl/icu/source/common/unicode/ubidi.h',
+   '/intl/icu/source/common/unicode/ubrk.h',
+   '/intl/icu/source/common/unicode/ucasemap.h',
+   '/intl/icu/source/common/unicode/ucat.h',
+   '/intl/icu/source/common/unicode/uchar.h',
+   '/intl/icu/source/common/unicode/ucharstrie.h',
+   '/intl/icu/source/common/unicode/ucharstriebuilder.h',
+   '/intl/icu/source/common/unicode/uchriter.h',
+   '/intl/icu/source/common/unicode/uclean.h',
+   '/intl/icu/source/common/unicode/ucnv.h',
+   '/intl/icu/source/common/unicode/ucnv_cb.h',
+   '/intl/icu/source/common/unicode/ucnv_err.h',
+   '/intl/icu/source/common/unicode/ucnvsel.h',
+   '/intl/icu/source/common/unicode/uconfig.h',
+   '/intl/icu/source/common/unicode/udata.h',
+   '/intl/icu/source/common/unicode/uenum.h',
+   '/intl/icu/source/common/unicode/uidna.h',
+   '/intl/icu/source/common/unicode/uiter.h',
+   '/intl/icu/source/common/unicode/ulistformatter.h',
+   '/intl/icu/source/common/unicode/uloc.h',
+   '/intl/icu/source/common/unicode/umachine.h',
+   '/intl/icu/source/common/unicode/umisc.h',
+   '/intl/icu/source/common/unicode/unifilt.h',
+   '/intl/icu/source/common/unicode/unifunct.h',
+   '/intl/icu/source/common/unicode/unimatch.h',
+   '/intl/icu/source/common/unicode/uniset.h',
+   '/intl/icu/source/common/unicode/unistr.h',
+   '/intl/icu/source/common/unicode/unorm.h',
+   '/intl/icu/source/common/unicode/unorm2.h',
+   '/intl/icu/source/common/unicode/uobject.h',
+   '/intl/icu/source/common/unicode/urename.h',
+   '/intl/icu/source/common/unicode/urep.h',
+   '/intl/icu/source/common/unicode/ures.h',
+   '/intl/icu/source/common/unicode/uscript.h',
+   '/intl/icu/source/common/unicode/uset.h',
+   '/intl/icu/source/common/unicode/usetiter.h',
+   '/intl/icu/source/common/unicode/ushape.h',
+   '/intl/icu/source/common/unicode/usprep.h',
+   '/intl/icu/source/common/unicode/ustring.h',
+   '/intl/icu/source/common/unicode/ustringtrie.h',
+   '/intl/icu/source/common/unicode/utext.h',
+   '/intl/icu/source/common/unicode/utf.h',
+   '/intl/icu/source/common/unicode/utf16.h',
+   '/intl/icu/source/common/unicode/utf32.h',
+   '/intl/icu/source/common/unicode/utf8.h',
+   '/intl/icu/source/common/unicode/utf_old.h',
+   '/intl/icu/source/common/unicode/utrace.h',
+   '/intl/icu/source/common/unicode/utypes.h',
+   '/intl/icu/source/common/unicode/uvernum.h',
+   '/intl/icu/source/common/unicode/uversion.h',
+]
--- a/config/external/icu/i18n/moz.build
+++ b/config/external/icu/i18n/moz.build
@@ -4,12 +4,12 @@
 # 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/.
 
 Library('icui18n')
 FINAL_LIBRARY = 'icu'
 
 DEFINES['U_I18N_IMPLEMENTATION'] = True
 
-LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
+LOCAL_INCLUDES += ['/intl/icu/source/common']
 
 include('../defs.mozbuild')
 include('sources.mozbuild')
--- a/config/external/icu/i18n/sources.mozbuild
+++ b/config/external/icu/i18n/sources.mozbuild
@@ -194,8 +194,92 @@ SOURCES += [
    '/intl/icu/source/i18n/vzone.cpp',
    '/intl/icu/source/i18n/windtfmt.cpp',
    '/intl/icu/source/i18n/winnmfmt.cpp',
    '/intl/icu/source/i18n/wintzimpl.cpp',
    '/intl/icu/source/i18n/zonemeta.cpp',
    '/intl/icu/source/i18n/zrule.cpp',
    '/intl/icu/source/i18n/ztrans.cpp',
 ]
+
+EXPORTS.unicode += [
+   '/intl/icu/source/i18n/unicode/alphaindex.h',
+   '/intl/icu/source/i18n/unicode/basictz.h',
+   '/intl/icu/source/i18n/unicode/calendar.h',
+   '/intl/icu/source/i18n/unicode/choicfmt.h',
+   '/intl/icu/source/i18n/unicode/coleitr.h',
+   '/intl/icu/source/i18n/unicode/coll.h',
+   '/intl/icu/source/i18n/unicode/compactdecimalformat.h',
+   '/intl/icu/source/i18n/unicode/curramt.h',
+   '/intl/icu/source/i18n/unicode/currpinf.h',
+   '/intl/icu/source/i18n/unicode/currunit.h',
+   '/intl/icu/source/i18n/unicode/datefmt.h',
+   '/intl/icu/source/i18n/unicode/dcfmtsym.h',
+   '/intl/icu/source/i18n/unicode/decimfmt.h',
+   '/intl/icu/source/i18n/unicode/dtfmtsym.h',
+   '/intl/icu/source/i18n/unicode/dtitvfmt.h',
+   '/intl/icu/source/i18n/unicode/dtitvinf.h',
+   '/intl/icu/source/i18n/unicode/dtptngen.h',
+   '/intl/icu/source/i18n/unicode/dtrule.h',
+   '/intl/icu/source/i18n/unicode/fieldpos.h',
+   '/intl/icu/source/i18n/unicode/fmtable.h',
+   '/intl/icu/source/i18n/unicode/format.h',
+   '/intl/icu/source/i18n/unicode/fpositer.h',
+   '/intl/icu/source/i18n/unicode/gender.h',
+   '/intl/icu/source/i18n/unicode/gregocal.h',
+   '/intl/icu/source/i18n/unicode/locdspnm.h',
+   '/intl/icu/source/i18n/unicode/measfmt.h',
+   '/intl/icu/source/i18n/unicode/measunit.h',
+   '/intl/icu/source/i18n/unicode/measure.h',
+   '/intl/icu/source/i18n/unicode/msgfmt.h',
+   '/intl/icu/source/i18n/unicode/numfmt.h',
+   '/intl/icu/source/i18n/unicode/numsys.h',
+   '/intl/icu/source/i18n/unicode/plurfmt.h',
+   '/intl/icu/source/i18n/unicode/plurrule.h',
+   '/intl/icu/source/i18n/unicode/rbnf.h',
+   '/intl/icu/source/i18n/unicode/rbtz.h',
+   '/intl/icu/source/i18n/unicode/regex.h',
+   '/intl/icu/source/i18n/unicode/region.h',
+   '/intl/icu/source/i18n/unicode/reldatefmt.h',
+   '/intl/icu/source/i18n/unicode/scientificnumberformatter.h',
+   '/intl/icu/source/i18n/unicode/search.h',
+   '/intl/icu/source/i18n/unicode/selfmt.h',
+   '/intl/icu/source/i18n/unicode/simpletz.h',
+   '/intl/icu/source/i18n/unicode/smpdtfmt.h',
+   '/intl/icu/source/i18n/unicode/sortkey.h',
+   '/intl/icu/source/i18n/unicode/stsearch.h',
+   '/intl/icu/source/i18n/unicode/tblcoll.h',
+   '/intl/icu/source/i18n/unicode/timezone.h',
+   '/intl/icu/source/i18n/unicode/tmunit.h',
+   '/intl/icu/source/i18n/unicode/tmutamt.h',
+   '/intl/icu/source/i18n/unicode/tmutfmt.h',
+   '/intl/icu/source/i18n/unicode/translit.h',
+   '/intl/icu/source/i18n/unicode/tzfmt.h',
+   '/intl/icu/source/i18n/unicode/tznames.h',
+   '/intl/icu/source/i18n/unicode/tzrule.h',
+   '/intl/icu/source/i18n/unicode/tztrans.h',
+   '/intl/icu/source/i18n/unicode/ucal.h',
+   '/intl/icu/source/i18n/unicode/ucol.h',
+   '/intl/icu/source/i18n/unicode/ucoleitr.h',
+   '/intl/icu/source/i18n/unicode/ucsdet.h',
+   '/intl/icu/source/i18n/unicode/ucurr.h',
+   '/intl/icu/source/i18n/unicode/udat.h',
+   '/intl/icu/source/i18n/unicode/udateintervalformat.h',
+   '/intl/icu/source/i18n/unicode/udatpg.h',
+   '/intl/icu/source/i18n/unicode/udisplaycontext.h',
+   '/intl/icu/source/i18n/unicode/ufieldpositer.h',
+   '/intl/icu/source/i18n/unicode/uformattable.h',
+   '/intl/icu/source/i18n/unicode/ugender.h',
+   '/intl/icu/source/i18n/unicode/uldnames.h',
+   '/intl/icu/source/i18n/unicode/ulocdata.h',
+   '/intl/icu/source/i18n/unicode/umsg.h',
+   '/intl/icu/source/i18n/unicode/unirepl.h',
+   '/intl/icu/source/i18n/unicode/unum.h',
+   '/intl/icu/source/i18n/unicode/unumsys.h',
+   '/intl/icu/source/i18n/unicode/upluralrules.h',
+   '/intl/icu/source/i18n/unicode/uregex.h',
+   '/intl/icu/source/i18n/unicode/uregion.h',
+   '/intl/icu/source/i18n/unicode/usearch.h',
+   '/intl/icu/source/i18n/unicode/uspoof.h',
+   '/intl/icu/source/i18n/unicode/utmscale.h',
+   '/intl/icu/source/i18n/unicode/utrans.h',
+   '/intl/icu/source/i18n/unicode/vtzone.h',
+]
--- a/config/external/icu/stubdata/moz.build
+++ b/config/external/icu/stubdata/moz.build
@@ -5,11 +5,9 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # This builds the ICU stubdata library, since we are shipping ICU
 # data in a separate data file. ICU needs a data symbol to link against
 # even if you're loading its data from a file.
 
 Library('icustubdata')
 
-LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
-
 SOURCES += ['/intl/icu/source/stubdata/stubdata.c']
--- a/config/nsinstall.py
+++ b/config/nsinstall.py
@@ -6,16 +6,17 @@
 # It's intended to be used when there's no natively compile nsinstall
 # available, and doesn't intend to be fully equivalent.
 # Its major use is for l10n repackaging on systems that don't have
 # a full build environment set up.
 # The basic limitation is, it doesn't even try to link and ignores
 # all related options.
 from __future__ import print_function
 from optparse import OptionParser
+import mozfile
 import os
 import os.path
 import sys
 import shutil
 import stat
 
 def _nsinstall_internal(argv):
   usage = "usage: %prog [options] arg1 [arg2 ...] target-directory"
@@ -121,20 +122,20 @@ def _nsinstall_internal(argv):
           os.mkdir(targetpath)
         entries = [os.path.join(srcpath, e) for e in os.listdir(srcpath)]
         copy_all_entries(entries, targetpath)
         # options.t is not relevant for directories
         if options.m:
           os.chmod(targetpath, options.m)
       else:
         if os.path.exists(targetpath):
-          # On Windows, read-only files can't be deleted
           if sys.platform == "win32":
-            os.chmod(targetpath, stat.S_IWUSR)
-          os.remove(targetpath)
+            mozfile.remove(targetpath)
+          else:
+            os.remove(targetpath)
         if options.t:
           shutil.copy2(srcpath, targetpath)
         else:
           shutil.copy(srcpath, targetpath)
 
   # the last argument is the target directory
   target = args.pop()
   # ensure target directory (importantly, we do not apply a mode to the directory
--- a/config/system-headers
+++ b/config/system-headers
@@ -552,16 +552,20 @@ intshcut.h
 inttypes.h
 iodef.h
 io.h
 IOKit/IOKitLib.h
 IOKit/IOMessage.h
 IOKit/pwr_mgt/IOPMLib.h
 iomanip
 iostream.h
+#if MOZ_JACK==1
+jack/jack.h
+jack/statistics.h
+#endif
 JavaControl.h
 JavaEmbedding/JavaControl.h
 JavaVM/jni.h
 JManager.h
 JNIEnvTests.h
 jni.h
 #if MOZ_SYSTEM_JPEG==1
 jpeglib.h
--- a/devtools/client/inspector/rules/test/browser_rules_user-agent-styles.js
+++ b/devtools/client/inspector/rules/test/browser_rules_user-agent-styles.js
@@ -111,18 +111,18 @@ function* userAgentStylesVisible(inspect
   }
 
   ok(userRules.some(rule => rule.matchedSelectors.length === 1),
     "There is an inline style for element in user styles");
 
   // These tests rely on the "a" selector being the last test in
   // TEST_DATA.
   ok(uaRules.some(rule => {
-    return rule.matchedSelectors.indexOf(":-moz-any-link") !== -1;
-  }), "There is a rule for :-moz-any-link");
+    return rule.matchedSelectors.indexOf(":any-link") !== -1;
+  }), "There is a rule for :any-link");
   ok(uaRules.some(rule => {
     return rule.matchedSelectors.indexOf("*|*:link") !== -1;
   }), "There is a rule for *|*:link");
   ok(uaRules.some(rule => {
     return rule.matchedSelectors.length === 1;
   }), "Inline styles for ua styles");
 }
 
--- a/devtools/client/responsive.html/components/device-modal.js
+++ b/devtools/client/responsive.html/components/device-modal.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+/* eslint-env browser */
+
 "use strict";
 
 const { DOM: dom, createClass, PropTypes, addons } =
   require("devtools/client/shared/vendor/react");
 const { getStr } = require("../utils/l10n");
 const Types = require("../types");
 
 module.exports = createClass({
@@ -20,30 +22,38 @@ module.exports = createClass({
   },
 
   mixins: [ addons.PureRenderMixin ],
 
   getInitialState() {
     return {};
   },
 
+  componentDidMount() {
+    window.addEventListener("keydown", this.onKeyDown, true);
+  },
+
   componentWillReceiveProps(nextProps) {
     let {
       devices,
     } = nextProps;
 
     for (let type of devices.types) {
       for (let device of devices[type]) {
         this.setState({
           [device.name]: device.displayed,
         });
       }
     }
   },
 
+  componentWillUnmount() {
+    window.removeEventListener("keydown", this.onKeyDown, true);
+  },
+
   onDeviceCheckboxClick({ target }) {
     this.setState({
       [target.value]: !this.state[target.value]
     });
   },
 
   onDeviceModalSubmit() {
     let {
@@ -73,80 +83,99 @@ module.exports = createClass({
         }
       }
     }
 
     onDeviceListUpdate(preferredDevices);
     onUpdateDeviceModalOpen(false);
   },
 
+  onKeyDown(event) {
+    if (!this.props.devices.isModalOpen) {
+      return;
+    }
+    // Escape keycode
+    if (event.keyCode === 27) {
+      let {
+        onUpdateDeviceModalOpen
+      } = this.props;
+      onUpdateDeviceModalOpen(false);
+    }
+  },
+
   render() {
     let {
       devices,
       onUpdateDeviceModalOpen,
     } = this.props;
 
-    let modalClass = "device-modal container";
-
-    if (!devices.isModalOpen) {
-      modalClass += " hidden";
-    }
-
     const sortedDevices = {};
     for (let type of devices.types) {
       sortedDevices[type] = Object.assign([], devices[type])
         .sort((a, b) => a.name.localeCompare(b.name));
     }
 
     return dom.div(
       {
-        className: modalClass,
+        id: "device-modal-wrapper",
+        className: this.props.devices.isModalOpen ? "opened" : "closed",
       },
-      dom.button({
-        id: "device-close-button",
-        className: "toolbar-button devtools-button",
-        onClick: () => onUpdateDeviceModalOpen(false),
-      }),
       dom.div(
         {
-          className: "device-modal-content",
+          className: "device-modal container",
         },
-        devices.types.map(type => {
-          return dom.div(
-            {
-              className: "device-type",
-              key: type,
-            },
-            dom.header(
+        dom.button({
+          id: "device-close-button",
+          className: "toolbar-button devtools-button",
+          onClick: () => onUpdateDeviceModalOpen(false),
+        }),
+        dom.div(
+          {
+            className: "device-modal-content",
+          },
+          devices.types.map(type => {
+            return dom.div(
               {
-                className: "device-header",
+                className: "device-type",
+                key: type,
               },
-              type
-            ),
-            sortedDevices[type].map(device => {
-              return dom.label(
+              dom.header(
                 {
-                  className: "device-label",
-                  key: device.name,
+                  className: "device-header",
                 },
-                dom.input({
-                  className: "device-input-checkbox",
-                  type: "checkbox",
-                  value: device.name,
-                  checked: this.state[device.name],
-                  onChange: this.onDeviceCheckboxClick,
-                }),
-                device.name
-              );
-            })
-          );
-        })
+                type
+              ),
+              sortedDevices[type].map(device => {
+                return dom.label(
+                  {
+                    className: "device-label",
+                    key: device.name,
+                  },
+                  dom.input({
+                    className: "device-input-checkbox",
+                    type: "checkbox",
+                    value: device.name,
+                    checked: this.state[device.name],
+                    onChange: this.onDeviceCheckboxClick,
+                  }),
+                  device.name
+                );
+              })
+            );
+          })
+        ),
+        dom.button(
+          {
+            id: "device-submit-button",
+            onClick: this.onDeviceModalSubmit,
+          },
+          getStr("responsive.done")
+        )
       ),
-      dom.button(
+      dom.div(
         {
-          id: "device-submit-button",
-          onClick: this.onDeviceModalSubmit,
-        },
-        getStr("responsive.done")
+          className: "modal-overlay",
+          onClick: () => onUpdateDeviceModalOpen(false),
+        }
       )
     );
   },
 });
--- a/devtools/client/responsive.html/index.css
+++ b/devtools/client/responsive.html/index.css
@@ -34,30 +34,29 @@
 }
 
 * {
   box-sizing: border-box;
 }
 
 #root,
 html, body {
+  height: 100%;
   margin: 0;
 }
 
 #app {
   /* Center the viewports container */
   display: flex;
   align-items: center;
   flex-direction: column;
-  height: 100vh;
-
-  /* Snap to the top of the app when there isn't enough vertical space anymore
-     to center the viewports (so we don't lose the global toolbar) */
-  position: sticky;
-  top: 0;
+  padding-top: 15px;
+  padding-bottom: 1%;
+  position: relative;
+  height: 100%;
 }
 
 /**
  * Common style for containers and toolbar buttons
  */
 
 .container {
   background-color: var(--theme-toolbar-background);
@@ -81,17 +80,17 @@ html, body {
 /**
  * Global Toolbar
  */
 
 #global-toolbar {
   color: var(--theme-body-color-alt);
   border-radius: 2px;
   box-shadow: var(--rdm-box-shadow);
-  margin: 10% 0 30px 0;
+  margin: 0 0 15px 0;
   padding: 4px 5px;
   display: inline-flex;
   -moz-user-select: none;
 }
 
 #global-toolbar > .title {
   border-right: 1px solid var(--theme-splitter-color);
   padding: 1px 6px 0 2px;
@@ -331,31 +330,76 @@ html, body {
 .viewport-dimension-separator {
   -moz-user-select: none;
 }
 
 /**
  * Device Modal
  */
 
+@keyframes fade-in-and-up {
+  0% {
+    opacity: 0;
+    transform: translateY(5px);
+  }
+  100% {
+    opacity: 1;
+    transform: translateY(0px);
+  }
+}
+
+@keyframes fade-down-and-out {
+  0% {
+    opacity: 1;
+    transform: translateY(0px);
+  }
+  100% {
+    opacity: 0;
+    transform: translateY(5px);
+    visibility: hidden;
+  }
+}
+
 .device-modal {
   border-radius: 2px;
   box-shadow: var(--rdm-box-shadow);
+  display: none;
   position: absolute;
   margin: auto;
   top: 0;
   bottom: 0;
   left: 0;
   right: 0;
   width: 642px;
   height: 612px;
+  z-index: 1;
+}
+
+/* Handles the opening/closing of the modal */
+#device-modal-wrapper.opened .device-modal {
+  animation: fade-in-and-up 0.3s ease;
+  animation-fill-mode: forwards;
+  display: block;
 }
 
-.device-modal.hidden {
-  display: none;
+#device-modal-wrapper.closed .device-modal {
+  animation: fade-down-and-out 0.3s ease;
+  animation-fill-mode: forwards;
+  display: block;
+}
+
+#device-modal-wrapper.opened .modal-overlay {
+  background-color: var(--theme-splitter-color);
+  position: absolute;
+  top: 0;
+  left: 0;
+  height: 100%;
+  width: 100%;
+  z-index: 0;
+  opacity: 0.5;
 }
 
 .device-modal-content {
   display: flex;
   flex-direction: column;
   flex-wrap: wrap;
   overflow: auto;
   height: 550px;
--- a/devtools/client/responsive.html/test/browser/browser_device_modal_exit.js
+++ b/devtools/client/responsive.html/test/browser/browser_device_modal_exit.js
@@ -5,17 +5,17 @@ http://creativecommons.org/publicdomain/
 
 // Test submitting display device changes on the device modal
 
 const TEST_URL = "data:text/html;charset=utf-8,";
 const Types = require("devtools/client/responsive.html/types");
 
 addRDMTask(TEST_URL, function* ({ ui }) {
   let { store, document } = ui.toolWindow;
-  let modal = document.querySelector(".device-modal");
+  let modal = document.querySelector("#device-modal-wrapper");
   let closeButton = document.querySelector("#device-close-button");
 
   // Wait until the viewport has been added and the device list has been loaded
   yield waitUntilState(store, state => state.viewports.length == 1
     && state.devices.listState == Types.deviceListState.LOADED);
 
   openDeviceModal(ui);
 
@@ -23,18 +23,18 @@ addRDMTask(TEST_URL, function* ({ ui }) 
 
   info("Check the first unchecked device and exit the modal.");
   let uncheckedCb = [...document.querySelectorAll(".device-input-checkbox")]
     .filter(cb => !cb.checked)[0];
   let value = uncheckedCb.value;
   uncheckedCb.click();
   closeButton.click();
 
-  ok(modal.classList.contains("hidden"),
-    "The device modal is hidden on exit.");
+  ok(modal.classList.contains("closed") && !modal.classList.contains("opened"),
+    "The device modal is closed on exit.");
 
   info("Check that the device list remains unchanged after exitting.");
   let preferredDevicesAfter = _loadPreferredDevices();
 
   is(preferredDevicesBefore.added.size, preferredDevicesAfter.added.size,
     "Got expected number of added devices.");
 
   is(preferredDevicesBefore.removed.size, preferredDevicesAfter.removed.size,
--- a/devtools/client/responsive.html/test/browser/browser_device_modal_submit.js
+++ b/devtools/client/responsive.html/test/browser/browser_device_modal_submit.js
@@ -18,17 +18,17 @@ const addedDevice = {
   "featured": true,
 };
 
 const TEST_URL = "data:text/html;charset=utf-8,";
 const Types = require("devtools/client/responsive.html/types");
 
 addRDMTask(TEST_URL, function* ({ ui }) {
   let { store, document } = ui.toolWindow;
-  let modal = document.querySelector(".device-modal");
+  let modal = document.querySelector("#device-modal-wrapper");
   let select = document.querySelector(".viewport-device-selector");
   let submitButton = document.querySelector("#device-submit-button");
 
   // Wait until the viewport has been added and the device list has been loaded
   yield waitUntilState(store, state => state.viewports.length == 1
     && state.devices.listState == Types.deviceListState.LOADED);
 
   openDeviceModal(ui);
@@ -56,18 +56,18 @@ addRDMTask(TEST_URL, function* ({ ui }) 
   // Tests where the user adds a non-featured device
   info("Check the first unchecked device and submit new device list.");
   let uncheckedCb = [...document.querySelectorAll(".device-input-checkbox")]
     .filter(cb => !cb.checked)[0];
   let value = uncheckedCb.value;
   uncheckedCb.click();
   submitButton.click();
 
-  ok(modal.classList.contains("hidden"),
-    "The device modal is hidden on submit.");
+  ok(modal.classList.contains("closed") && !modal.classList.contains("opened"),
+    "The device modal is closed on submit.");
 
   info("Checking that the new device is added to the user preference list.");
   let preferredDevices = _loadPreferredDevices();
   ok(preferredDevices.added.has(value), value + " in user added list.");
 
   info("Checking new device is added to the device selector.");
   let options = [...select.options];
   is(options.length - 2, featuredCount + 1,
--- a/devtools/client/responsive.html/test/browser/head.js
+++ b/devtools/client/responsive.html/test/browser/head.js
@@ -130,32 +130,32 @@ var setViewportSize = Task.async(functio
     ui.setViewportSize(width, height);
     yield resized;
   }
 });
 
 function openDeviceModal(ui) {
   let { document } = ui.toolWindow;
   let select = document.querySelector(".viewport-device-selector");
-  let modal = document.querySelector(".device-modal");
+  let modal = document.querySelector("#device-modal-wrapper");
   let editDeviceOption = [...select.options].filter(o => {
     return o.value === OPEN_DEVICE_MODAL_VALUE;
   })[0];
 
   info("Checking initial device modal state");
-  ok(modal.classList.contains("hidden"),
-    "The device modal is hidden by default.");
+  ok(modal.classList.contains("closed") && !modal.classList.contains("opened"),
+    "The device modal is closed by default.");
 
   info("Opening device modal through device selector.");
   EventUtils.synthesizeMouseAtCenter(select, {type: "mousedown"},
     ui.toolWindow);
   EventUtils.synthesizeMouseAtCenter(editDeviceOption, {type: "mouseup"},
     ui.toolWindow);
 
-  ok(!modal.classList.contains("hidden"),
+  ok(modal.classList.contains("opened") && !modal.classList.contains("closed"),
     "The device modal is displayed.");
 }
 
 function getSessionHistory(browser) {
   return ContentTask.spawn(browser, {}, function* () {
     /* eslint-disable no-undef */
     let { interfaces: Ci } = Components;
     let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -810,16 +810,17 @@ nsDocShell::nsDocShell()
 #ifdef DEBUG
   , mInEnsureScriptEnv(false)
 #endif
   , mDefaultLoadFlags(nsIRequest::LOAD_NORMAL)
   , mFrameType(FRAME_TYPE_REGULAR)
   , mPrivateBrowsingId(0)
   , mParentCharsetSource(0)
   , mJSRunToCompletionDepth(0)
+  , mTouchEventsOverride(nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE)
 {
   AssertOriginAttributesMatchPrivateBrowsing();
   mHistoryID = ++gDocshellIDCounter;
   if (gDocShellCount++ == 0) {
     NS_ASSERTION(sURIFixup == nullptr,
                  "Huh, sURIFixup not null in first nsDocShell ctor!");
 
     CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
@@ -3175,16 +3176,46 @@ nsDocShell::SetCustomUserAgent(const nsA
     nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
     if (childShell) {
       childShell->SetCustomUserAgent(aCustomUserAgent);
     }
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDocShell::GetTouchEventsOverride(uint32_t* aTouchEventsOverride)
+{
+  NS_ENSURE_ARG_POINTER(aTouchEventsOverride);
+
+  *aTouchEventsOverride = mTouchEventsOverride;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetTouchEventsOverride(uint32_t aTouchEventsOverride)
+{
+  if (!(aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE ||
+        aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_ENABLED ||
+        aTouchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_DISABLED)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  mTouchEventsOverride = aTouchEventsOverride;
+
+  uint32_t childCount = mChildList.Length();
+  for (uint32_t i = 0; i < childCount; ++i) {
+    nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
+    if (childShell) {
+      childShell->SetTouchEventsOverride(aTouchEventsOverride);
+    }
+  }
+  return NS_OK;
+}
+
 /* virtual */ int32_t
 nsDocShell::ItemType()
 {
   return mItemType;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetItemType(int32_t* aItemType)
@@ -3347,16 +3378,20 @@ nsDocShell::SetDocLoaderParent(nsDocLoad
     }
     SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
     value = parentAsDocShell->GetAffectPrivateSessionLifetime();
     SetAffectPrivateSessionLifetime(value);
     uint32_t flags;
     if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags))) {
       SetDefaultLoadFlags(flags);
     }
+    uint32_t touchEventsOverride;
+    if (NS_SUCCEEDED(parentAsDocShell->GetTouchEventsOverride(&touchEventsOverride))) {
+      SetTouchEventsOverride(touchEventsOverride);
+    }
   }
 
   nsCOMPtr<nsILoadContext> parentAsLoadContext(do_QueryInterface(parent));
   if (parentAsLoadContext &&
       NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value))) {
     SetPrivateBrowsing(value);
   }
 
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -1033,16 +1033,20 @@ private:
   nsCString mOriginalUriString;
   nsWeakPtr mOpener;
   mozilla::DocShellOriginAttributes mOriginAttributes;
 
   // A depth count of how many times NotifyRunToCompletionStart
   // has been called without a matching NotifyRunToCompletionStop.
   uint32_t mJSRunToCompletionDepth;
 
+  // Whether or not touch events are overridden. Possible values are defined
+  // as constants in the nsIDocShell.idl file.
+  uint32_t mTouchEventsOverride;
+
   // Separate function to do the actual name (i.e. not _top, _self etc.)
   // searching for FindItemWithName.
   nsresult DoFindItemWithName(const char16_t* aName,
                               nsISupports* aRequestor,
                               nsIDocShellTreeItem* aOriginalRequestor,
                               nsIDocShellTreeItem** aResult);
 
   // Helper assertion to enforce that mInPrivateBrowsing is in sync with
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -1109,9 +1109,27 @@ interface nsIDocShell : nsIDocShellTreeI
 
   /**
    * The tab child for this docshell.
    */
   [binaryname(ScriptableTabChild)] readonly attribute nsITabChild tabChild;
   [noscript,notxpcom,nostdcall] TabChildRef GetTabChild();
 
   [noscript,nostdcall,notxpcom] nsICommandManager GetCommandManager();
+
+  /**
+   * This allows chrome to override the default choice of whether touch events
+   * are available on a specific docshell. Possible values are listed below.
+   */
+  attribute unsigned long touchEventsOverride;
+  /**
+   * Override platform/pref default behaviour and force-disable touch events.
+   */
+  const unsigned long TOUCHEVENTS_OVERRIDE_DISABLED = 0;
+  /**
+   * Override platform/pref default behaviour and force-enable touch events.
+   */
+  const unsigned long TOUCHEVENTS_OVERRIDE_ENABLED = 1;
+  /**
+   * Don't override the platform/pref default behaviour for touch events.
+   */
+  const unsigned long TOUCHEVENTS_OVERRIDE_NONE = 2;
 };
--- a/docshell/test/browser/browser.ini
+++ b/docshell/test/browser/browser.ini
@@ -79,15 +79,16 @@ skip-if = buildapp == 'mulet'
 [browser_uriFixupAlternateRedirects.js]
 support-files =
   redirect_to_example.sjs
 [browser_loadDisallowInherit.js]
 [browser_loadURI.js]
 [browser_multiple_pushState.js]
 [browser_onbeforeunload_navigation.js]
 [browser_search_notification.js]
-[browser_ua_emulation.js]
+[browser_tab_touch_events.js]
 [browser_timelineMarkers-01.js]
 [browser_timelineMarkers-02.js]
 skip-if = true # Bug 1220415
 [browser_timelineMarkers-03.js]
 [browser_timelineMarkers-04.js]
 [browser_timelineMarkers-05.js]
+[browser_ua_emulation.js]
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/browser_tab_touch_events.js
@@ -0,0 +1,64 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(function*() {
+  yield openUrl("data:text/html;charset=utf-8,<iframe id='test-iframe'></iframe>");
+
+  let docshell = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                        .getInterface(Ci.nsIWebNavigation)
+                        .QueryInterface(Ci.nsIDocShell);
+
+  is(docshell.touchEventsOverride, Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_NONE,
+    "touchEventsOverride flag should be initially set to NONE");
+
+  docshell.touchEventsOverride = Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_DISABLED;
+  is(docshell.touchEventsOverride, Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_DISABLED,
+    "touchEventsOverride flag should be changed to DISABLED");
+
+  let frameWin = content.document.querySelector("#test-iframe").contentWindow;
+  docshell = frameWin.QueryInterface(Ci.nsIInterfaceRequestor)
+                     .getInterface(Ci.nsIWebNavigation)
+                     .QueryInterface(Ci.nsIDocShell);
+  is(docshell.touchEventsOverride, Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_DISABLED,
+    "touchEventsOverride flag should be passed on to frames.");
+
+  let newFrame = content.document.createElement("iframe");
+  content.document.body.appendChild(newFrame);
+
+  let newFrameWin = newFrame.contentWindow;
+  docshell = newFrameWin.QueryInterface(Ci.nsIInterfaceRequestor)
+                        .getInterface(Ci.nsIWebNavigation)
+                        .QueryInterface(Ci.nsIDocShell);
+  is(docshell.touchEventsOverride, Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_DISABLED,
+    "Newly created frames should use the new touchEventsOverride flag");
+
+  newFrameWin.location.reload();
+  yield waitForEvent(newFrameWin, "load");
+
+  is(docshell.touchEventsOverride, Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_DISABLED,
+    "New touchEventsOverride flag should persist across reloads");
+
+  gBrowser.removeCurrentTab();
+});
+
+function waitForEvent(target, event) {
+  return new Promise(function(resolve) {
+    target.addEventListener(event, resolve);
+  });
+}
+
+function openUrl(url) {
+  return new Promise(function(resolve, reject) {
+    window.focus();
+
+    let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
+    let linkedBrowser = tab.linkedBrowser;
+
+    linkedBrowser.addEventListener("load", function onload() {
+      linkedBrowser.removeEventListener("load", onload, true);
+      resolve(tab);
+    }, true);
+  });
+}
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8342,16 +8342,35 @@ nsContentUtils::SetFetchReferrerURIWithP
   net::ReferrerPolicy referrerPolicy = aReferrerPolicy;
   if (referrerPolicy == net::RP_Default) {
     referrerPolicy = aDoc->GetReferrerPolicy();
   }
   return aChannel->SetReferrerWithPolicy(referrerURI, referrerPolicy);
 }
 
 // static
+net::ReferrerPolicy
+nsContentUtils::GetReferrerPolicyFromHeader(const nsAString& aHeader)
+{
+  // Multiple headers could be concatenated into one comma-separated
+  // list of policies. Need to tokenize the multiple headers.
+  nsCharSeparatedTokenizer tokenizer(aHeader, ',');
+  nsAutoString token;
+  net::ReferrerPolicy referrerPolicy = mozilla::net::RP_Unset;
+  while (tokenizer.hasMoreTokens()) {
+    token = tokenizer.nextToken();
+    net::ReferrerPolicy policy = net::ReferrerPolicyFromString(token);
+    if (policy != net::RP_Unset) {
+      referrerPolicy = policy;
+    }
+  }
+  return referrerPolicy;
+}
+
+// static
 bool
 nsContentUtils::PushEnabled(JSContext* aCx, JSObject* aObj)
 {
   if (NS_IsMainThread()) {
     return Preferences::GetBool("dom.push.enabled", false);
   }
 
   using namespace workers;
@@ -9207,8 +9226,48 @@ nsContentUtils::GetPresentationURL(nsIDo
   nsCOMPtr<nsIDOMElement> topFrameElement;
   loadContext->GetTopFrameElement(getter_AddRefs(topFrameElement));
   if (!topFrameElement) {
     return;
   }
 
   topFrameElement->GetAttribute(NS_LITERAL_STRING("mozpresentation"), aPresentationUrl);
 }
+
+/* static */ nsIDocShell*
+nsContentUtils::GetDocShellForEventTarget(EventTarget* aTarget)
+{
+  nsCOMPtr<nsINode> node(do_QueryInterface(aTarget));
+  nsIDocument* doc = nullptr;
+  nsIDocShell* docShell = nullptr;
+
+  if (node) {
+    doc = node->OwnerDoc();
+    if (!doc->GetDocShell()) {
+      bool ignore;
+      nsCOMPtr<nsPIDOMWindowInner> window =
+        do_QueryInterface(doc->GetScriptHandlingObject(ignore));
+      if (window) {
+        doc = window->GetExtantDoc();
+      }
+    }
+  } else {
+    nsCOMPtr<nsPIDOMWindowInner> window(do_QueryInterface(aTarget));
+    if (window) {
+      doc = window->GetExtantDoc();
+    }
+  }
+
+  if (!doc) {
+    nsCOMPtr<DOMEventTargetHelper> helper(do_QueryInterface(aTarget));
+    if (helper) {
+      if (nsPIDOMWindowInner* window = helper->GetOwner()) {
+        doc = window->GetExtantDoc();
+      }
+    }
+  }
+
+  if (doc) {
+    docShell = doc->GetDocShell();
+  }
+
+  return docShell;
+}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2573,16 +2573,25 @@ public:
    *
    * https://w3c.github.io/webappsec/specs/referrer-policy/#determine-requests-referrer
    */
   static nsresult SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
                                                 nsIDocument* aDoc,
                                                 nsIHttpChannel* aChannel,
                                                 mozilla::net::ReferrerPolicy aReferrerPolicy);
 
+    /*
+   * Parse a referrer policy from a Referrer-Policy header
+   * https://www.w3.org/TR/referrer-policy/#parse-referrer-policy-from-header
+   *
+   * @param aHeader the response's Referrer-Policy header to parse
+   * @return referrer policy from the response header.
+   */
+  static mozilla::net::ReferrerPolicy GetReferrerPolicyFromHeader(const nsAString& aHeader);
+
   static bool PushEnabled(JSContext* aCx, JSObject* aObj);
 
   static bool IsNonSubresourceRequest(nsIChannel* aChannel);
 
   static uint32_t CookiesBehavior()
   {
     return sCookiesBehavior;
   }
@@ -2637,16 +2646,21 @@ public:
   static void SetScrollbarsVisibility(nsIDocShell* aDocShell, bool aVisible);
 
   /*
    * Return the associated presentation URL of the presented content.
    * Will return empty string if the docshell is not in a presented content.
    */
   static void GetPresentationURL(nsIDocShell* aDocShell, nsAString& aPresentationUrl);
 
+  /*
+   * Try to find the docshell corresponding to the given event target.
+   */
+  static nsIDocShell* GetDocShellForEventTarget(mozilla::dom::EventTarget* aTarget);
+
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3780,16 +3780,25 @@ nsDocument::SetHeaderData(nsIAtom* aHead
     // referrer policy to policy
     if (policy != mozilla::net::RP_Unset) {
       // Referrer policy spec (section 6.1) says that we always use the newest
       // referrer policy we find
       mReferrerPolicy = policy;
       mReferrerPolicySet = true;
     }
   }
+
+  if (aHeaderField == nsGkAtoms::headerReferrerPolicy && !aData.IsEmpty()) {
+     ReferrerPolicy policy = nsContentUtils::GetReferrerPolicyFromHeader(aData);
+    if (policy != mozilla::net::RP_Unset) {
+      mReferrerPolicy = policy;
+      mReferrerPolicySet = true;
+    }
+  }
+
 }
 void
 nsDocument::TryChannelCharset(nsIChannel *aChannel,
                               int32_t& aCharsetSource,
                               nsACString& aCharset,
                               nsHtml5TreeOpExecutor* aExecutor)
 {
   if (aChannel) {
@@ -8721,16 +8730,17 @@ nsDocument::RetrieveRelevantHeaders(nsIC
     static const char *const headers[] = {
       "default-style",
       "content-style-type",
       "content-language",
       "content-disposition",
       "refresh",
       "x-dns-prefetch-control",
       "x-frame-options",
+      "referrer-policy",
       // add more http headers if you need
       // XXXbz don't add content-location support without reading bug
       // 238654 and its dependencies/dups first.
       0
     };
 
     nsAutoCString headerVal;
     const char *const *name = headers;
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1730,17 +1730,18 @@ nsMessageManagerScriptExecutor::LoadScri
   }
 
   JS::Rooted<JSObject*> global(rt, mGlobal->GetJSObject());
   if (global) {
     AutoEntryScript aes(global, "message manager script load");
     JSContext* cx = aes.cx();
     if (script) {
       if (aRunInGlobalScope) {
-        JS::CloneAndExecuteScript(cx, script);
+        JS::RootedValue rval(cx);
+        JS::CloneAndExecuteScript(cx, script, &rval);
       } else {
         JS::Rooted<JSObject*> scope(cx);
         bool ok = js::ExecuteInGlobalAndReturnScope(cx, global, script, &scope);
         if (ok) {
           // Force the scope to stay alive.
           mAnonymousGlobalScopes.AppendElement(scope);
         }
       }
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -594,16 +594,17 @@ GK_ATOM(menugroup, "menugroup")
 GK_ATOM(menuitem, "menuitem")
 GK_ATOM(menulist, "menulist")
 GK_ATOM(menupopup, "menupopup")
 GK_ATOM(menuseparator, "menuseparator")
 GK_ATOM(message, "message")
 GK_ATOM(meta, "meta")
 GK_ATOM(referrer, "referrer")
 GK_ATOM(referrerpolicy, "referrerpolicy")
+GK_ATOM(headerReferrerPolicy, "referrer-policy")
 GK_ATOM(meter, "meter")
 GK_ATOM(method, "method")
 GK_ATOM(middle, "middle")
 GK_ATOM(min, "min")
 GK_ATOM(minheight, "minheight")
 GK_ATOM(minimum_scale, "minimum-scale")
 GK_ATOM(minpos, "minpos")
 GK_ATOM(minusSign, "minus-sign")
--- a/dom/base/nsIScriptElement.h
+++ b/dom/base/nsIScriptElement.h
@@ -46,29 +46,30 @@ public:
                      // behave like script-created scripts.
       mCreatorParser(nullptr)
   {
   }
 
   /**
    * Content type identifying the scripting language. Can be empty, in
    * which case javascript will be assumed.
+   * Return false if type attribute is not found.
    */
-  virtual void GetScriptType(nsAString& type) = 0;
-    
+  virtual bool GetScriptType(nsAString& type) = 0;
+
   /**
    * Location of script source text. Can return null, in which case
    * this is assumed to be an inline script element.
    */
   nsIURI* GetScriptURI()
   {
     NS_PRECONDITION(mFrozen, "Not ready for this call yet!");
     return mUri;
   }
-  
+
   /**
    * Script source text for inline script elements.
    */
   virtual void GetScriptText(nsAString& text) = 0;
 
   virtual void GetScriptCharset(nsAString& charset) = 0;
 
   /**
@@ -88,17 +89,17 @@ public:
   }
 
   /**
    * Is the script async. Currently only supported by HTML scripts.
    */
   bool GetScriptAsync()
   {
     NS_PRECONDITION(mFrozen, "Not ready for this call yet!");
-    return mAsync;  
+    return mAsync;
   }
 
   /**
    * Is the script an external script?
    */
   bool GetScriptExternal()
   {
     NS_PRECONDITION(mFrozen, "Not ready for this call yet!");
@@ -193,17 +194,17 @@ public:
    */
   void EndEvaluating()
   {
     nsCOMPtr<nsIParser> parser = do_QueryReferent(mCreatorParser);
     if (parser) {
       parser->EndEvaluatingParserInsertedScript();
     }
   }
-  
+
   /**
    * Retrieves a pointer to the creator parser if this has one or null if not
    */
   already_AddRefed<nsIParser> GetCreatorParser()
   {
     nsCOMPtr<nsIParser> parser = do_QueryReferent(mCreatorParser);
     return parser.forget();
   }
@@ -259,69 +260,69 @@ protected:
    *         loaded
    */
   virtual bool MaybeProcessScript() = 0;
 
   /**
    * The start line number of the script.
    */
   uint32_t mLineNumber;
-  
+
   /**
    * The "already started" flag per HTML5.
    */
   bool mAlreadyStarted;
-  
+
   /**
    * The script didn't have an end tag.
    */
   bool mMalformed;
-  
+
   /**
    * False if parser-inserted but the parser hasn't triggered running yet.
    */
   bool mDoneAddingChildren;
 
   /**
    * If true, the .async property returns true instead of reflecting the
    * content attribute.
    */
   bool mForceAsync;
 
   /**
    * Whether src, defer and async are frozen.
    */
   bool mFrozen;
-  
+
   /**
    * The effective deferredness.
    */
   bool mDefer;
-  
+
   /**
    * The effective asyncness.
    */
   bool mAsync;
-  
+
   /**
    * The effective externalness. A script can be external with mUri being null
    * if the src attribute contained an invalid URL string.
    */
   bool mExternal;
 
   /**
    * Whether this element was parser-created.
    */
   mozilla::dom::FromParser mParserCreated;
 
   /**
    * The effective src (or null if no src).
    */
   nsCOMPtr<nsIURI> mUri;
-  
+
   /**
    * The creator parser of a non-defer, non-async parser-inserted script.
    */
   nsWeakPtr mCreatorParser;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptElement, NS_ISCRIPTELEMENT_IID)
 
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -190,17 +190,17 @@ nsJSUtils::EvaluateString(JSContext* aCx
       if (!JS_WrapObject(aCx, scopeChain[i])) {
         ok = false;
         break;
       }
     }
 
     if (ok && aOffThreadToken) {
       JS::Rooted<JSScript*>
-        script(aCx, JS::FinishOffThreadScript(aCx, JS_GetRuntime(aCx), *aOffThreadToken));
+        script(aCx, JS::FinishOffThreadScript(aCx, *aOffThreadToken));
       *aOffThreadToken = nullptr; // Mark the token as having been finished.
       if (script) {
         ok = JS_ExecuteScript(aCx, scopeChain, script);
       } else {
         ok = false;
       }
     } else if (ok) {
       ok = JS::Evaluate(aCx, scopeChain, aCompileOptions, aSrcBuf, aRetValue);
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -703,18 +703,17 @@ nsScriptLoader::CreateModuleScript(nsMod
       masterScriptUpdater.emplace(master->ScriptLoader(),
                                   aRequest->mElement);
     }
 
     JSContext* cx = aes.cx();
     JS::Rooted<JSObject*> module(cx);
 
     if (aRequest->mWasCompiledOMT) {
-      module = JS::FinishOffThreadModule(cx, xpc::GetJSRuntime(),
-                                         aRequest->mOffThreadToken);
+      module = JS::FinishOffThreadModule(cx, aRequest->mOffThreadToken);
       aRequest->mOffThreadToken = nullptr;
       rv = module ? NS_OK : NS_ERROR_FAILURE;
     } else {
       JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
 
       JS::CompileOptions options(cx);
       FillCompileOptionsForRequest(aes, aRequest, global, &options);
 
@@ -1272,26 +1271,27 @@ nsScriptLoader::ProcessScriptElement(nsI
     return false;
   }
 
   JSVersion version = JSVERSION_DEFAULT;
 
   // Check the type attribute to determine language and version.
   // If type exists, it trumps the deprecated 'language='
   nsAutoString type;
-  aElement->GetScriptType(type);
+  bool hasType = aElement->GetScriptType(type);
+
   nsScriptKind scriptKind = nsScriptKind::Classic;
   if (!type.IsEmpty()) {
     // Support type="module" only for chrome documents.
     if (nsContentUtils::IsChromeDoc(mDocument) && type.LowerCaseEqualsASCII("module")) {
       scriptKind = nsScriptKind::Module;
     } else {
       NS_ENSURE_TRUE(ParseTypeAttribute(type, &version), false);
     }
-  } else {
+  } else if (!hasType) {
     // no 'type=' element
     // "language" is a deprecated attribute of HTML, so we check it only for
     // HTML script elements.
     if (scriptContent->IsHTMLElement()) {
       nsAutoString language;
       scriptContent->GetAttr(kNameSpaceID_None, nsGkAtoms::language, language);
       if (!language.IsEmpty()) {
         if (!nsContentUtils::IsJavaScriptLanguage(language)) {
@@ -1824,17 +1824,18 @@ nsScriptLoader::ProcessRequest(nsScriptL
   }
 
   if (aRequest->mOffThreadToken) {
     // The request was parsed off-main-thread, but the result of the off
     // thread parse was not actually needed to process the request
     // (disappearing window, some other error, ...). Finish the
     // request to avoid leaks in the JS engine.
     MOZ_ASSERT(!aRequest->IsModuleRequest());
-    JS::FinishOffThreadScript(nullptr, xpc::GetJSRuntime(), aRequest->mOffThreadToken);
+    JSContext* cx = JS_GetContext(xpc::GetJSRuntime());
+    JS::CancelOffThreadScript(cx, aRequest->mOffThreadToken);
     aRequest->mOffThreadToken = nullptr;
   }
 
   // Free any source data.
   free(aRequest->mScriptTextBuf);
   aRequest->mScriptTextBuf = nullptr;
   aRequest->mScriptTextLength = 0;
 
--- a/dom/base/test/TestGetURL.cpp
+++ b/dom/base/test/TestGetURL.cpp
@@ -39,17 +39,17 @@ nsresult TestGetURL(const nsCString& aUR
   nsCOMPtr<nsIScriptSecurityManager> secman =
     do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
   TEST_ENSURE_SUCCESS(rv, "Couldn't get script security manager!");
 
   nsCOMPtr<nsIPrincipal> systemPrincipal;
   rv = secman->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
   TEST_ENSURE_SUCCESS(rv, "Couldn't get system principal!");
 
-  rv = xhr->Init(systemPrincipal, nullptr, nullptr, nullptr, nullptr);
+  rv = xhr->Init(systemPrincipal, nullptr, nullptr, nullptr);
   TEST_ENSURE_SUCCESS(rv, "Couldn't initialize the XHR!");
 
   rv = xhr->Open(getString, aURL, false, empty, empty);
   TEST_ENSURE_SUCCESS(rv, "OpenRequest failed!");
 
   rv = xhr->Send(nullptr);
   TEST_ENSURE_SUCCESS(rv, "Send failed!");
 
--- a/dom/base/test/TestNativeXMLHttpRequest.cpp
+++ b/dom/base/test/TestNativeXMLHttpRequest.cpp
@@ -49,17 +49,17 @@ nsresult TestNativeXMLHttpRequest()
   nsCOMPtr<nsIScriptSecurityManager> secman =
     do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
   TEST_ENSURE_SUCCESS(rv, "Couldn't get script security manager!");
 
   nsCOMPtr<nsIPrincipal> systemPrincipal;
   rv = secman->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
   TEST_ENSURE_SUCCESS(rv, "Couldn't get system principal!");
 
-  rv = xhr->Init(systemPrincipal, nullptr, nullptr, nullptr, nullptr);
+  rv = xhr->Init(systemPrincipal, nullptr, nullptr, nullptr);
   TEST_ENSURE_SUCCESS(rv, "Couldn't initialize the XHR!");
 
   rv = xhr->Open(getString, testURL, false, empty, empty);
   TEST_ENSURE_SUCCESS(rv, "Open failed!");
 
   rv = xhr->Send(nullptr);
   TEST_ENSURE_SUCCESS(rv, "Send failed!");
 
--- a/dom/bindings/AtomList.h
+++ b/dom/bindings/AtomList.h
@@ -11,19 +11,17 @@
 #include "mozilla/dom/GeneratedAtomList.h"
 
 namespace mozilla {
 namespace dom {
 
 template<class T>
 T* GetAtomCache(JSContext* aCx)
 {
-  JSRuntime* rt = JS_GetRuntime(aCx);
-
-  auto atomCache = static_cast<PerThreadAtomCache*>(JS_GetRuntimePrivate(rt));
+  auto atomCache = static_cast<PerThreadAtomCache*>(JS_GetContextPrivate(aCx));
 
   return static_cast<T*>(atomCache);
 }
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_AtomList_h
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -3905,38 +3905,37 @@ CanvasRenderingContext2D::DrawOrMeasureT
                                             float aX,
                                             float aY,
                                             const Optional<double>& aMaxWidth,
                                             TextDrawOperation aOp,
                                             float* aWidth)
 {
   nsresult rv;
 
-  // spec isn't clear on what should happen if aMaxWidth <= 0, so
-  // treat it as an invalid argument
-  // technically, 0 should be an invalid value as well, but 0 is the default
-  // arg, and there is no way to tell if the default was used
-  if (aMaxWidth.WasPassed() && aMaxWidth.Value() < 0)
-    return NS_ERROR_INVALID_ARG;
-
   if (!mCanvasElement && !mDocShell) {
     NS_WARNING("Canvas element must be non-null or a docshell must be provided");
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
   if (!presShell)
     return NS_ERROR_FAILURE;
 
   nsIDocument* document = presShell->GetDocument();
 
   // replace all the whitespace characters with U+0020 SPACE
   nsAutoString textToDraw(aRawText);
   TextReplaceWhitespaceCharacters(textToDraw);
 
+  // According to spec, the API should return an empty array if maxWidth was provided
+  // but is less than or equal to zero or equal to NaN.
+  if (aMaxWidth.WasPassed() && (aMaxWidth.Value() <= 0 || IsNaN(aMaxWidth.Value()))) {
+    textToDraw.Truncate();
+  }
+
   // for now, default to ltr if not in doc
   bool isRTL = false;
 
   RefPtr<nsStyleContext> canvasStyle;
   if (mCanvasElement && mCanvasElement->IsInUncomposedDoc()) {
     // try to find the closest context
     canvasStyle =
       nsComputedDOMStyle::GetStyleContextForElement(mCanvasElement,
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -15,22 +15,22 @@
 #include "WebGLContext.h"
 #include "WebGLTexelConversions.h"
 #include "WebGLTexture.h"
 
 namespace mozilla {
 namespace webgl {
 
 static bool
-UnpackFormatHasAlpha(GLenum unpackFormat)
+UnpackFormatHasColorAndAlpha(GLenum unpackFormat)
 {
     switch (unpackFormat) {
-    case LOCAL_GL_ALPHA:
     case LOCAL_GL_LUMINANCE_ALPHA:
     case LOCAL_GL_RGBA:
+    case LOCAL_GL_SRGB_ALPHA:
         return true;
 
     default:
         return false;
     }
 }
 
 static WebGLTexelFormat
@@ -211,17 +211,17 @@ TexUnpackBlob::ConvertIfNeeded(WebGLCont
         return false;
     }
 
     //////
 
     const auto dstFormat = FormatForPackingInfo(pi);
 
     bool premultMatches = (mIsSrcPremult == isDstPremult);
-    if (!UnpackFormatHasAlpha(dstDUI->unpackFormat)) {
+    if (!UnpackFormatHasColorAndAlpha(dstDUI->unpackFormat)) {
         premultMatches = true;
     }
 
     const bool needsPixelConversion = (srcFormat != dstFormat || !premultMatches);
     const bool originsMatch = (srcOrigin == dstOrigin);
 
     MOZ_ASSERT_IF(!needsPixelConversion, srcBPP == dstBPP);
 
--- a/dom/canvas/WebGLExtensionVertexArray.cpp
+++ b/dom/canvas/WebGLExtensionVertexArray.cpp
@@ -20,56 +20,44 @@ WebGLExtensionVertexArray::WebGLExtensio
 
 WebGLExtensionVertexArray::~WebGLExtensionVertexArray()
 {
 }
 
 already_AddRefed<WebGLVertexArray>
 WebGLExtensionVertexArray::CreateVertexArrayOES()
 {
-    if (mIsLost) {
-        mContext->ErrorInvalidOperation("%s: Extension is lost.",
-                                        "createVertexArrayOES");
+    if (mIsLost)
         return nullptr;
-    }
 
     return mContext->CreateVertexArray();
 }
 
 void
 WebGLExtensionVertexArray::DeleteVertexArrayOES(WebGLVertexArray* array)
 {
-    if (mIsLost) {
-        mContext->ErrorInvalidOperation("%s: Extension is lost.",
-                                        "deleteVertexArrayOES");
+    if (mIsLost)
         return;
-    }
 
     mContext->DeleteVertexArray(array);
 }
 
 bool
 WebGLExtensionVertexArray::IsVertexArrayOES(WebGLVertexArray* array)
 {
-    if (mIsLost) {
-        mContext->ErrorInvalidOperation("%s: Extension is lost.",
-                                        "isVertexArrayOES");
+    if (mIsLost)
         return false;
-    }
 
     return mContext->IsVertexArray(array);
 }
 
 void
 WebGLExtensionVertexArray::BindVertexArrayOES(WebGLVertexArray* array)
 {
-    if (mIsLost) {
-        mContext->ErrorInvalidOperation("%s: Extension is lost.",
-                                        "bindVertexArrayOES");
+    if (mIsLost)
         return;
-    }
 
     mContext->BindVertexArray(array);
 }
 
 IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionVertexArray, OES_vertex_array_object)
 
 } // namespace mozilla
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -391,16 +391,20 @@ QueryProgramInfo(WebGLProgram* prog, gl:
                                                                            elemType,
                                                                            isArray,
                                                                            baseUserName,
                                                                            mappedName);
             info->transformFeedbackVaryings.push_back(activeInfo);
         }
     }
 
+    // Frag outputs
+
+    prog->EnumerateFragOutputs(info->fragDataMap);
+
     return info.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 webgl::LinkedProgramInfo::LinkedProgramInfo(WebGLProgram* prog)
     : prog(prog)
 { }
@@ -627,19 +631,19 @@ WebGLProgram::GetFragDataLocation(const 
         return -1;
 
     if (!IsLinked()) {
         mContext->ErrorInvalidOperation("getFragDataLocation: `program` must be linked.");
         return -1;
     }
 
     const NS_LossyConvertUTF16toASCII userName(userName_wide);
+    nsCString mappedName;
 
-    nsCString mappedName;
-    if (!FindActiveOutputMappedNameByUserName(userName, &mappedName)) {
+    if (!LinkInfo()->FindFragData(userName, &mappedName)) {
         mappedName = userName;
     }
 
     gl::GLContext* gl = mContext->GL();
     gl->MakeCurrent();
 
     return gl->fGetFragDataLocation(mGLName, mappedName.BeginReading());
 }
@@ -1185,27 +1189,16 @@ WebGLProgram::LinkAndUpdate()
     if (!ok)
         return;
 
     mMostRecentLinkInfo = QueryProgramInfo(this, gl);
     MOZ_RELEASE_ASSERT(mMostRecentLinkInfo, "GFX: most recent link info not set.");
 }
 
 bool
-WebGLProgram::FindActiveOutputMappedNameByUserName(const nsACString& userName,
-                                                   nsCString* const out_mappedName) const
-{
-    if (mFragShader->FindActiveOutputMappedNameByUserName(userName, out_mappedName)) {
-        return true;
-    }
-
-    return false;
-}
-
-bool
 WebGLProgram::FindAttribUserNameByMappedName(const nsACString& mappedName,
                                              nsDependentCString* const out_userName) const
 {
     if (mVertShader->FindAttribUserNameByMappedName(mappedName, out_userName))
         return true;
 
     return false;
 }
@@ -1304,16 +1297,24 @@ WebGLProgram::FindUniformBlockByMappedNa
         return true;
 
     if (mFragShader->FindUniformBlockByMappedName(mappedName, out_userName, out_isArray))
         return true;
 
     return false;
 }
 
+void
+WebGLProgram::EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const
+{
+    MOZ_ASSERT(mFragShader);
+
+    mFragShader->EnumerateFragOutputs(out_FragOutputs);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 bool
 webgl::LinkedProgramInfo::FindAttrib(const nsCString& baseUserName,
                                      const webgl::AttribInfo** const out) const
 {
     for (const auto& attrib : attribs) {
         if (attrib.mActiveInfo->mBaseUserName == baseUserName) {
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -85,23 +85,39 @@ struct LinkedProgramInfo final
     std::vector<const UniformBlockInfo*> uniformBlocks; // Owns its contents.
     std::vector<RefPtr<WebGLActiveInfo>> transformFeedbackVaryings;
 
     // Needed for draw call validation.
     std::vector<UniformInfo*> uniformSamplers;
 
     //////
 
+    // The maps for the frag data names to the translated names.
+    std::map<nsCString, const nsCString> fragDataMap;
+
     explicit LinkedProgramInfo(WebGLProgram* prog);
     ~LinkedProgramInfo();
 
     bool FindAttrib(const nsCString& baseUserName, const AttribInfo** const out) const;
     bool FindUniform(const nsCString& baseUserName, UniformInfo** const out) const;
     bool FindUniformBlock(const nsCString& baseUserName,
                           const UniformBlockInfo** const out) const;
+
+    bool
+    FindFragData(const nsCString& baseUserName,
+                 nsCString* const out_baseMappedName) const
+    {
+        const auto itr = fragDataMap.find(baseUserName);
+        if (itr == fragDataMap.end()) {
+            return false;
+        }
+
+        *out_baseMappedName = itr->second;
+        return true;
+    }
 };
 
 } // namespace webgl
 
 class WebGLProgram final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLProgram>
     , public LinkedListElement<WebGLProgram>
@@ -139,34 +155,34 @@ public:
     void UniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) const;
 
     void LinkProgram();
     bool UseProgram() const;
     void ValidateProgram() const;
 
     ////////////////
 
-    bool FindActiveOutputMappedNameByUserName(const nsACString& userName,
-                                              nsCString* const out_mappedName) const;
     bool FindAttribUserNameByMappedName(const nsACString& mappedName,
                                         nsDependentCString* const out_userName) const;
     bool FindVaryingByMappedName(const nsACString& mappedName,
                                  nsCString* const out_userName,
                                  bool* const out_isArray) const;
     bool FindUniformByMappedName(const nsACString& mappedName,
                                  nsCString* const out_userName,
                                  bool* const out_isArray) const;
     bool FindUniformBlockByMappedName(const nsACString& mappedName,
                                       nsCString* const out_userName,
                                       bool* const out_isArray) const;
 
     void TransformFeedbackVaryings(const dom::Sequence<nsString>& varyings,
                                    GLenum bufferMode);
     already_AddRefed<WebGLActiveInfo> GetTransformFeedbackVarying(GLuint index);
 
+    void EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const;
+
     bool IsLinked() const { return mMostRecentLinkInfo; }
 
     const webgl::LinkedProgramInfo* LinkInfo() const {
         return mMostRecentLinkInfo.get();
     }
 
     WebGLContext* GetParentObject() const {
         return mContext;
--- a/dom/canvas/WebGLShader.cpp
+++ b/dom/canvas/WebGLShader.cpp
@@ -325,32 +325,16 @@ WebGLShader::BindAttribLocation(GLuint p
     const std::string* mappedNameStr = &userNameStr;
     if (mValidator)
         mValidator->FindAttribMappedNameByUserName(userNameStr, &mappedNameStr);
 
     mContext->gl->fBindAttribLocation(prog, index, mappedNameStr->c_str());
 }
 
 bool
-WebGLShader::FindActiveOutputMappedNameByUserName(const nsACString& userName,
-                                                  nsCString* const out_mappedName) const
-{
-    if (!mValidator)
-        return false;
-
-    const std::string userNameStr(userName.BeginReading());
-    const std::string* mappedNameStr;
-    if (!mValidator->FindActiveOutputMappedNameByUserName(userNameStr, &mappedNameStr))
-        return false;
-
-    *out_mappedName = mappedNameStr->c_str();
-    return true;
-}
-
-bool
 WebGLShader::FindAttribUserNameByMappedName(const nsACString& mappedName,
                                             nsDependentCString* const out_userName) const
 {
     if (!mValidator)
         return false;
 
     const std::string mappedNameStr(mappedName.BeginReading());
     const std::string* userNameStr;
@@ -408,16 +392,27 @@ WebGLShader::FindUniformBlockByMappedNam
     if (!mValidator->FindUniformBlockByMappedName(mappedNameStr, &userNameStr))
         return false;
 
     *out_userName = userNameStr.c_str();
     return true;
 }
 
 void
+WebGLShader::EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const
+{
+    out_FragOutputs.clear();
+
+    if (!mValidator) {
+        return;
+    }
+    mValidator->EnumerateFragOutputs(out_FragOutputs);
+}
+
+void
 WebGLShader::ApplyTransformFeedbackVaryings(GLuint prog,
                                             const std::vector<nsCString>& varyings,
                                             GLenum bufferMode,
                                             std::vector<std::string>* out_mappedVaryings) const
 {
     MOZ_ASSERT(mType == LOCAL_GL_VERTEX_SHADER);
     MOZ_ASSERT(!varyings.empty());
     MOZ_ASSERT(out_mappedVaryings);
--- a/dom/canvas/WebGLShader.h
+++ b/dom/canvas/WebGLShader.h
@@ -47,30 +47,30 @@ public:
     void GetShaderTranslatedSource(nsAString* out) const;
     void ShaderSource(const nsAString& source);
 
     // Util funcs
     bool CanLinkTo(const WebGLShader* prev, nsCString* const out_log) const;
     size_t CalcNumSamplerUniforms() const;
     size_t NumAttributes() const;
     void BindAttribLocation(GLuint prog, const nsCString& userName, GLuint index) const;
-    bool FindActiveOutputMappedNameByUserName(const nsACString& userName,
-                                              nsCString* const out_mappedName) const;
     bool FindAttribUserNameByMappedName(const nsACString& mappedName,
                                         nsDependentCString* const out_userName) const;
     bool FindVaryingByMappedName(const nsACString& mappedName,
                                  nsCString* const out_userName,
                                  bool* const out_isArray) const;
     bool FindUniformByMappedName(const nsACString& mappedName,
                                  nsCString* const out_userName,
                                  bool* const out_isArray) const;
     bool FindUniformBlockByMappedName(const nsACString& mappedName,
                                       nsCString* const out_userName,
                                       bool* const out_isArray) const;
 
+    void EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const;
+
     bool IsCompiled() const {
         return mTranslationSuccessful && mCompilationSuccessful;
     }
 
     void ApplyTransformFeedbackVaryings(GLuint prog,
                                         const std::vector<nsCString>& varyings,
                                         GLenum bufferMode,
                                         std::vector<std::string>* out_mappedVaryings) const;
--- a/dom/canvas/WebGLShaderValidator.cpp
+++ b/dom/canvas/WebGLShaderValidator.cpp
@@ -6,17 +6,16 @@
 #include "WebGLShaderValidator.h"
 
 #include "angle/ShaderLang.h"
 #include "gfxPrefs.h"
 #include "GLContext.h"
 #include "mozilla/Preferences.h"
 #include "MurmurHash3.h"
 #include "nsPrintfCString.h"
-#include "nsTArray.h"
 #include <string>
 #include <vector>
 #include "WebGLContext.h"
 
 namespace mozilla {
 namespace webgl {
 
 uint64_t
@@ -284,81 +283,127 @@ ShaderValidator::CanLinkTo(const ShaderV
                     *out_log = error;
                     return false;
                 }
 
                 break;
             }
         }
     }
+
+    const auto& vertVaryings = ShGetVaryings(prev->mHandle);
+    const auto& fragVaryings = ShGetVaryings(mHandle);
+    if (!vertVaryings || !fragVaryings) {
+        nsPrintfCString error("Could not create varying list.");
+        *out_log = error;
+        return false;
+    }
+
     {
-        const std::vector<sh::Varying>* vertPtr = ShGetVaryings(prev->mHandle);
-        const std::vector<sh::Varying>* fragPtr = ShGetVaryings(mHandle);
-        if (!vertPtr || !fragPtr) {
-            nsPrintfCString error("Could not create varying list.");
-            *out_log = error;
-            return false;
-        }
+        std::vector<ShVariableInfo> staticUseVaryingList;
 
-        nsTArray<ShVariableInfo> staticUseVaryingList;
-
-        for (auto itrFrag = fragPtr->begin(); itrFrag != fragPtr->end(); ++itrFrag) {
-            const ShVariableInfo varInfo = { itrFrag->type,
-                                             (int)itrFrag->elementCount() };
+        for (const auto& fragVarying : *fragVaryings) {
+            const ShVariableInfo varInfo = { fragVarying.type,
+                                             (int)fragVarying.elementCount() };
 
             static const char prefix[] = "gl_";
-            if (StartsWith(itrFrag->name, prefix)) {
-                if (itrFrag->staticUse)
-                    staticUseVaryingList.AppendElement(varInfo);
-
+            if (StartsWith(fragVarying.name, prefix)) {
+                if (fragVarying.staticUse) {
+                    staticUseVaryingList.push_back(varInfo);
+                }
                 continue;
             }
 
             bool definedInVertShader = false;
             bool staticVertUse = false;
 
-            for (auto itrVert = vertPtr->begin(); itrVert != vertPtr->end(); ++itrVert) {
-                if (itrVert->name != itrFrag->name)
+            for (const auto& vertVarying : *vertVaryings) {
+                if (vertVarying.name != fragVarying.name)
                     continue;
 
-                if (!itrVert->isSameVaryingAtLinkTime(*itrFrag)) {
+                if (!vertVarying.isSameVaryingAtLinkTime(fragVarying)) {
                     nsPrintfCString error("Varying `%s`is not linkable between"
                                           " attached shaders.",
-                                          itrFrag->name.c_str());
+                                          fragVarying.name.c_str());
                     *out_log = error;
                     return false;
                 }
 
                 definedInVertShader = true;
-                staticVertUse = itrVert->staticUse;
+                staticVertUse = vertVarying.staticUse;
                 break;
             }
 
-            if (!definedInVertShader && itrFrag->staticUse) {
+            if (!definedInVertShader && fragVarying.staticUse) {
                 nsPrintfCString error("Varying `%s` has static-use in the frag"
                                       " shader, but is undeclared in the vert"
-                                      " shader.", itrFrag->name.c_str());
+                                      " shader.", fragVarying.name.c_str());
                 *out_log = error;
                 return false;
             }
 
-            if (staticVertUse && itrFrag->staticUse)
-                staticUseVaryingList.AppendElement(varInfo);
+            if (staticVertUse && fragVarying.staticUse) {
+                staticUseVaryingList.push_back(varInfo);
+            }
         }
 
         if (!ShCheckVariablesWithinPackingLimits(mMaxVaryingVectors,
-                                                 staticUseVaryingList.Elements(),
-                                                 staticUseVaryingList.Length()))
+                                                 staticUseVaryingList.data(),
+                                                 staticUseVaryingList.size()))
         {
             *out_log = "Statically used varyings do not fit within packing limits. (see"
                        " GLSL ES Specification 1.0.17, p111)";
             return false;
         }
     }
 
+    {
+        bool isInvariant_Position = false;
+        bool isInvariant_PointSize = false;
+        bool isInvariant_FragCoord = false;
+        bool isInvariant_PointCoord = false;
+
+        for (const auto& varying : *vertVaryings) {
+            if (varying.name == "gl_Position") {
+                isInvariant_Position = varying.isInvariant;
+            } else if (varying.name == "gl_PointSize") {
+                isInvariant_PointSize = varying.isInvariant;
+            }
+        }
+
+        for (const auto& varying : *fragVaryings) {
+            if (varying.name == "gl_FragCoord") {
+                isInvariant_FragCoord = varying.isInvariant;
+            } else if (varying.name == "gl_PointCoord") {
+                isInvariant_PointCoord = varying.isInvariant;
+            }
+        }
+
+        ////
+
+        const auto fnCanBuiltInsLink = [](bool vertIsInvariant, bool fragIsInvariant) {
+            if (vertIsInvariant)
+                return true;
+
+            return !fragIsInvariant;
+        };
+
+        if (!fnCanBuiltInsLink(isInvariant_Position, isInvariant_FragCoord)) {
+            *out_log = "gl_Position must be invariant if gl_FragCoord is. (see GLSL ES"
+                       " Specification 1.0.17, p39)";
+            return false;
+        }
+
+        if (!fnCanBuiltInsLink(isInvariant_PointSize, isInvariant_PointCoord)) {
+            *out_log = "gl_PointSize must be invariant if gl_PointCoord is. (see GLSL ES"
+                       " Specification 1.0.17, p39)";
+            return false;
+        }
+    }
+
     return true;
 }
 
 size_t
 ShaderValidator::CalcNumSamplerUniforms() const
 {
     size_t accum = 0;
 
@@ -395,31 +440,16 @@ ShaderValidator::FindAttribUserNameByMap
             return true;
         }
     }
 
     return false;
 }
 
 bool
-ShaderValidator::FindActiveOutputMappedNameByUserName(const std::string& userName,
-                                                      const std::string** const out_mappedName) const
-{
-    const std::vector<sh::OutputVariable>& varibles = *ShGetOutputVariables(mHandle);
-    for (auto itr = varibles.begin(); itr != varibles.end(); ++itr) {
-        if (itr->name == userName) {
-            *out_mappedName = &(itr->mappedName);
-            return true;
-        }
-    }
-
-    return false;
-}
-
-bool
 ShaderValidator::FindAttribMappedNameByUserName(const std::string& userName,
                                                 const std::string** const out_mappedName) const
 {
     const std::vector<sh::Attribute>& attribs = *ShGetAttributes(mHandle);
     for (auto itr = attribs.begin(); itr != attribs.end(); ++itr) {
         if (itr->name == userName) {
             *out_mappedName = &(itr->mappedName);
             return true;
@@ -533,10 +563,23 @@ ShaderValidator::FindUniformBlockByMappe
             *out_userName = interface.name;
             return true;
         }
     }
 
     return false;
 }
 
+void
+ShaderValidator::EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const
+{
+    const auto* fragOutputs = ShGetOutputVariables(mHandle);
+
+    if (fragOutputs) {
+        for (const auto& fragOutput : *fragOutputs) {
+            out_FragOutputs.insert({nsCString(fragOutput.name.c_str()),
+                                    nsCString(fragOutput.mappedName.c_str())});
+        }
+    }
+}
+
 } // namespace webgl
 } // namespace mozilla
--- a/dom/canvas/WebGLShaderValidator.h
+++ b/dom/canvas/WebGLShaderValidator.h
@@ -43,32 +43,30 @@ public:
     void GetOutput(nsACString* out) const;
     bool CanLinkTo(const ShaderValidator* prev, nsCString* const out_log) const;
     size_t CalcNumSamplerUniforms() const;
     size_t NumAttributes() const;
 
     bool FindAttribUserNameByMappedName(const std::string& mappedName,
                                         const std::string** const out_userName) const;
 
-    bool FindActiveOutputMappedNameByUserName(const std::string& userName,
-                                              const std::string** const out_mappedName) const;
-
     bool FindAttribMappedNameByUserName(const std::string& userName,
                                         const std::string** const out_mappedName) const;
 
     bool FindVaryingMappedNameByUserName(const std::string& userName,
                                          const std::string** const out_mappedName) const;
 
     bool FindVaryingByMappedName(const std::string& mappedName,
                                  std::string* const out_userName,
                                  bool* const out_isArray) const;
     bool FindUniformByMappedName(const std::string& mappedName,
                                  std::string* const out_userName,
                                  bool* const out_isArray) const;
     bool FindUniformBlockByMappedName(const std::string& mappedName,
                                       std::string* const out_userName) const;
 
+    void EnumerateFragOutputs(std::map<nsCString, const nsCString> &out_FragOutputs) const;
 };
 
 } // namespace webgl
 } // namespace mozilla
 
 #endif // WEBGL_SHADER_VALIDATOR_H_
--- a/dom/canvas/test/imagebitmap_extensions.js
+++ b/dom/canvas/test/imagebitmap_extensions.js
@@ -118,33 +118,33 @@ function testColorConversions() {
   return Promise.all([// From RGBA32
                       testColorConversion("RGBA32", "RGBA32"),
                       testColorConversion("RGBA32", "BGRA32"),
                       testColorConversion("RGBA32", "RGB24"),
                       testColorConversion("RGBA32", "BGR24"),
                       testColorConversion("RGBA32", "GRAY8"),
                       testColorConversion("RGBA32", "YUV444P"),
                       testColorConversion("RGBA32", "YUV422P"),
-                      testColorConversion("RGBA32", "YUV420P", 2),
+                      testColorConversion("RGBA32", "YUV420P"),
                       testColorConversion("RGBA32", "YUV420SP_NV12"),
                       testColorConversion("RGBA32", "YUV420SP_NV21"),
                       testColorConversion("RGBA32", "HSV", 0.01),
                       testColorConversion("RGBA32", "Lab", 0.5),
 
                       // From BGRA32
                       testColorConversion("BGRA32", "RGBA32"),
                       testColorConversion("BGRA32", "BGRA32"),
                       testColorConversion("BGRA32", "RGB24"),
                       testColorConversion("BGRA32", "BGR24"),
                       testColorConversion("BGRA32", "GRAY8"),
                       testColorConversion("BGRA32", "YUV444P", 3),
-                      testColorConversion("BGRA32", "YUV422P", 2),
-                      testColorConversion("BGRA32", "YUV420P", 2),
-                      testColorConversion("BGRA32", "YUV420SP_NV12", 2),
-                      testColorConversion("BGRA32", "YUV420SP_NV21", 2),
+                      testColorConversion("BGRA32", "YUV422P"),
+                      testColorConversion("BGRA32", "YUV420P"),
+                      testColorConversion("BGRA32", "YUV420SP_NV12"),
+                      testColorConversion("BGRA32", "YUV420SP_NV21"),
                       testColorConversion("BGRA32", "HSV", 0.01),
                       testColorConversion("BGRA32", "Lab", 0.5),
 
                       // From RGB24
                       testColorConversion("RGB24", "RGBA32"),
                       testColorConversion("RGB24", "BGRA32"),
                       testColorConversion("RGB24", "RGB24"),
                       testColorConversion("RGB24", "BGR24"),
@@ -173,73 +173,73 @@ function testColorConversions() {
 
                       // From YUV444P
                       testColorConversion("YUV444P", "RGBA32"),
                       testColorConversion("YUV444P", "BGRA32"),
                       testColorConversion("YUV444P", "RGB24"),
                       testColorConversion("YUV444P", "BGR24"),
                       testColorConversion("YUV444P", "GRAY8"),
                       testColorConversion("YUV444P", "YUV444P"),
-                      testColorConversion("YUV444P", "YUV422P", 4),
+                      testColorConversion("YUV444P", "YUV422P", 3),
                       testColorConversion("YUV444P", "YUV420P", 3),
                       testColorConversion("YUV444P", "YUV420SP_NV12", 3),
                       testColorConversion("YUV444P", "YUV420SP_NV21", 3),
                       testColorConversion("YUV444P", "HSV", 0.01),
                       testColorConversion("YUV444P", "Lab", 0.01),
 
                       // From YUV422P
                       testColorConversion("YUV422P", "RGBA32"),
-                      testColorConversion("YUV422P", "BGRA32", 2),
+                      testColorConversion("YUV422P", "BGRA32"),
                       testColorConversion("YUV422P", "RGB24"),
                       testColorConversion("YUV422P", "BGR24"),
                       testColorConversion("YUV422P", "GRAY8"),
                       testColorConversion("YUV422P", "YUV444P", 3),
                       testColorConversion("YUV422P", "YUV422P"),
                       testColorConversion("YUV422P", "YUV420P"),
                       testColorConversion("YUV422P", "YUV420SP_NV12"),
                       testColorConversion("YUV422P", "YUV420SP_NV21"),
                       testColorConversion("YUV422P", "HSV", 0.01),
                       testColorConversion("YUV422P", "Lab", 0.01),
 
                       // From YUV420P
-                      testColorConversion("YUV420P", "RGBA32", 2),
-                      testColorConversion("YUV420P", "BGRA32", 2),
+                      testColorConversion("YUV420P", "RGBA32"),
+                      testColorConversion("YUV420P", "BGRA32"),
                       testColorConversion("YUV420P", "RGB24"),
                       testColorConversion("YUV420P", "BGR24"),
                       testColorConversion("YUV420P", "GRAY8"),
                       testColorConversion("YUV420P", "YUV444P", 3),
-                      testColorConversion("YUV420P", "YUV422P", 1),
+                      testColorConversion("YUV420P", "YUV422P"),
                       testColorConversion("YUV420P", "YUV420P"),
                       testColorConversion("YUV420P", "YUV420SP_NV12"),
                       testColorConversion("YUV420P", "YUV420SP_NV21"),
                       testColorConversion("YUV420P", "HSV", 0.01),
                       testColorConversion("YUV420P", "Lab", 0.01),
 
                       // From NV12
                       testColorConversion("YUV420SP_NV12", "RGBA32"),
-                      testColorConversion("YUV420SP_NV12", "BGRA32", 2),
+                      testColorConversion("YUV420SP_NV12", "BGRA32"),
                       testColorConversion("YUV420SP_NV12", "RGB24"),
                       testColorConversion("YUV420SP_NV12", "BGR24"),
                       testColorConversion("YUV420SP_NV12", "GRAY8"),
                       testColorConversion("YUV420SP_NV12", "YUV444P", 3),
-                      testColorConversion("YUV420SP_NV12", "YUV422P", 1),
+                      testColorConversion("YUV420SP_NV12", "YUV422P"),
                       testColorConversion("YUV420SP_NV12", "YUV420P"),
                       testColorConversion("YUV420SP_NV12", "YUV420SP_NV12"),
                       testColorConversion("YUV420SP_NV12", "YUV420SP_NV21"),
                       testColorConversion("YUV420SP_NV12", "HSV", 0.01),
                       testColorConversion("YUV420SP_NV12", "Lab", 0.01),
 
                       // From NV21
                       testColorConversion("YUV420SP_NV21", "RGBA32"),
-                      testColorConversion("YUV420SP_NV21", "BGRA32", 2),
+                      testColorConversion("YUV420SP_NV21", "BGRA32"),
                       testColorConversion("YUV420SP_NV21", "RGB24"),
                       testColorConversion("YUV420SP_NV21", "BGR24"),
                       testColorConversion("YUV420SP_NV21", "GRAY8"),
                       testColorConversion("YUV420SP_NV21", "YUV444P", 3),
-                      testColorConversion("YUV420SP_NV21", "YUV422P", 1),
+                      testColorConversion("YUV420SP_NV21", "YUV422P"),
                       testColorConversion("YUV420SP_NV21", "YUV420P"),
                       testColorConversion("YUV420SP_NV21", "YUV420SP_NV12"),
                       testColorConversion("YUV420SP_NV21", "YUV420SP_NV21"),
                       testColorConversion("YUV420SP_NV21", "HSV", 0.01),
                       testColorConversion("YUV420SP_NV21", "Lab", 0.01),
 
                       // From HSV
                       testColorConversion("HSV", "RGBA32"),
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -5209,17 +5209,17 @@ skip-if = (os == 'android' || os == 'lin
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__glsl__misc__shader-with-while-loop.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__glsl__misc__shader-without-precision.frag.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__glsl__misc__shaders-with-constant-expression-loop-conditions.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__glsl__misc__shaders-with-invariance.html]
-fail-if = (os == 'mac') || (os == 'win')
+fail-if = (os == 'mac')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__glsl__misc__shaders-with-mis-matching-uniforms.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__glsl__misc__shaders-with-mis-matching-varyings.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__glsl__misc__shaders-with-missing-varyings.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1') || (os == 'win' && os_version == '6.2'))
 [generated/test_2_conformance__glsl__misc__shaders-with-name-conflicts.html]
@@ -5986,17 +5986,16 @@ fail-if = (os == 'mac' && os_version == 
 [generated/test_conformance__context__context-creation-and-destruction.html]
 [generated/test_conformance__context__context-creation.html]
 skip-if = (os == 'android')
 [generated/test_conformance__context__context-eviction-with-garbage-collection.html]
 skip-if = (os == 'android')
 [generated/test_conformance__context__context-hidden-alpha.html]
 [generated/test_conformance__context__context-lost-restored.html]
 [generated/test_conformance__context__context-lost.html]
-fail-if = 1
 [generated/test_conformance__context__context-release-upon-reload.html]
 skip-if = (os == 'android')
 [generated/test_conformance__context__context-release-with-workers.html]
 skip-if = (os == 'android')
 [generated/test_conformance__context__context-size-change.html]
 [generated/test_conformance__context__context-type-test.html]
 [generated/test_conformance__context__incorrect-context-object-behaviour.html]
 [generated/test_conformance__context__methods.html]
@@ -6344,17 +6343,17 @@ fail-if = (os == 'android')
 [generated/test_conformance__glsl__misc__shader-with-version-100.vert.html]
 [generated/test_conformance__glsl__misc__shader-with-version-120.vert.html]
 [generated/test_conformance__glsl__misc__shader-with-version-130.vert.html]
 [generated/test_conformance__glsl__misc__shader-with-webgl-identifier.vert.html]
 [generated/test_conformance__glsl__misc__shader-with-while-loop.html]
 [generated/test_conformance__glsl__misc__shader-without-precision.frag.html]
 [generated/test_conformance__glsl__misc__shaders-with-constant-expression-loop-conditions.html]
 [generated/test_conformance__glsl__misc__shaders-with-invariance.html]
-fail-if = 1
+fail-if = (os == 'mac')
 [generated/test_conformance__glsl__misc__shaders-with-mis-matching-uniforms.html]
 [generated/test_conformance__glsl__misc__shaders-with-mis-matching-varyings.html]
 [generated/test_conformance__glsl__misc__shaders-with-missing-varyings.html]
 [generated/test_conformance__glsl__misc__shaders-with-name-conflicts.html]
 [generated/test_conformance__glsl__misc__shaders-with-uniform-structs.html]
 [generated/test_conformance__glsl__misc__shaders-with-varyings.html]
 [generated/test_conformance__glsl__misc__shared.html]
 [generated/test_conformance__glsl__misc__struct-assign.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -27,20 +27,16 @@
 [DEFAULT]
 subsuite = webgl
 # Skip B2G for now, until we get a handle on the longer tail of emulator bugs.
 # Bug 1136181 disabled on Mulet for intermittent failures
 skip-if = os == 'b2g' || ((os == 'linux') && (buildapp == 'mulet'))
 
 [generated/test_..__always-fail.html]
 fail-if = 1
-[generated/test_conformance__context__context-lost.html]
-fail-if = 1
-[generated/test_conformance__glsl__misc__shaders-with-invariance.html]
-fail-if = 1
 
 ####################
 # Tests requesting non-local network connections.
 
 [generated/test_conformance__more__functions__readPixelsBadArgs.html]
 # (TODO) FATAL ERROR: Non-local network connections are disabled and a connection attempt to www.opengl.org (45.55.206.190) was made.
 skip-if = 1
 [generated/test_2_conformance__more__functions__readPixelsBadArgs.html]
@@ -170,18 +166,16 @@ fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__buffers__buffer-copying-contents.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__reading__read-pixels-pack-parameters.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_conformance__attribs__gl-vertexattribpointer.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
 [generated/test_conformance__buffers__buffer-data-and-buffer-sub-data.html]
 fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
-[generated/test_2_conformance__glsl__misc__shaders-with-invariance.html]
-fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance__textures__misc__tex-sub-image-2d-bad-args.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_2_conformance2__misc__expando-loss-2.html]
 fail-if = (os == 'mac') || (os == 'win')
 [generated/test_conformance__ogles__GL__biuDepthRange__biuDepthRange_001_to_002.html]
 fail-if = (os == 'android') || (os == 'linux')
 [generated/test_conformance__ogles__GL__gl_FragCoord__gl_FragCoord_001_to_003.html]
 fail-if = (os == 'android') || (os == 'linux')
@@ -565,16 +559,20 @@ fail-if = (os == 'mac')
 [generated/test_2_conformance__glsl__variables__gl-pointcoord.html]
 fail-if = (os == 'mac')
 [generated/test_2_conformance2__state__gl-get-calls.html]
 fail-if = (os == 'mac')
 [generated/test_conformance__extensions__angle-instanced-arrays.html]
 fail-if = (os == 'mac')
 [generated/test_2_conformance__rendering__point-size.html]
 fail-if = (os == 'mac')
+[generated/test_conformance__glsl__misc__shaders-with-invariance.html]
+fail-if = (os == 'mac')
+[generated/test_2_conformance__glsl__misc__shaders-with-invariance.html]
+fail-if = (os == 'mac')
 
 [generated/test_2_conformance2__extensions__ext-color-buffer-float.html]
 skip-if = (os == 'mac' && debug)
 [generated/test_2_conformance__limits__gl-line-width.html]
 skip-if = (os == 'mac')
 [generated/test_2_conformance__misc__type-conversion-test.html]
 skip-if = (os == 'mac' && debug)
 [generated/test_2_conformance__state__gl-get-calls.html]
--- a/dom/canvas/test/webgl-mochitest/test_fuzzing_bugs.html
+++ b/dom/canvas/test/webgl-mochitest/test_fuzzing_bugs.html
@@ -77,20 +77,72 @@ function getFramebufferAttachmentParamet
   // The gl call should still work even though we use another gl context before.
   gl1.getFramebufferAttachmentParameter(gl1.FRAMEBUFFER,
                                         gl1.COLOR_ATTACHMENT0,
                                         gl1.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME);
 
   ok(true, 'WebGL test getFramebufferAttachmentParameter');
 }
 
+// Test to call getFragDataLocation() when the fragment shader is detatched.
+function getFragDataLocation_bug1259702() {
+  var canvas = document.createElement('canvas');
+
+  var gl = canvas.getContext('webgl2');
+
+  if (!gl) {
+    todo(false, 'WebGL2 is not supported');
+    return;
+  }
+
+  var program = gl.createProgram();
+
+  var vertexShaderSrc =
+    "void main(void) { \
+       gl_Position = vec4(1.0, 1.0, 1.0, 1.0); \
+    }";
+  var fragmentShaderSrc =
+    "void main(void) { \
+       gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); \
+    }";
+
+  var vertexShader = gl.createShader(gl.VERTEX_SHADER);
+  var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
+
+  gl.shaderSource(vertexShader, vertexShaderSrc);
+  gl.compileShader(vertexShader);
+  gl.attachShader(program, vertexShader);
+
+  gl.shaderSource(fragmentShader, fragmentShaderSrc);
+  gl.compileShader(fragmentShader);
+  gl.attachShader(program, fragmentShader);
+
+  gl.linkProgram(program);
+  if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
+    var lastError = gl.getProgramInfoLog(program);
+    ok(false, 'WebGL getFragDataLocation() test, error in linking:' +
+        lastError);
+    return;
+  }
+
+  gl.useProgram(program);
+
+  gl.detachShader(program, fragmentShader);
+  // Test the getFragDataLocation() call after detatch the shader.
+  // This call should not hit crash.
+  gl.getFragDataLocation(program, "foobar");
+
+  ok(true, 'WebGL getFragDataLocation() call after detatch fragment shader');
+}
+
 function run() {
   webGL2ClearBufferXXX_bug1252414();
   framebufferTexture2D_bug1257593();
   getFramebufferAttachmentParameter_bug1267100();
+  getFragDataLocation_bug1259702();
 
   SimpleTest.finish();
 }
 
 SimpleTest.waitForExplicitFinish();
 
 try {
   var prefArrArr = [
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -926,17 +926,17 @@ EventDispatcher::CreateEvent(EventTarget
   // initPopStateEvent method.  This is nuts ... but copying it is unlikely to
   // break the web.
   if (aEventType.LowerCaseEqualsLiteral("popstateevent")) {
     AutoJSContext cx;
     RootedDictionary<PopStateEventInit> init(cx);
     return PopStateEvent::Constructor(aOwner, EmptyString(), init);
   }
   if (aEventType.LowerCaseEqualsLiteral("touchevent") &&
-      TouchEvent::PrefEnabled())
+      TouchEvent::PrefEnabled(nsContentUtils::GetDocShellForEventTarget(aOwner)))
     return NS_NewDOMTouchEvent(aOwner, aPresContext, nullptr);
   if (aEventType.LowerCaseEqualsLiteral("hashchangeevent")) {
     HashChangeEventInit init;
     return HashChangeEvent::Constructor(aOwner, EmptyString(), init);
   }
   if (aEventType.LowerCaseEqualsLiteral("customevent"))
     return NS_NewDOMCustomEvent(aOwner, aPresContext, nullptr);
   if (aEventType.LowerCaseEqualsLiteral("storageevent")) {
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -1149,55 +1149,16 @@ EventListenerManager::GetLegacyEventMess
       return eMozFullscreenChange;
     case eFullscreenError:
       return eMozFullscreenError;
     default:
       return aEventMessage;
   }
 }
 
-nsIDocShell*
-EventListenerManager::GetDocShellForTarget()
-{
-  nsCOMPtr<nsINode> node(do_QueryInterface(mTarget));
-  nsIDocument* doc = nullptr;
-  nsIDocShell* docShell = nullptr;
-
-  if (node) {
-    doc = node->OwnerDoc();
-    if (!doc->GetDocShell()) {
-      bool ignore;
-      nsCOMPtr<nsPIDOMWindowInner> window =
-        do_QueryInterface(doc->GetScriptHandlingObject(ignore));
-      if (window) {
-        doc = window->GetExtantDoc();
-      }
-    }
-  } else {
-    if (nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow()) {
-      doc = window->GetExtantDoc();
-    }
-  }
-
-  if (!doc) {
-    nsCOMPtr<DOMEventTargetHelper> helper(do_QueryInterface(mTarget));
-    if (helper) {
-      if (nsPIDOMWindowInner* window = helper->GetOwner()) {
-        doc = window->GetExtantDoc();
-      }
-    }
-  }
-
-  if (doc) {
-    docShell = doc->GetDocShell();
-  }
-
-  return docShell;
-}
-
 /**
 * Causes a check for event listeners and processing by them if they exist.
 * @param an event listener
 */
 
 void
 EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
                                           WidgetEvent* aEvent,
@@ -1264,17 +1225,17 @@ EventListenerManager::HandleEventInterna
             // Maybe add a marker to the docshell's timeline, but only
             // bother with all the logic if some docshell is recording.
             nsCOMPtr<nsIDocShell> docShell;
             RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
             bool needsEndEventMarker = false;
 
             if (mIsMainThreadELM &&
                 listener->mListenerType != Listener::eNativeListener) {
-              docShell = GetDocShellForTarget();
+              docShell = nsContentUtils::GetDocShellForEventTarget(mTarget);
               if (docShell) {
                 if (timelines && timelines->HasConsumer(docShell)) {
                   needsEndEventMarker = true;
                   nsAutoString typeStr;
                   (*aDOMEvent)->GetType(typeStr);
                   uint16_t phase;
                   (*aDOMEvent)->GetEventPhase(&phase);
                   timelines->AddMarkerForDocShell(docShell, Move(
@@ -1728,21 +1689,20 @@ EventListenerManager::IsApzAwareEvent(ns
     return true;
   }
   // In theory we should schedule a repaint if the touch event pref changes,
   // because the event regions might be out of date. In practice that seems like
   // overkill because users generally shouldn't be flipping this pref, much
   // less expecting touch listeners on the page to immediately start preventing
   // scrolling without so much as a repaint. Tests that we write can work
   // around this constraint easily enough.
-  if (TouchEvent::PrefEnabled()) {
-    if (aEvent == nsGkAtoms::ontouchstart ||
-        aEvent == nsGkAtoms::ontouchmove) {
-      return true;
-    }
+  if (aEvent == nsGkAtoms::ontouchstart ||
+      aEvent == nsGkAtoms::ontouchmove) {
+    return TouchEvent::PrefEnabled(
+        nsContentUtils::GetDocShellForEventTarget(mTarget));
   }
   return false;
 }
 
 already_AddRefed<nsIScriptGlobalObject>
 EventListenerManager::GetScriptGlobalAndDocument(nsIDocument** aDoc)
 {
   nsCOMPtr<nsINode> node(do_QueryInterface(mTarget));
--- a/dom/events/EventListenerManager.h
+++ b/dom/events/EventListenerManager.h
@@ -462,18 +462,16 @@ protected:
 
   /**
    * If the given EventMessage has a legacy version that we support, then this
    * function returns that legacy version. Otherwise, this function simply
    * returns the passed-in EventMessage.
    */
   EventMessage GetLegacyEventMessage(EventMessage aEventMessage) const;
 
-  nsIDocShell* GetDocShellForTarget();
-
   void ProcessApzAwareEventListenerAdd();
 
   /**
    * Compile the "inline" event listener for aListener.  The
    * body of the listener can be provided in aBody; if this is null we
    * will look for it on mTarget.  If aBody is provided, aElement should be
    * as well; otherwise it will also be inferred from mTarget.
    */
--- a/dom/events/TouchEvent.cpp
+++ b/dom/events/TouchEvent.cpp
@@ -1,20 +1,22 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/TouchEvent.h"
 #include "mozilla/dom/Touch.h"
 #include "mozilla/dom/TouchListBinding.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TouchEvents.h"
 #include "nsContentUtils.h"
+#include "nsIDocShell.h"
 #include "mozilla/WidgetUtils.h"
 
 namespace mozilla {
 
 namespace dom {
 
 /******************************************************************************
  * TouchList
@@ -160,48 +162,74 @@ TouchEvent::ChangedTouches()
   }
   return mChangedTouches;
 }
 
 // static
 bool
 TouchEvent::PrefEnabled(JSContext* aCx, JSObject* aGlobal)
 {
+  nsIDocShell* docShell = nullptr;
+  if (aGlobal) {
+    nsGlobalWindow* win = xpc::WindowOrNull(aGlobal);
+    if (win) {
+      docShell = win->GetDocShell();
+    }
+  }
+  return PrefEnabled(docShell);
+}
+
+// static
+bool
+TouchEvent::PrefEnabled(nsIDocShell* aDocShell)
+{
   static bool sPrefCached = false;
   static int32_t sPrefCacheValue = 0;
 
+  uint32_t touchEventsOverride = nsIDocShell::TOUCHEVENTS_OVERRIDE_NONE;
+  if (aDocShell) {
+    aDocShell->GetTouchEventsOverride(&touchEventsOverride);
+  }
+
   if (!sPrefCached) {
     sPrefCached = true;
     Preferences::AddIntVarCache(&sPrefCacheValue, "dom.w3c_touch_events.enabled");
   }
 
-  bool prefValue = false;
-  if (sPrefCacheValue == 2) {
+  bool enabled = false;
+  if (touchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_ENABLED) {
+    enabled = true;
+  } else if (touchEventsOverride == nsIDocShell::TOUCHEVENTS_OVERRIDE_DISABLED) {
+    enabled = false;
+  } else {
+    if (sPrefCacheValue == 2) {
 #if defined(MOZ_B2G) || defined(MOZ_WIDGET_ANDROID)
-    // Touch support is always enabled on B2G and android.
-    prefValue = true;
+      // Touch support is always enabled on B2G and android.
+      enabled = true;
 #elif defined(XP_WIN) || MOZ_WIDGET_GTK == 3
-    static bool sDidCheckTouchDeviceSupport = false;
-    static bool sIsTouchDeviceSupportPresent = false;
-    // On Windows and GTK3 we auto-detect based on device support.
-    if (!sDidCheckTouchDeviceSupport) {
-      sDidCheckTouchDeviceSupport = true;
-      sIsTouchDeviceSupportPresent = WidgetUtils::IsTouchDeviceSupportPresent();
+      static bool sDidCheckTouchDeviceSupport = false;
+      static bool sIsTouchDeviceSupportPresent = false;
+      // On Windows and GTK3 we auto-detect based on device support.
+      if (!sDidCheckTouchDeviceSupport) {
+        sDidCheckTouchDeviceSupport = true;
+        sIsTouchDeviceSupportPresent = WidgetUtils::IsTouchDeviceSupportPresent();
+      }
+      enabled = sIsTouchDeviceSupportPresent;
+#else
+      enabled = false;
+#endif
+    } else {
+      enabled = !!sPrefCacheValue;
     }
-    prefValue = sIsTouchDeviceSupportPresent;
-#else
-    prefValue = false;
-#endif
-  } else {
-    prefValue = !!sPrefCacheValue;
   }
-  if (prefValue) {
+
+  if (enabled) {
     nsContentUtils::InitializeTouchEventTable();
   }
-  return prefValue;
+  return enabled;
 }
 
 // static
 already_AddRefed<Event>
 TouchEvent::Constructor(const GlobalObject& aGlobal,
                         const nsAString& aType,
                         const TouchEventInit& aParam,
                         ErrorResult& aRv)
--- a/dom/events/TouchEvent.h
+++ b/dom/events/TouchEvent.h
@@ -47,18 +47,17 @@ public:
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   nsISupports* GetParentObject() const
   {
     return mParent;
   }
 
-  static bool PrefEnabled(JSContext* aCx = nullptr,
-                          JSObject* aGlobal = nullptr);
+  static bool PrefEnabled(JSContext* aCx, JSObject* aGlobal);
 
   uint32_t Length() const
   {
     return mPoints.Length();
   }
   Touch* Item(uint32_t aIndex) const
   {
     return mPoints.SafeElementAt(aIndex);
@@ -114,18 +113,18 @@ public:
                       bool aCtrlKey,
                       bool aAltKey,
                       bool aShiftKey,
                       bool aMetaKey,
                       TouchList* aTouches,
                       TouchList* aTargetTouches,
                       TouchList* aChangedTouches);
 
-  static bool PrefEnabled(JSContext* aCx = nullptr,
-                          JSObject* aGlobal = nullptr);
+  static bool PrefEnabled(JSContext* aCx, JSObject* aGlobal);
+  static bool PrefEnabled(nsIDocShell* aDocShell);
 
   static already_AddRefed<Event> Constructor(const GlobalObject& aGlobal,
                                              const nsAString& aType,
                                              const TouchEventInit& aParam,
                                              ErrorResult& aRv);
 
 protected:
   ~TouchEvent() {}
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -670,16 +670,23 @@ FetchDriver::AsyncOnChannelRedirect(nsIC
                                     uint32_t aFlags,
                                     nsIAsyncVerifyRedirectCallback *aCallback)
 {
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
   if (httpChannel) {
     SetRequestHeaders(httpChannel);
   }
 
+  nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(aOldChannel);
+  nsAutoCString tRPHeaderCValue;
+  if (oldHttpChannel) {
+    oldHttpChannel->GetResponseHeader(NS_LITERAL_CSTRING("referrer-policy"),
+                                      tRPHeaderCValue);
+  }
+
   // "HTTP-redirect fetch": step 14 "Append locationURL to request's URL list."
   nsCOMPtr<nsIURI> uri;
   MOZ_ALWAYS_SUCCEEDS(aNewChannel->GetURI(getter_AddRefs(uri)));
 
   nsCOMPtr<nsIURI> uriClone;
   nsresult rv = uri->CloneIgnoringRef(getter_AddRefs(uriClone));
   if(NS_WARN_IF(NS_FAILED(rv))){
     return rv;
@@ -688,16 +695,49 @@ FetchDriver::AsyncOnChannelRedirect(nsIC
   nsCString spec;
   rv = uriClone->GetSpec(spec);
   if(NS_WARN_IF(NS_FAILED(rv))){
     return rv;
   }
 
   mRequest->AddURL(spec);
 
+  NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue);
+  // updates request’s associated referrer policy according to the
+  // Referrer-Policy header (if any).
+  if (!tRPHeaderValue.IsEmpty()) {
+    net::ReferrerPolicy net_referrerPolicy =
+      nsContentUtils::GetReferrerPolicyFromHeader(tRPHeaderValue);
+    if (net_referrerPolicy != net::RP_Unset) {
+      ReferrerPolicy referrerPolicy = mRequest->ReferrerPolicy_();
+      switch (net_referrerPolicy) {
+        case net::RP_No_Referrer:
+          referrerPolicy = ReferrerPolicy::No_referrer;
+          break;
+        case net::RP_No_Referrer_When_Downgrade:
+          referrerPolicy = ReferrerPolicy::No_referrer_when_downgrade;
+          break;
+        case net::RP_Origin:
+          referrerPolicy = ReferrerPolicy::Origin;
+          break;
+        case net::RP_Origin_When_Crossorigin:
+          referrerPolicy = ReferrerPolicy::Origin_when_cross_origin;
+          break;
+        case net::RP_Unsafe_URL:
+          referrerPolicy = ReferrerPolicy::Unsafe_url;
+          break;
+        default:
+          MOZ_ASSERT_UNREACHABLE("Invalid ReferrerPolicy value");
+          break;
+      }
+
+      mRequest->SetReferrerPolicy(referrerPolicy);
+    }
+  }
+
   aCallback->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FetchDriver::CheckListenerChain()
 {
   return NS_OK;
--- a/dom/fetch/InternalResponse.cpp
+++ b/dom/fetch/InternalResponse.cpp
@@ -24,18 +24,16 @@ InternalResponse::InternalResponse(uint1
   , mHeaders(new InternalHeaders(HeadersGuardEnum::Response))
   , mBodySize(UNKNOWN_BODY_SIZE)
 {
 }
 
 already_AddRefed<InternalResponse>
 InternalResponse::FromIPC(const IPCInternalResponse& aIPCResponse)
 {
-  MOZ_ASSERT(!aIPCResponse.urlList().IsEmpty());
-
   if (aIPCResponse.type() == ResponseType::Error) {
     return InternalResponse::NetworkError();
   }
 
   RefPtr<InternalResponse> response =
     new InternalResponse(aIPCResponse.status(),
                          aIPCResponse.statusText());
 
@@ -104,17 +102,16 @@ InternalResponse::ToIPC<mozilla::ipc::PB
 
 template<typename M>
 void
 InternalResponse::ToIPC(IPCInternalResponse* aIPCResponse,
                         M* aManager,
                         UniquePtr<mozilla::ipc::AutoIPCStream>& aAutoStream)
 {
   MOZ_ASSERT(aIPCResponse);
-  MOZ_ASSERT(!mURLList.IsEmpty());
   aIPCResponse->type() = mType;
   aIPCResponse->urlList() = mURLList;
   aIPCResponse->status() = GetUnfilteredStatus();
   aIPCResponse->statusText() = GetUnfilteredStatusText();
 
   mHeaders->ToIPC(aIPCResponse->headers(), aIPCResponse->headersGuard());
 
   aIPCResponse->channelInfo() = mChannelInfo.AsIPCChannelInfo();
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -7029,18 +7029,17 @@ HTMLInputElement::HasPatternMismatch() c
   nsIDocument* doc = OwnerDoc();
 
   return !nsContentUtils::IsPatternMatching(value, pattern, doc);
 }
 
 bool
 HTMLInputElement::IsRangeOverflow() const
 {
-  // TODO: this is temporary until bug 888324 is fixed.
-  if (!DoesMinMaxApply() || mType == NS_FORM_INPUT_MONTH) {
+  if (!DoesMinMaxApply()) {
     return false;
   }
 
   Decimal maximum = GetMaximum();
   if (maximum.isNaN()) {
     return false;
   }
 
@@ -7050,18 +7049,17 @@ HTMLInputElement::IsRangeOverflow() cons
   }
 
   return value > maximum;
 }
 
 bool
 HTMLInputElement::IsRangeUnderflow() const
 {
-  // TODO: this is temporary until bug 888324 is fixed.
-  if (!DoesMinMaxApply() || mType == NS_FORM_INPUT_MONTH) {
+  if (!DoesMinMaxApply()) {
     return false;
   }
 
   Decimal minimum = GetMinimum();
   if (minimum.isNaN()) {
     return false;
   }
 
@@ -7438,18 +7436,18 @@ HTMLInputElement::GetValidationMessage(n
         //We want to show the value as parsed when it's a number
         Decimal maximum = GetMaximum();
         MOZ_ASSERT(!maximum.isNaN());
 
         char buf[32];
         DebugOnly<bool> ok = maximum.toString(buf, ArrayLength(buf));
         maxStr.AssignASCII(buf);
         MOZ_ASSERT(ok, "buf not big enough");
-      } else if (mType == NS_FORM_INPUT_DATE || mType == NS_FORM_INPUT_TIME) {
-        msgTemplate = mType == NS_FORM_INPUT_DATE ? kDateOverTemplate : kTimeOverTemplate;
+      } else if (IsDateTimeInputType(mType)) {
+        msgTemplate = mType == NS_FORM_INPUT_TIME ? kTimeOverTemplate : kDateOverTemplate;
         GetAttr(kNameSpaceID_None, nsGkAtoms::max, maxStr);
       } else {
         msgTemplate = kNumberOverTemplate;
         NS_NOTREACHED("Unexpected input type");
       }
 
       const char16_t* params[] = { maxStr.get() };
       rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
@@ -7474,18 +7472,18 @@ HTMLInputElement::GetValidationMessage(n
 
         Decimal minimum = GetMinimum();
         MOZ_ASSERT(!minimum.isNaN());
 
         char buf[32];
         DebugOnly<bool> ok = minimum.toString(buf, ArrayLength(buf));
         minStr.AssignASCII(buf);
         MOZ_ASSERT(ok, "buf not big enough");
-      } else if (mType == NS_FORM_INPUT_DATE || mType == NS_FORM_INPUT_TIME) {
-        msgTemplate = mType == NS_FORM_INPUT_DATE ? kDateUnderTemplate : kTimeUnderTemplate;
+      } else if (IsDateTimeInputType(mType)) {
+        msgTemplate = mType == NS_FORM_INPUT_TIME ? kTimeUnderTemplate : kDateUnderTemplate;
         GetAttr(kNameSpaceID_None, nsGkAtoms::min, minStr);
       } else {
         msgTemplate = kNumberUnderTemplate;
         NS_NOTREACHED("Unexpected input type");
       }
 
       const char16_t* params[] = { minStr.get() };
       rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
@@ -8030,18 +8028,17 @@ HTMLInputElement::UpdateHasRange()
 {
   /*
    * There is a range if min/max applies for the type and if the element
    * currently have a valid min or max.
    */
 
   mHasRange = false;
 
-  // TODO: this is temporary until bug 888324 is fixed.
-  if (!DoesMinMaxApply() || mType == NS_FORM_INPUT_MONTH) {
+  if (!DoesMinMaxApply()) {
     return;
   }
 
   Decimal minimum = GetMinimum();
   if (!minimum.isNaN()) {
     mHasRange = true;
     return;
   }
--- a/dom/html/HTMLScriptElement.cpp
+++ b/dom/html/HTMLScriptElement.cpp
@@ -239,20 +239,20 @@ HTMLScriptElement::SetInnerHTML(const ns
                                 ErrorResult& aError)
 {
   aError = nsContentUtils::SetNodeTextContent(this, aInnerHTML, true);
 }
 
 // variation of this code in nsSVGScriptElement - check if changes
 // need to be transfered when modifying
 
-void
+bool
 HTMLScriptElement::GetScriptType(nsAString& type)
 {
-  GetType(type);
+  return GetAttr(kNameSpaceID_None, nsGkAtoms::type, type);
 }
 
 void
 HTMLScriptElement::GetScriptText(nsAString& text)
 {
   GetText(text);
 }
 
--- a/dom/html/HTMLScriptElement.h
+++ b/dom/html/HTMLScriptElement.h
@@ -33,17 +33,17 @@ public:
   using nsGenericHTMLElement::SetInnerHTML;
   virtual void SetInnerHTML(const nsAString& aInnerHTML,
                             mozilla::ErrorResult& aError) override;
 
   // nsIDOMHTMLScriptElement
   NS_DECL_NSIDOMHTMLSCRIPTELEMENT
 
   // nsIScriptElement
-  virtual void GetScriptType(nsAString& type) override;
+  virtual bool GetScriptType(nsAString& type) override;
   virtual void GetScriptText(nsAString& text) override;
   virtual void GetScriptCharset(nsAString& charset) override;
   virtual void FreezeUriAsyncDefer() override;
   virtual CORSMode GetCORSMode() const override;
 
   // nsIContent
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
--- a/dom/html/TextTrackManager.cpp
+++ b/dom/html/TextTrackManager.cpp
@@ -219,16 +219,18 @@ TextTrackManager::DidSeek()
     mLastTimeMarchesOnCalled = mMediaElement->CurrentTime();
   }
   mHasSeeked = true;
 }
 
 void
 TextTrackManager::UpdateCueDisplay()
 {
+  mUpdateCueDisplayDispatched = false;
+
   if (!mMediaElement || !mTextTracks) {
     return;
   }
 
   nsIFrame* frame = mMediaElement->GetPrimaryFrame();
   nsVideoFrame* videoFrame = do_QueryFrame(frame);
   if (!videoFrame) {
     return;
@@ -236,26 +238,25 @@ TextTrackManager::UpdateCueDisplay()
 
   nsCOMPtr<nsIContent> overlay = videoFrame->GetCaptionOverlay();
   nsCOMPtr<nsIContent> controls = videoFrame->GetVideoControls();
   if (!overlay) {
     return;
   }
 
   nsTArray<RefPtr<TextTrackCue> > activeCues;
-  mTextTracks->UpdateAndGetShowingCues(activeCues);
+  mTextTracks->GetShowingCues(activeCues);
 
   if (activeCues.Length() > 0) {
     RefPtr<nsVariantCC> jsCues = new nsVariantCC();
 
     jsCues->SetAsArray(nsIDataType::VTYPE_INTERFACE,
                        &NS_GET_IID(nsIDOMEventTarget),
                        activeCues.Length(),
                        static_cast<void*>(activeCues.Elements()));
-
     nsPIDOMWindowInner* window = mMediaElement->OwnerDoc()->GetInnerWindow();
     if (window) {
       sParserWrapper->ProcessCues(window, jsCues, overlay, controls);
     }
   } else if (overlay->Length() > 0) {
     nsContentUtils::SetNodeTextContent(overlay, EmptyString(), true);
   }
 }
@@ -272,16 +273,20 @@ TextTrackManager::NotifyCueAdded(TextTra
 
 void
 TextTrackManager::NotifyCueRemoved(TextTrackCue& aCue)
 {
   if (mNewCues) {
     mNewCues->RemoveCue(aCue);
   }
   DispatchTimeMarchesOn();
+  if (aCue.GetActive()) {
+    // We remove an active cue, need to update the display.
+    DispatchUpdateCueDisplay();
+  }
 }
 
 void
 TextTrackManager::PopulatePendingList()
 {
   if (!mTextTracks || !mPendingTextTracks || !mMediaElement) {
     return;
   }
@@ -519,16 +524,26 @@ public:
   {
     return mTextTracks.SafeElementAt(aIndex, nullptr);
   }
 private:
   nsTArray<RefPtr<TextTrack>> mTextTracks;
 };
 
 void
+TextTrackManager::DispatchUpdateCueDisplay()
+{
+  if (!mUpdateCueDisplayDispatched && !mShutdown &&
+      (mMediaElement->GetHasUserInteraction() || mMediaElement->IsCurrentlyPlaying())) {
+    NS_DispatchToMainThread(NewRunnableMethod(this, &TextTrackManager::UpdateCueDisplay));
+    mUpdateCueDisplayDispatched = true;
+  }
+}
+
+void
 TextTrackManager::DispatchTimeMarchesOn()
 {
   // Run the algorithm if no previous instance is still running, otherwise
   // enqueue the current playback position and whether only that changed
   // through its usual monotonic increase during normal playback; current
   // executing call upon completion will check queue for further 'work'.
   if (!mTimeMarchesOnDispatched && !mShutdown &&
       (mMediaElement->GetHasUserInteraction() || mMediaElement->IsCurrentlyPlaying())) {
--- a/dom/html/TextTrackManager.h
+++ b/dom/html/TextTrackManager.h
@@ -87,16 +87,17 @@ public:
 
   void AddListeners();
 
   // The HTMLMediaElement that this TextTrackManager manages the TextTracks of.
   RefPtr<HTMLMediaElement> mMediaElement;
 
   void DispatchTimeMarchesOn();
   void TimeMarchesOn();
+  void DispatchUpdateCueDisplay();
 
   void NotifyShutdown()
   {
     mShutdown = true;
   }
 
   void NotifyCueUpdated(TextTrackCue *aCue);
 
@@ -123,16 +124,17 @@ private:
 
   // True if the media player playback changed due to seeking prior to and
   // during running the "Time Marches On" algorithm.
   bool mHasSeeked;
   // Playback position at the time of last "Time Marches On" call
   double mLastTimeMarchesOnCalled;
 
   bool mTimeMarchesOnDispatched;
+  bool mUpdateCueDisplayDispatched;
 
   static StaticRefPtr<nsIWebVTTParserWrapper> sParserWrapper;
 
   bool performedTrackSelection;
 
   // Runs the algorithm for performing automatic track selection.
   void HonorUserPreferencesForTrackSelection();
   // Performs track selection for a single TextTrackKind.
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -1916,19 +1916,19 @@ nsGenericHTMLElement::SetUndoScopeIntern
       slots->mUndoManager = nullptr;
     }
   }
   return NS_OK;
 }
 
 // static
 bool
-nsGenericHTMLElement::TouchEventsEnabled(JSContext* /* unused */, JSObject* /* unused */)
+nsGenericHTMLElement::TouchEventsEnabled(JSContext* aCx, JSObject* aGlobal)
 {
-  return TouchEvent::PrefEnabled();
+  return TouchEvent::PrefEnabled(aCx, aGlobal);
 }
 
 //----------------------------------------------------------------------
 
 nsGenericHTMLFormElement::nsGenericHTMLFormElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo)
   , mForm(nullptr)
   , mFieldSet(nullptr)
--- a/dom/html/test/forms/test_max_attribute.html
+++ b/dom/html/test/forms/test_max_attribute.html
@@ -24,18 +24,17 @@ var data = [
   { type: 'text', apply: false },
   { type: 'search', apply: false },
   { type: 'tel', apply: false },
   { type: 'url', apply: false },
   { type: 'email', apply: false },
   { type: 'password', apply: false },
   { type: 'datetime', apply: true, todo: true },
   { type: 'date', apply: true },
-  // TODO: temporary set to false until bug 888324 is fixed.
-  { type: 'month', apply: false },
+  { type: 'month', apply: true },
   { type: 'week', apply: true, todo: true },
   { type: 'time', apply: true },
   { type: 'datetime-local', apply: true, todo: true },
   { type: 'number', apply: true },
   { type: 'range', apply: true },
   { type: 'color', apply: false },
   { type: 'checkbox', apply: false },
   { type: 'radio', apply: false },
@@ -66,17 +65,18 @@ function checkValidity(aElement, aValidi
 {
   aValidity = aApply ? aValidity : true;
 
   is(aElement.validity.valid, aValidity,
      "element validity should be " + aValidity);
   is(aElement.validity.rangeOverflow, !aValidity,
      "element overflow status should be " + !aValidity);
   var overflowMsg =
-        (aElement.type == "date" || aElement.type == "time") ?
+        (aElement.type == "date" || aElement.type == "time" ||
+         aElement.type == "month") ?
         ("Please select a value that is no later than " + aElement.max + ".") :
         ("Please select a value that is no more than " + aElement.max + ".");
   is(aElement.validationMessage,
      aValidity ? "" : overflowMsg, "Checking range overflow validation message");
 
   is(aElement.matches(":valid"), aElement.willValidate && aValidity,
      (aElement.willValidate && aValidity) ? ":valid should apply" : "valid shouldn't apply");
   is(aElement.matches(":invalid"), aElement.willValidate && !aValidity,
@@ -142,17 +142,17 @@ for (var test of data) {
       // range is special, since setting max to -1 will make it invalid since
       // it's default would then be 0, meaning it suffers from overflow.
       input.max = '-1';
       checkValidity(input, false, apply, apply);
       // Now make it something that won't cause an error below:
       input.max = '10';
       break;
     case 'month':
-      // TODO: this is temporary until bug 888324 is fixed.
+      input.max = '2016-12';
       break;
     default:
       ok(false, 'please, add a case for this new type (' + input.type + ')');
   }
 
   checkValidity(input, true, apply, apply);
 
   switch (input.type) {
@@ -338,16 +338,54 @@ for (var test of data) {
 
       input.max = '';
       checkValidity(input, true, apply, false);
 
       input.max = 'foo';
       checkValidity(input, true, apply, false);
 
       break;
+    case 'month':
+      input.value = '2016-06';
+      checkValidity(input, true, apply, apply);
+
+      input.value = '2016-12';
+      checkValidity(input, true, apply, apply);
+
+      input.value = 'foo';
+      checkValidity(input, true, apply, apply);
+
+      input.value = '2017-01';
+      checkValidity(input, false, apply, apply);
+
+      input.max = '2017-07';
+      checkValidity(input, true, apply, apply);
+
+      input.value = '2017-12';
+      checkValidity(input, false, apply, apply);
+
+      input.value = '1000-01';
+      checkValidity(input, true, apply, apply);
+
+      input.value = '20160-01';
+      checkValidity(input, false, apply, apply);
+
+      input.max = '0050-01';
+      checkValidity(input, false, apply, apply);
+
+      input.value = '0049-12';
+      checkValidity(input, true, apply, apply);
+
+      input.max = '';
+      checkValidity(input, true, apply, false);
+
+      input.max = 'foo';
+      checkValidity(input, true, apply, false);
+
+      break;
   }
 
   // Cleaning up,
   input.removeAttribute('max');
   input.value = '';
 }
 
 </script>
--- a/dom/html/test/forms/test_min_attribute.html
+++ b/dom/html/test/forms/test_min_attribute.html
@@ -24,18 +24,17 @@ var data = [
   { type: 'text', apply: false },
   { type: 'search', apply: false },
   { type: 'tel', apply: false },
   { type: 'url', apply: false },
   { type: 'email', apply: false },
   { type: 'password', apply: false },
   { type: 'datetime', apply: true, todo: true },
   { type: 'date', apply: true },
-  // TODO: temporary set to false until bug 888324 is fixed.
-  { type: 'month', apply: false },
+  { type: 'month', apply: true },
   { type: 'week', apply: true, todo: true },
   { type: 'time', apply: true },
   { type: 'datetime-local', apply: true, todo: true },
   { type: 'number', apply: true },
   { type: 'range', apply: true },
   { type: 'color', apply: false },
   { type: 'checkbox', apply: false },
   { type: 'radio', apply: false },
@@ -66,17 +65,18 @@ function checkValidity(aElement, aValidi
 {
   aValidity = aApply ? aValidity : true;
 
   is(aElement.validity.valid, aValidity,
      "element validity should be " + aValidity);
   is(aElement.validity.rangeUnderflow, !aValidity,
      "element underflow status should be " + !aValidity);
   var underflowMsg =
-        (aElement.type == "date" || aElement.type == "time") ?
+        (aElement.type == "date" || aElement.type == "time" ||
+         aElement.type == "month") ?
         ("Please select a value that is no earlier than " + aElement.min + ".") :
         ("Please select a value that is no less than " + aElement.min + ".");
   is(aElement.validationMessage,
      aValidity ? "" : underflowMsg, "Checking range underflow validation message");
 
   is(aElement.matches(":valid"), aElement.willValidate && aValidity,
      (aElement.willValidate && aValidity) ? ":valid should apply" : "valid shouldn't apply");
   is(aElement.matches(":invalid"), aElement.willValidate && !aValidity,
@@ -138,17 +138,17 @@ for (var test of data) {
       input.min = '20:20';
       break;
     case 'range':
       // range is special, since setting min to 999 will make it invalid since
       // it's default maximum is 100, its value would be 999, and it would
       // suffer from overflow.
       break;
     case 'month':
-      // TODO: this is temporary until bug 888324 is fixed.
+      input.min = '2016-06';
       break;
     default:
       ok(false, 'please, add a case for this new type (' + input.type + ')');
   }
 
   // The element should still be valid and range should apply if it can.
   checkValidity(input, true, apply, apply);
 
@@ -335,17 +335,51 @@ for (var test of data) {
 
       input.min = '';
       checkValidity(input, true, apply, false);
 
       input.min = 'foo';
       checkValidity(input, true, apply, false);
       break;
     case 'month':
-      // TODO: this is temporary until bug 888324 is fixed.
+      input.value = '2016-07';
+      checkValidity(input, true, apply, apply);
+
+      input.value = '2016-06';
+      checkValidity(input, true, apply, apply);
+
+      input.value = 'foo';
+      checkValidity(input, true, apply, apply);
+
+      input.value = '2016-05';
+      checkValidity(input, false, apply, apply);
+
+      input.min = '2016-01';
+      checkValidity(input, true, apply, apply);
+
+      input.value = '2015-12';
+      checkValidity(input, false, apply, apply);
+
+      input.value = '1000-01';
+      checkValidity(input, false, apply, apply);
+
+      input.value = '10000-01';
+      checkValidity(input, true, apply, apply);
+
+      input.min = '0010-01';
+      checkValidity(input, true, apply, apply);
+
+      input.value = '0001-01';
+      checkValidity(input, false, apply, apply);
+
+      input.min = '';
+      checkValidity(input, true, apply, false);
+
+      input.min = 'foo';
+      checkValidity(input, true, apply, false);
       break;
     default:
       ok(false, 'write tests for ' + input.type);
   }
 
   // Cleaning up,
   input.removeAttribute('min');
   input.value = '';
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -8990,16 +8990,22 @@ public:
   StartIdleMaintenance() override;
 
   virtual void
   StopIdleMaintenance() override;
 
   virtual void
   ShutdownWorkThreads() override;
 
+  virtual void
+  DidInitialize(QuotaManager* aQuotaManager) override;
+
+  virtual void
+  WillShutdown() override;
+
 private:
   ~QuotaClient();
 
   nsresult
   GetDirectory(PersistenceType aPersistenceType,
                const nsACString& aOrigin,
                nsIFile** aDirectory);
 
@@ -9910,17 +9916,17 @@ DeallocPBackgroundIndexedDBUtilsParent(P
 bool
 RecvFlushPendingFileDeletions()
 {
   AssertIsOnBackgroundThread();
 
   RefPtr<FlushPendingFileDeletionsRunnable> runnable =
     new FlushPendingFileDeletionsRunnable();
 
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
+  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
 
   return true;
 }
 
 PIndexedDBPermissionRequestParent*
 AllocPIndexedDBPermissionRequestParent(Element* aOwnerElement,
                                        nsIPrincipal* aPrincipal)
 {
@@ -12009,21 +12015,21 @@ ConnectionPool::ShutdownThread(ThreadInf
 
   nsCOMPtr<nsIThread> thread;
   aThreadInfo.mThread.swap(thread);
 
   IDB_DEBUG_LOG(("ConnectionPool shutting down thread %lu",
                  runnable->SerialNumber()));
 
   // This should clean up the thread with the profiler.
-  MOZ_ALWAYS_SUCCEEDS(thread->Dispatch(runnable, NS_DISPATCH_NORMAL));
-
-  nsCOMPtr<nsIRunnable> shutdownRunnable =
-    NewRunnableMethod(thread, &nsIThread::Shutdown);
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(shutdownRunnable));
+  MOZ_ALWAYS_SUCCEEDS(thread->Dispatch(runnable.forget(),
+                                       NS_DISPATCH_NORMAL));
+
+  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
+                        NewRunnableMethod(thread, &nsIThread::Shutdown)));
 
   mTotalThreadCount--;
 }
 
 void
 ConnectionPool::CloseIdleDatabases()
 {
   AssertIsOnOwningThread();
@@ -12128,17 +12134,17 @@ ConnectionPool::ScheduleTransaction(Tran
         for (uint32_t index = mDatabasesPerformingIdleMaintenance.Length();
              index > 0;
              index--) {
           DatabaseInfo* dbInfo = mDatabasesPerformingIdleMaintenance[index - 1];
           MOZ_ASSERT(dbInfo);
           MOZ_ASSERT(dbInfo->mThreadInfo.mThread);
 
           MOZ_ALWAYS_SUCCEEDS(
-            dbInfo->mThreadInfo.mThread->Dispatch(runnable,
+            dbInfo->mThreadInfo.mThread->Dispatch(runnable.forget(),
                                                   NS_DISPATCH_NORMAL));
         }
       }
 
       if (!created) {
         if (!aFromQueuedTransactions) {
           MOZ_ASSERT(!mQueuedTransactions.Contains(aTransactionInfo));
           mQueuedTransactions.AppendElement(aTransactionInfo);
@@ -12186,17 +12192,18 @@ ConnectionPool::ScheduleTransaction(Tran
   if (!queuedRunnables.IsEmpty()) {
     for (uint32_t index = 0, count = queuedRunnables.Length();
          index < count;
          index++) {
       nsCOMPtr<nsIRunnable> runnable;
       queuedRunnables[index].swap(runnable);
 
       MOZ_ALWAYS_SUCCEEDS(
-        dbInfo->mThreadInfo.mThread->Dispatch(runnable, NS_DISPATCH_NORMAL));
+        dbInfo->mThreadInfo.mThread->Dispatch(runnable.forget(),
+                                              NS_DISPATCH_NORMAL));
     }
 
     queuedRunnables.Clear();
   }
 
   return true;
 }
 
@@ -12520,17 +12527,17 @@ ConnectionPool::PerformIdleDatabaseMaint
     new IdleConnectionRunnable(aDatabaseInfo, aDatabaseInfo->mNeedsCheckpoint);
 
   aDatabaseInfo->mNeedsCheckpoint = false;
   aDatabaseInfo->mIdle = false;
 
   mDatabasesPerformingIdleMaintenance.AppendElement(aDatabaseInfo);
 
   MOZ_ALWAYS_SUCCEEDS(
-    aDatabaseInfo->mThreadInfo.mThread->Dispatch(runnable,
+    aDatabaseInfo->mThreadInfo.mThread->Dispatch(runnable.forget(),
                                                  NS_DISPATCH_NORMAL));
 }
 
 void
 ConnectionPool::CloseDatabase(DatabaseInfo* aDatabaseInfo)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aDatabaseInfo);
@@ -12541,17 +12548,17 @@ ConnectionPool::CloseDatabase(DatabaseIn
 
   aDatabaseInfo->mIdle = false;
   aDatabaseInfo->mNeedsCheckpoint = false;
   aDatabaseInfo->mClosing = true;
 
   nsCOMPtr<nsIRunnable> runnable = new CloseConnectionRunnable(aDatabaseInfo);
 
   MOZ_ALWAYS_SUCCEEDS(
-    aDatabaseInfo->mThreadInfo.mThread->Dispatch(runnable,
+    aDatabaseInfo->mThreadInfo.mThread->Dispatch(runnable.forget(),
                                                  NS_DISPATCH_NORMAL));
 }
 
 bool
 ConnectionPool::CloseDatabaseWhenIdleInternal(const nsACString& aDatabaseId)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(!aDatabaseId.IsEmpty());
@@ -17408,16 +17415,36 @@ QuotaClient::ShutdownWorkThreads()
     gFileHandleThreadPool.get();
   if (fileHandleThreadPool) {
     fileHandleThreadPool->Shutdown();
 
     gFileHandleThreadPool = nullptr;
   }
 }
 
+void
+QuotaClient::DidInitialize(QuotaManager* aQuotaManager)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
+    mgr->NoteLiveQuotaManager(aQuotaManager);
+  }
+}
+
+void
+QuotaClient::WillShutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
+    mgr->NoteShuttingDownQuotaManager();
+  }
+}
+
 nsresult
 QuotaClient::GetDirectory(PersistenceType aPersistenceType,
                           const nsACString& aOrigin, nsIFile** aDirectory)
 {
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "This should never fail!");
 
   nsCOMPtr<nsIFile> directory;
@@ -18578,18 +18605,17 @@ DatabaseMaintenance::FullVacuum(mozIStor
 }
 
 void
 DatabaseMaintenance::RunOnOwningThread()
 {
   AssertIsOnBackgroundThread();
 
   if (mCompleteCallback) {
-    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mCompleteCallback));
-    mCompleteCallback = nullptr;
+    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mCompleteCallback.forget()));
   }
 
   mMaintenance->UnregisterDatabaseMaintenance(this);
 }
 
 void
 DatabaseMaintenance::RunOnConnectionThread()
 {
@@ -19925,24 +19951,22 @@ FactoryOp::Open()
 
   if (permission == PermissionRequestBase::kPermissionDenied) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   {
     // These services have to be started on the main thread currently.
 
-    IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate();
-    if (NS_WARN_IF(!mgr)) {
+    IndexedDatabaseManager* mgr;
+    if (NS_WARN_IF(!(mgr = IndexedDatabaseManager::GetOrCreate()))) {
       IDB_REPORT_INTERNAL_ERR();
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
-    mgr->NoteBackgroundThread(mOwningThread);
-
     nsCOMPtr<mozIStorageService> ss;
     if (NS_WARN_IF(!(ss = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID)))) {
       IDB_REPORT_INTERNAL_ERR();
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
   }
 
   const DatabaseMetadata& metadata = mCommonParams.metadata();
@@ -20138,18 +20162,17 @@ FactoryOp::FinishSendResults()
   MOZ_ASSERT(mFactory);
 
   // Make sure to release the factory on this thread.
   RefPtr<Factory> factory;
   mFactory.swap(factory);
 
   if (mBlockedDatabaseOpen) {
     if (mDelayedOp) {
-      MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mDelayedOp));
-      mDelayedOp = nullptr;
+      MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mDelayedOp.forget()));
     }
 
     MOZ_ASSERT(gFactoryOps);
     gFactoryOps->RemoveElement(this);
 
     // Match the IncreaseBusyCount in DirectoryOpen().
     DecreaseBusyCount();
   }
@@ -22197,17 +22220,18 @@ DeleteDatabaseOp::DispatchToWorkThread()
   mState = State::DatabaseWorkVersionChange;
 
   RefPtr<VersionChangeOp> versionChangeOp = new VersionChangeOp(this);
 
   QuotaManager* quotaManager = QuotaManager::Get();
   MOZ_ASSERT(quotaManager);
 
   nsresult rv =
-    quotaManager->IOThread()->Dispatch(versionChangeOp, NS_DISPATCH_NORMAL);
+    quotaManager->IOThread()->Dispatch(versionChangeOp.forget(),
+                                       NS_DISPATCH_NORMAL);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     IDB_REPORT_INTERNAL_ERR();
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   return NS_OK;
 }
 
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -343,16 +343,20 @@ IndexedDatabaseManager::Init()
     NS_ENSURE_STATE(obs);
 
     nsresult rv =
       obs->AddObserver(this, DISKSPACEWATCHER_OBSERVER_TOPIC, false);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mDeleteTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
     NS_ENSURE_STATE(mDeleteTimer);
+
+    if (QuotaManager* quotaManager = QuotaManager::Get()) {
+      NoteLiveQuotaManager(quotaManager);
+    }
   }
 
   Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
                                        kTestingPref,
                                        &gTestingMode);
   Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
                                        kPrefExperimental,
                                        &gExperimentalFeaturesEnabled);
@@ -742,23 +746,34 @@ void
 IndexedDatabaseManager::ClearBackgroundActor()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mBackgroundActor = nullptr;
 }
 
 void
-IndexedDatabaseManager::NoteBackgroundThread(nsIEventTarget* aBackgroundThread)
+IndexedDatabaseManager::NoteLiveQuotaManager(QuotaManager* aQuotaManager)
 {
   MOZ_ASSERT(IsMainProcess());
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aBackgroundThread);
+  MOZ_ASSERT(aQuotaManager);
+
+  mBackgroundThread = aQuotaManager->OwningThread();
+}
 
-  mBackgroundThread = aBackgroundThread;
+void
+IndexedDatabaseManager::NoteShuttingDownQuotaManager()
+{
+  MOZ_ASSERT(IsMainProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ALWAYS_SUCCEEDS(mDeleteTimer->Cancel());
+
+  mBackgroundThread = nullptr;
 }
 
 already_AddRefed<FileManager>
 IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType,
                                        const nsACString& aOrigin,
                                        const nsAString& aDatabaseName)
 {
   AssertIsOnIOThread();
@@ -847,16 +862,20 @@ IndexedDatabaseManager::AsyncDeleteFile(
                                         int64_t aFileId)
 {
   MOZ_ASSERT(IsMainProcess());
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aFileManager);
   MOZ_ASSERT(aFileId > 0);
   MOZ_ASSERT(mDeleteTimer);
 
+  if (!mBackgroundThread) {
+    return NS_OK;
+  }
+
   nsresult rv = mDeleteTimer->Cancel();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = mDeleteTimer->InitWithCallback(this, kDeleteTimeoutMs,
                                       nsITimer::TYPE_ONE_SHOT);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1038,16 +1057,17 @@ IndexedDatabaseManager::Observe(nsISuppo
    return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
 IndexedDatabaseManager::Notify(nsITimer* aTimer)
 {
   MOZ_ASSERT(IsMainProcess());
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mBackgroundThread);
 
   for (auto iter = mPendingDeleteInfos.ConstIter(); !iter.Done(); iter.Next()) {
     auto key = iter.Key();
     auto value = iter.Data();
     MOZ_ASSERT(!value->IsEmpty());
 
     RefPtr<DeleteFilesRunnable> runnable =
       new DeleteFilesRunnable(mBackgroundThread, key, *value);
--- a/dom/indexedDB/IndexedDatabaseManager.h
+++ b/dom/indexedDB/IndexedDatabaseManager.h
@@ -23,29 +23,36 @@ class nsIEventTarget;
 namespace mozilla {
 
 class EventChainPostVisitor;
 
 namespace dom {
 
 class IDBFactory;
 
+namespace quota {
+
+class QuotaManager;
+
+} // namespace quota
+
 namespace indexedDB {
 
 class BackgroundUtilsChild;
 class FileManager;
 class FileManagerInfo;
 
 } // namespace indexedDB
 
 class IndexedDatabaseManager final
   : public nsIObserver
   , public nsITimerCallback
 {
   typedef mozilla::dom::quota::PersistenceType PersistenceType;
+  typedef mozilla::dom::quota::QuotaManager QuotaManager;
   typedef mozilla::dom::indexedDB::FileManager FileManager;
   typedef mozilla::dom::indexedDB::FileManagerInfo FileManagerInfo;
 
 public:
   enum LoggingMode
   {
     Logging_Disabled = 0,
     Logging_Concise,
@@ -123,17 +130,20 @@ public:
 
   static bool
   IsFileHandleEnabled();
 
   void
   ClearBackgroundActor();
 
   void
-  NoteBackgroundThread(nsIEventTarget* aBackgroundThread);
+  NoteLiveQuotaManager(QuotaManager* aQuotaManager);
+
+  void
+  NoteShuttingDownQuotaManager();
 
   already_AddRefed<FileManager>
   GetFileManager(PersistenceType aPersistenceType,
                  const nsACString& aOrigin,
                  const nsAString& aDatabaseName);
 
   void
   AddFileManager(FileManager* aFileManager);
--- a/dom/indexedDB/moz.build
+++ b/dom/indexedDB/moz.build
@@ -95,25 +95,17 @@ include('/ipc/chromium/chromium-config.m
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['GNU_CC']:
     # Suppress gcc warning about a comparison being always false due to the
     # range of the data type
     SOURCES['Key.cpp'].flags += ['-Wno-error=type-limits']
     CXXFLAGS += ['-Wno-error=shadow']
 
-if CONFIG['_MSC_VER']:
-    # This is intended as a temporary hack to support building with VS2015.
-    CXXFLAGS += ['-wd4577']
-
 LOCAL_INCLUDES += [
     '/db/sqlite3/src',
     '/dom/base',
     '/dom/storage',
     '/dom/workers',
     '/ipc/glue',
     '/xpcom/build',
     '/xpcom/threads',
 ]
-
-if CONFIG['ENABLE_INTL_API']:
-    CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
-    LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -143,29 +143,16 @@ NS_IMPL_ISUPPORTS(ContentListener, nsIDO
 
 static const CSSSize kDefaultViewportSize(980, 480);
 
 static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
 
 typedef nsDataHashtable<nsUint64HashKey, TabChild*> TabChildMap;
 static TabChildMap* sTabChildren;
 
-static bool
-UsingCompositorLRU()
-{
-  static bool sHavePrefs = false;
-  static uint32_t sCompositorLRUSize = 0;
-  if (!sHavePrefs) {
-    sHavePrefs = true;
-    Preferences::AddUintVarCache(&sCompositorLRUSize,
-                                 "layers.compositor-lru-size", 0);
-  }
-  return sCompositorLRUSize != 0;
-}
-
 TabChildBase::TabChildBase()
   : mTabChildGlobal(nullptr)
 {
   mozilla::HoldJSObjects(this);
 }
 
 TabChildBase::~TabChildBase()
 {
@@ -2867,38 +2854,29 @@ TabChild::NotifyPainted()
         mRemoteFrame->SendNotifyCompositorTransaction();
         mNotified = true;
     }
 }
 
 void
 TabChild::MakeVisible()
 {
-  CompositorBridgeChild* compositor = CompositorBridgeChild::Get();
-  if (UsingCompositorLRU()) {
-    compositor->SendNotifyVisible(mLayersId);
-  }
-
   if (mPuppetWidget) {
     mPuppetWidget->Show(true);
   }
 }
 
 void
 TabChild::MakeHidden()
 {
   CompositorBridgeChild* compositor = CompositorBridgeChild::Get();
-  if (UsingCompositorLRU()) {
-    compositor->SendNotifyHidden(mLayersId);
-  } else {
-    // Clear cached resources directly. This avoids one extra IPC
-    // round-trip from CompositorBridgeChild to CompositorBridgeParent when
-    // CompositorLRU is not used.
-    compositor->RecvClearCachedResources(mLayersId);
-  }
+
+  // Clear cached resources directly. This avoids one extra IPC
+  // round-trip from CompositorBridgeChild to CompositorBridgeParent.
+  compositor->RecvClearCachedResources(mLayersId);
 
   if (mPuppetWidget) {
     mPuppetWidget->Show(false);
   }
 }
 
 void
 TabChild::UpdateHitRegion(const nsRegion& aRegion)
--- a/dom/media/TextTrack.cpp
+++ b/dom/media/TextTrack.cpp
@@ -88,26 +88,26 @@ TextTrack::WrapObject(JSContext* aCx, JS
 }
 
 void
 TextTrack::SetMode(TextTrackMode aValue)
 {
   if (mMode != aValue) {
     mMode = aValue;
     if (aValue == TextTrackMode::Disabled) {
-      SetCuesInactive();
       // Remove all the cues in MediaElement.
       if (mTextTrackList) {
         HTMLMediaElement* mediaElement = mTextTrackList->GetMediaElement();
         if (mediaElement) {
           for (size_t i = 0; i < mCueList->Length(); ++i) {
             mediaElement->NotifyCueRemoved(*(*mCueList)[i]);
           }
         }
       }
+      SetCuesInactive();
     } else {
       // Add all the cues into MediaElement.
       if (mTextTrackList) {
         HTMLMediaElement* mediaElement = mTextTrackList->GetMediaElement();
         if (mediaElement) {
           for (size_t i = 0; i < mCueList->Length(); ++i) {
             mediaElement->NotifyCueAdded(*(*mCueList)[i]);
           }
--- a/dom/media/TextTrackList.cpp
+++ b/dom/media/TextTrackList.cpp
@@ -35,29 +35,21 @@ TextTrackList::TextTrackList(nsPIDOMWind
 {
 }
 
 TextTrackList::~TextTrackList()
 {
 }
 
 void
-TextTrackList::UpdateAndGetShowingCues(nsTArray<RefPtr<TextTrackCue> >& aCues)
+TextTrackList::GetShowingCues(nsTArray<RefPtr<TextTrackCue> >& aCues)
 {
   nsTArray< RefPtr<TextTrackCue> > cues;
   for (uint32_t i = 0; i < Length(); i++) {
-    TextTrackMode mode = mTextTracks[i]->Mode();
-    // If the mode is hidden then we just need to update the active cue list,
-    // we don't need to show it on the video.
-    if (mode == TextTrackMode::Hidden) {
-      mTextTracks[i]->UpdateActiveCueList();
-    } else if (mode == TextTrackMode::Showing) {
-      // If the mode is showing then we need to update the cue list and show it
-      // on the video. GetActiveCueArray() calls UpdateActiveCueList() so we
-      // don't need to call it explicitly.
+    if (mTextTracks[i]->Mode() == TextTrackMode::Showing) {
       mTextTracks[i]->GetActiveCueArray(cues);
       aCues.AppendElements(cues);
     }
   }
 }
 
 JSObject*
 TextTrackList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
--- a/dom/media/TextTrackList.h
+++ b/dom/media/TextTrackList.h
@@ -32,17 +32,17 @@ public:
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   uint32_t Length() const
   {
     return mTextTracks.Length();
   }
 
   // Get all the current active cues.
-  void UpdateAndGetShowingCues(nsTArray<RefPtr<TextTrackCue> >& aCues);
+  void GetShowingCues(nsTArray<RefPtr<TextTrackCue> >& aCues);
 
   TextTrack* IndexedGetter(uint32_t aIndex, bool& aFound);
   TextTrack* operator[](uint32_t aIndex);
 
   already_AddRefed<TextTrack> AddTextTrack(TextTrackKind aKind,
                                            const nsAString& aLabel,
                                            const nsAString& aLanguage,
                                            TextTrackMode aMode,
--- a/dom/media/directshow/AudioSinkFilter.cpp
+++ b/dom/media/directshow/AudioSinkFilter.cpp
@@ -18,18 +18,18 @@
 
 DEFINE_GUID(CLSID_MozAudioSinkFilter, 0x1872d8c8, 0xea8d, 0x4c34, 0xae, 0x96, 0x69, 0xde,
             0xf1, 0x33, 0x7b, 0x33);
 
 using namespace mozilla::media;
 
 namespace mozilla {
 
-extern LogModule* GetDirectShowLog();
-#define LOG(...) MOZ_LOG(GetDirectShowLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+static LazyLogModule gDirectShowLog("DirectShowDecoder");
+#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 AudioSinkFilter::AudioSinkFilter(const wchar_t* aObjectName, HRESULT* aOutResult)
   : BaseFilter(aObjectName, CLSID_MozAudioSinkFilter),
     mFilterCritSec("AudioSinkFilter::mFilterCritSec")
 {
   (*aOutResult) = S_OK;
   mInputPin = new AudioSinkInputPin(L"AudioSinkInputPin",
                                     this,
--- a/dom/media/directshow/AudioSinkInputPin.cpp
+++ b/dom/media/directshow/AudioSinkInputPin.cpp
@@ -10,18 +10,18 @@
 #include "mozilla/Logging.h"
 
 #include <wmsdkidl.h>
 
 using namespace mozilla::media;
 
 namespace mozilla {
 
-extern LogModule* GetDirectShowLog();
-#define LOG(...) MOZ_LOG(GetDirectShowLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+static LazyLogModule gDirectShowLog("DirectShowDecoder");
+#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 AudioSinkInputPin::AudioSinkInputPin(wchar_t* aObjectName,
                                      AudioSinkFilter* aFilter,
                                      mozilla::CriticalSection* aLock,
                                      HRESULT* aOutResult)
   : BaseInputPin(aObjectName, aFilter, aLock, aOutResult, aObjectName),
     mSegmentStartTime(0)
 {
--- a/dom/media/directshow/DirectShowDecoder.cpp
+++ b/dom/media/directshow/DirectShowDecoder.cpp
@@ -1,16 +1,17 @@
 /* -*- 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 "DirectShowDecoder.h"
 #include "DirectShowReader.h"
+#include "DirectShowUtils.h"
 #include "MediaDecoderStateMachine.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/WindowsVersion.h"
 
 namespace mozilla {
 
 MediaDecoderStateMachine* DirectShowDecoder::CreateStateMachine()
 {
--- a/dom/media/directshow/DirectShowReader.cpp
+++ b/dom/media/directshow/DirectShowReader.cpp
@@ -12,23 +12,24 @@
 #include "SourceFilter.h"
 #include "SampleSink.h"
 #include "VideoUtils.h"
 
 using namespace mozilla::media;
 
 namespace mozilla {
 
-LogModule*
-GetDirectShowLog() {
-  static LazyLogModule log("DirectShowDecoder");
-  return log;
-}
+// Windows XP's MP3 decoder filter. This is available on XP only, on Vista
+// and later we can use the DMO Wrapper filter and MP3 decoder DMO.
+const GUID DirectShowReader::CLSID_MPEG_LAYER_3_DECODER_FILTER =
+{ 0x38BE3000, 0xDBF4, 0x11D0, {0x86, 0x0E, 0x00, 0xA0, 0x24, 0xCF, 0xEF, 0x6D} };
 
-#define LOG(...) MOZ_LOG(GetDirectShowLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+
+static LazyLogModule gDirectShowLog("DirectShowDecoder");
+#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 DirectShowReader::DirectShowReader(AbstractMediaDecoder* aDecoder)
   : MediaDecoderReader(aDecoder),
     mMP3FrameParser(aDecoder->GetResource()->GetLength()),
 #ifdef DIRECTSHOW_REGISTER_GRAPH
     mRotRegister(0),
 #endif
     mNumChannels(0),
@@ -73,21 +74,16 @@ ParseMP3Headers(MP3FrameParser *aParser,
 
     aParser->Parse(reinterpret_cast<uint8_t*>(buffer), bytesRead, offset);
     offset += bytesRead;
   }
 
   return aParser->IsMP3() ? NS_OK : NS_ERROR_FAILURE;
 }
 
-// Windows XP's MP3 decoder filter. This is available on XP only, on Vista
-// and later we can use the DMO Wrapper filter and MP3 decoder DMO.
-static const GUID CLSID_MPEG_LAYER_3_DECODER_FILTER =
-{ 0x38BE3000, 0xDBF4, 0x11D0, {0x86, 0x0E, 0x00, 0xA0, 0x24, 0xCF, 0xEF, 0x6D} };
-
 nsresult
 DirectShowReader::ReadMetadata(MediaInfo* aInfo,
                                MetadataTags** aTags)
 {
   MOZ_ASSERT(OnTaskQueue());
   HRESULT hr;
   nsresult rv;
 
--- a/dom/media/directshow/DirectShowReader.h
+++ b/dom/media/directshow/DirectShowReader.h
@@ -55,16 +55,18 @@ public:
                         int64_t aTimeThreshold) override;
 
   nsresult ReadMetadata(MediaInfo* aInfo,
                         MetadataTags** aTags) override;
 
   RefPtr<SeekPromise>
   Seek(SeekTarget aTarget, int64_t aEndTime) override;
 
+  static const GUID CLSID_MPEG_LAYER_3_DECODER_FILTER;
+
 private:
   // Notifies the filter graph that playback is complete. aStatus is
   // the code to send to the filter graph. Always returns false, so
   // that we can just "return Finish()" from DecodeAudioData().
   bool Finish(HRESULT aStatus);
 
   nsresult SeekInternal(int64_t aTime);
 
--- a/dom/media/directshow/DirectShowUtils.cpp
+++ b/dom/media/directshow/DirectShowUtils.cpp
@@ -282,17 +282,17 @@ CanDecodeMP3UsingDirectShow()
                               nullptr,
                               CLSCTX_INPROC_SERVER,
                               IID_IBaseFilter,
                               getter_AddRefs(filter)))) {
     return false;
   }
 
   // Can we create either the WinXP MP3 decoder filter or the MP3 DMO decoder?
-  if (FAILED(CoCreateInstance(CLSID_MPEG_LAYER_3_DECODER_FILTER,
+  if (FAILED(CoCreateInstance(DirectShowReader::CLSID_MPEG_LAYER_3_DECODER_FILTER,
                               nullptr,
                               CLSCTX_INPROC_SERVER,
                               IID_IBaseFilter,
                               getter_AddRefs(filter))) &&
       FAILED(CreateMP3DMOWrapperFilter(getter_AddRefs(filter)))) {
     return false;
   }
 
--- a/dom/media/directshow/SampleSink.cpp
+++ b/dom/media/directshow/SampleSink.cpp
@@ -9,18 +9,18 @@
 #include "AudioSinkInputPin.h"
 #include "VideoUtils.h"
 #include "mozilla/Logging.h"
 
 using namespace mozilla::media;
 
 namespace mozilla {
 
-extern LogModule* GetDirectShowLog();
-#define LOG(...) MOZ_LOG(GetDirectShowLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+static LazyLogModule gDirectShowLog("DirectShowDecoder");
+#define LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 SampleSink::SampleSink()
   : mMonitor("SampleSink"),
     mIsFlushing(false),
     mAtEOS(false)
 {
   MOZ_COUNT_CTOR(SampleSink);
 }
@@ -60,17 +60,17 @@ SampleSink::Receive(IMediaSample* aSampl
     }
     if (mAtEOS) {
       return E_UNEXPECTED;
     }
     // Wait until the consumer thread consumes the sample.
     mon.Wait();
   }
 
-  if (MOZ_LOG_TEST(GetDirectShowLog(), LogLevel::Debug)) {
+  if (MOZ_LOG_TEST(gDirectShowLog, LogLevel::Debug)) {
     REFERENCE_TIME start = 0, end = 0;
     HRESULT hr = aSample->GetMediaTime(&start, &end);
     LOG("SampleSink::Receive() [%4.2lf-%4.2lf]",
         (double)RefTimeToUsecs(start) / USECS_PER_S,
         (double)RefTimeToUsecs(end) / USECS_PER_S);
   }
 
   mSample = aSample;
@@ -97,17 +97,17 @@ SampleSink::Extract(RefPtr<IMediaSample>
       // before reporting EOS.
       return E_UNEXPECTED;
     }
     // Wait until the producer thread gives us a sample.
     mon.Wait();
   }
   aOutSample = mSample;
 
-  if (MOZ_LOG_TEST(GetDirectShowLog(), LogLevel::Debug)) {
+  if (MOZ_LOG_TEST(gDirectShowLog, LogLevel::Debug)) {
     int64_t start = 0, end = 0;
     mSample->GetMediaTime(&start, &end);
     LOG("SampleSink::Extract() [%4.2lf-%4.2lf]",
         (double)RefTimeToUsecs(start) / USECS_PER_S,
         (double)RefTimeToUsecs(end) / USECS_PER_S);
   }
 
   mSample = nullptr;
--- a/dom/media/directshow/SourceFilter.cpp
+++ b/dom/media/directshow/SourceFilter.cpp
@@ -15,18 +15,18 @@
 using namespace mozilla::media;
 
 namespace mozilla {
 
 // Define to trace what's on...
 //#define DEBUG_SOURCE_TRACE 1
 
 #if defined (DEBUG_SOURCE_TRACE)
-extern LogModule* GetDirectShowLog();
-#define DIRECTSHOW_LOG(...) MOZ_LOG(GetDirectShowLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+static LazyLogModule gDirectShowLog("DirectShowDecoder");
+#define DIRECTSHOW_LOG(...) MOZ_LOG(gDirectShowLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 #else
 #define DIRECTSHOW_LOG(...)
 #endif
 
 static HRESULT
 DoGetInterface(IUnknown* aUnknown, void** aInterface)
 {
   if (!aInterface)
--- a/dom/media/directshow/moz.build
+++ b/dom/media/directshow/moz.build
@@ -8,26 +8,26 @@ EXPORTS += [
     'AudioSinkFilter.h',
     'AudioSinkInputPin.h',
     'DirectShowDecoder.h',
     'DirectShowReader.h',
     'DirectShowUtils.h',
 ]
 
 UNIFIED_SOURCES += [
-    'AudioSinkInputPin.cpp',
     'DirectShowDecoder.cpp',
-    'DirectShowReader.cpp',
     'DirectShowUtils.cpp',
-    'SampleSink.cpp',
     'SourceFilter.cpp',
 ]
 
 SOURCES += [
     'AudioSinkFilter.cpp',
+    'AudioSinkInputPin.cpp',
+    'DirectShowReader.cpp',
+    'SampleSink.cpp',
 ]
 
 # If WebRTC isn't being built, we need to compile the DirectShow base classes so that
 # they're available at link time.
 if not CONFIG['MOZ_WEBRTC']:
     SOURCES += [
         '/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseFilter.cpp',
         '/media/webrtc/trunk/webrtc/modules/video_capture/windows/BaseInputPin.cpp',
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -3,302 +3,194 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef CDMProxy_h_
 #define CDMProxy_h_
 
 #include "mozilla/CDMCaps.h"
-#include "mozilla/Monitor.h"
 #include "mozilla/MozPromise.h"
 
+#include "mozilla/dom/MediaKeyMessageEvent.h"
 #include "mozilla/dom/MediaKeys.h"
 
 #include "nsIThread.h"
-#include "nsString.h"
-#include "nsAutoPtr.h"
-#include "GMPDecryptorProxy.h"
 
 namespace mozilla {
 class MediaRawData;
-class GMPCDMCallbackProxy;
 
-namespace dom {
-class MediaKeySession;
-} // namespace dom
+enum DecryptStatus {
+  Ok = 0,
+  GenericErr = 1,
+  NoKeyErr = 2,
+  AbortedErr = 3,
+};
 
 struct DecryptResult {
-  DecryptResult(GMPErr aStatus, MediaRawData* aSample)
+  DecryptResult(DecryptStatus aStatus, MediaRawData* aSample)
     : mStatus(aStatus)
     , mSample(aSample)
   {}
-  GMPErr mStatus;
+  DecryptStatus mStatus;
   RefPtr<MediaRawData> mSample;
 };
 
-// Proxies calls GMP/CDM, and proxies calls back.
+// Proxies calls CDM, and proxies calls back.
 // Note: Promises are passed in via a PromiseId, so that the ID can be
 // passed via IPC to the CDM, which can then signal when to reject or
 // resolve the promise using its PromiseId.
 class CDMProxy {
+protected:
   typedef dom::PromiseId PromiseId;
   typedef dom::SessionType SessionType;
 public:
 
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CDMProxy)
+  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+  NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
 
   typedef MozPromise<DecryptResult, DecryptResult, /* IsExclusive = */ true> DecryptPromise;
 
   // Main thread only.
-  CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem);
+  CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem)
+  : mKeys(aKeys), mKeySystem(aKeySystem)
+  {}
 
   // Main thread only.
   // Loads the CDM corresponding to mKeySystem.
   // Calls MediaKeys::OnCDMCreated() when the CDM is created.
-  void Init(PromiseId aPromiseId,
-            const nsAString& aOrigin,
-            const nsAString& aTopLevelOrigin,
-            const nsAString& aGMPName,
-            bool aInPrivateBrowsing,
-            GMPCrashHelper* aHelper);
+  virtual void Init(PromiseId aPromiseId,
+                    const nsAString& aOrigin,
+                    const nsAString& aTopLevelOrigin,
+                    const nsAString& aName,
+                    bool aInPrivateBrowsing) = 0;
 
   // Main thread only.
   // Uses the CDM to create a key session.
   // Calls MediaKeys::OnSessionActivated() when session is created.
   // Assumes ownership of (Move()s) aInitData's contents.
-  void CreateSession(uint32_t aCreateSessionToken,
-                     dom::SessionType aSessionType,
-                     PromiseId aPromiseId,
-                     const nsAString& aInitDataType,
-                     nsTArray<uint8_t>& aInitData);
+  virtual void CreateSession(uint32_t aCreateSessionToken,
+                             dom::SessionType aSessionType,
+                             PromiseId aPromiseId,
+                             const nsAString& aInitDataType,
+                             nsTArray<uint8_t>& aInitData) = 0;
 
   // Main thread only.
   // Uses the CDM to load a presistent session stored on disk.
   // Calls MediaKeys::OnSessionActivated() when session is loaded.
-  void LoadSession(PromiseId aPromiseId,
-                   const nsAString& aSessionId);
+  virtual void LoadSession(PromiseId aPromiseId,
+                           const nsAString& aSessionId) = 0;
 
   // Main thread only.
   // Sends a new certificate to the CDM.
   // Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
   // processed the request.
   // Assumes ownership of (Move()s) aCert's contents.
-  void SetServerCertificate(PromiseId aPromiseId,
-                            nsTArray<uint8_t>& aCert);
+  virtual void SetServerCertificate(PromiseId aPromiseId,
+                                    nsTArray<uint8_t>& aCert) = 0;
 
   // Main thread only.
   // Sends an update to the CDM.
   // Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
   // processed the request.
   // Assumes ownership of (Move()s) aResponse's contents.
-  void UpdateSession(const nsAString& aSessionId,
-                     PromiseId aPromiseId,
-                     nsTArray<uint8_t>& aResponse);
+  virtual void UpdateSession(const nsAString& aSessionId,
+                             PromiseId aPromiseId,
+                             nsTArray<uint8_t>& aResponse) = 0;
 
   // Main thread only.
   // Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
   // processed the request.
   // If processing this operation results in the session actually closing,
   // we also call MediaKeySession::OnClosed(), which in turn calls
   // MediaKeys::OnSessionClosed().
-  void CloseSession(const nsAString& aSessionId,
-                    PromiseId aPromiseId);
+  virtual void CloseSession(const nsAString& aSessionId,
+                            PromiseId aPromiseId) = 0;
 
   // Main thread only.
   // Removes all data for a persisent session.
   // Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
   // processed the request.
-  void RemoveSession(const nsAString& aSessionId,
-                     PromiseId aPromiseId);
+  virtual void RemoveSession(const nsAString& aSessionId,
+                             PromiseId aPromiseId) = 0;
 
   // Main thread only.
-  void Shutdown();
+  virtual void Shutdown() = 0;
 
   // Main thread only.
-  void Terminated();
+  virtual void Terminated() = 0;
 
   // Threadsafe.
-  const nsCString& GetNodeId() const;
+  virtual const nsCString& GetNodeId() const = 0;
 
   // Main thread only.
-  void OnSetSessionId(uint32_t aCreateSessionToken,
-                      const nsAString& aSessionId);
+  virtual void OnSetSessionId(uint32_t aCreateSessionToken,
+                              const nsAString& aSessionId) = 0;
 
   // Main thread only.
-  void OnResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess);
+  virtual void OnResolveLoadSessionPromise(uint32_t aPromiseId,
+                                           bool aSuccess) = 0;
 
   // Main thread only.
-  void OnSessionMessage(const nsAString& aSessionId,
-                        GMPSessionMessageType aMessageType,
-                        nsTArray<uint8_t>& aMessage);
+  virtual void OnSessionMessage(const nsAString& aSessionId,
+                                dom::MediaKeyMessageType aMessageType,
+                                nsTArray<uint8_t>& aMessage) = 0;
 
   // Main thread only.
-  void OnExpirationChange(const nsAString& aSessionId,
-                          GMPTimestamp aExpiryTime);
+  virtual void OnExpirationChange(const nsAString& aSessionId,
+                                  int64_t aExpiryTime) = 0;
 
   // Main thread only.
-  void OnSessionClosed(const nsAString& aSessionId);
+  virtual void OnSessionClosed(const nsAString& aSessionId) = 0;
 
   // Main thread only.
-  void OnSessionError(const nsAString& aSessionId,
-                      nsresult aException,
-                      uint32_t aSystemCode,
-                      const nsAString& aMsg);
+  virtual void OnSessionError(const nsAString& aSessionId,
+                              nsresult aException,
+                              uint32_t aSystemCode,
+                              const nsAString& aMsg) = 0;
 
   // Main thread only.
-  void OnRejectPromise(uint32_t aPromiseId,
-                       nsresult aDOMException,
-                       const nsCString& aMsg);
+  virtual void OnRejectPromise(uint32_t aPromiseId,
+                               nsresult aDOMException,
+                               const nsCString& aMsg) = 0;
 
-  RefPtr<DecryptPromise> Decrypt(MediaRawData* aSample);
+  virtual RefPtr<DecryptPromise> Decrypt(MediaRawData* aSample) = 0;
+
+  // Owner thread only.
+  virtual void OnDecrypted(uint32_t aId,
+                           DecryptStatus aResult,
+                           const nsTArray<uint8_t>& aDecryptedData) = 0;
 
   // Reject promise with DOMException corresponding to aExceptionCode.
   // Can be called from any thread.
-  void RejectPromise(PromiseId aId, nsresult aExceptionCode,
-                     const nsCString& aReason);
+  virtual void RejectPromise(PromiseId aId,
+                             nsresult aExceptionCode,
+                             const nsCString& aReason) = 0;
 
   // Resolves promise with "undefined".
   // Can be called from any thread.
-  void ResolvePromise(PromiseId aId);
+  virtual void ResolvePromise(PromiseId aId) = 0;
 
   // Threadsafe.
-  const nsString& KeySystem() const;
-
-  // GMP thread only.
-  void gmp_Decrypted(uint32_t aId,
-                     GMPErr aResult,
-                     const nsTArray<uint8_t>& aDecryptedData);
-
-  CDMCaps& Capabilites();
-
-  // Main thread only.
-  void OnKeyStatusesChange(const nsAString& aSessionId);
-
-  void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
-                             nsTArray<nsCString>& aSessionIds);
-
-#ifdef DEBUG
-  bool IsOnGMPThread();
-#endif
+  virtual const nsString& KeySystem() const = 0;
 
-private:
-  friend class gmp_InitDoneCallback;
-  friend class gmp_InitGetGMPDecryptorCallback;
-
-  struct InitData {
-    uint32_t mPromiseId;
-    nsString mOrigin;
-    nsString mTopLevelOrigin;
-    nsString mGMPName;
-    RefPtr<GMPCrashHelper> mCrashHelper;
-    bool mInPrivateBrowsing;
-  };
-
-  // GMP thread only.
-  void gmp_Init(nsAutoPtr<InitData>&& aData);
-  void gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData);
-  void gmp_InitGetGMPDecryptor(nsresult aResult,
-                               const nsACString& aNodeId,
-                               nsAutoPtr<InitData>&& aData);
-
-  // GMP thread only.
-  void gmp_Shutdown();
+  virtual  CDMCaps& Capabilites() = 0;
 
   // Main thread only.
-  void OnCDMCreated(uint32_t aPromiseId);
-
-  struct CreateSessionData {
-    dom::SessionType mSessionType;
-    uint32_t mCreateSessionToken;
-    PromiseId mPromiseId;
-    nsCString mInitDataType;
-    nsTArray<uint8_t> mInitData;
-  };
-  // GMP thread only.
-  void gmp_CreateSession(nsAutoPtr<CreateSessionData> aData);
-
-  struct SessionOpData {
-    PromiseId mPromiseId;
-    nsCString mSessionId;
-  };
-  // GMP thread only.
-  void gmp_LoadSession(nsAutoPtr<SessionOpData> aData);
+  virtual void OnKeyStatusesChange(const nsAString& aSessionId) = 0;
 
-  struct SetServerCertificateData {
-    PromiseId mPromiseId;
-    nsTArray<uint8_t> mCert;
-  };
-  // GMP thread only.
-  void gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData);
-
-  struct UpdateSessionData {
-    PromiseId mPromiseId;
-    nsCString mSessionId;
-    nsTArray<uint8_t> mResponse;
-  };
-  // GMP thread only.
-  void gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData);
-
-  // GMP thread only.
-  void gmp_CloseSession(nsAutoPtr<SessionOpData> aData);
-
-  // GMP thread only.
-  void gmp_RemoveSession(nsAutoPtr<SessionOpData> aData);
-
-  class DecryptJob {
-  public:
-    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecryptJob)
+  virtual void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
+                                     nsTArray<nsCString>& aSessionIds) = 0;
 
-    explicit DecryptJob(MediaRawData* aSample)
-      : mId(0)
-      , mSample(aSample)
-    {
-    }
-
-    void PostResult(GMPErr aResult, const nsTArray<uint8_t>& aDecryptedData);
-    void PostResult(GMPErr aResult);
-
-    RefPtr<DecryptPromise> Ensure() {
-      return mPromise.Ensure(__func__);
-    }
-
-    uint32_t mId;
-    RefPtr<MediaRawData> mSample;
-  private:
-    ~DecryptJob() {}
-    MozPromiseHolder<DecryptPromise> mPromise;
-  };
-  // GMP thread only.
-  void gmp_Decrypt(RefPtr<DecryptJob> aJob);
+#ifdef DEBUG
+  virtual bool IsOnOwnerThread() = 0;
+#endif
 
-  class RejectPromiseTask : public Runnable {
-  public:
-    RejectPromiseTask(CDMProxy* aProxy,
-                      PromiseId aId,
-                      nsresult aCode,
-                      const nsCString& aReason)
-      : mProxy(aProxy)
-      , mId(aId)
-      , mCode(aCode)
-      , mReason(aReason)
-    {
-    }
-    NS_METHOD Run() {
-      mProxy->RejectPromise(mId, mCode, mReason);
-      return NS_OK;
-    }
-  private:
-    RefPtr<CDMProxy> mProxy;
-    PromiseId mId;
-    nsresult mCode;
-    nsCString mReason;
-  };
-
-  ~CDMProxy();
+protected:
+  virtual ~CDMProxy() {}
 
   // Helper to enforce that a raw pointer is only accessed on the main thread.
   template<class Type>
   class MainThreadOnlyRawPtr {
   public:
     explicit MainThreadOnlyRawPtr(Type* aPtr)
       : mPtr(aPtr)
     {
@@ -325,38 +217,21 @@ private:
 
   // Our reference back to the MediaKeys object.
   // WARNING: This is a non-owning reference that is cleared by MediaKeys
   // destructor. only use on main thread, and always nullcheck before using!
   MainThreadOnlyRawPtr<dom::MediaKeys> mKeys;
 
   const nsString mKeySystem;
 
-  // Gecko Media Plugin thread. All interactions with the out-of-process
-  // EME plugin must come from this thread.
-  RefPtr<nsIThread> mGMPThread;
+  // Onwer specified thread. e.g. Gecko Media Plugin thread.
+  // All interactions with the out-of-process EME plugin must come from this thread.
+  RefPtr<nsIThread> mOwnerThread;
 
   nsCString mNodeId;
 
-  GMPDecryptorProxy* mCDM;
   CDMCaps mCapabilites;
-  nsAutoPtr<GMPCDMCallbackProxy> mCallback;
-
-  // Decryption jobs sent to CDM, awaiting result.
-  // GMP thread only.
-  nsTArray<RefPtr<DecryptJob>> mDecryptionJobs;
-
-  // Number of buffers we've decrypted. Used to uniquely identify
-  // decryption jobs sent to CDM. Note we can't just use the length of
-  // mDecryptionJobs as that shrinks as jobs are completed and removed
-  // from it.
-  // GMP thread only.
-  uint32_t mDecryptionJobCount;
-
-  // True if CDMProxy::gmp_Shutdown was called.
-  // GMP thread only.
-  bool mShutdownCalled;
 };
 
 
 } // namespace mozilla
 
 #endif // CDMProxy_h_
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -8,17 +8,17 @@
 #include "GMPService.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/MediaKeysBinding.h"
 #include "mozilla/dom/MediaKeyMessageEvent.h"
 #include "mozilla/dom/MediaKeyError.h"
 #include "mozilla/dom/MediaKeySession.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/UnionTypes.h"
-#include "mozilla/CDMProxy.h"
+#include "GMPCDMProxy.h"
 #include "mozilla/EMEUtils.h"
 #include "nsContentUtils.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsContentTypeParser.h"
 #ifdef MOZ_FMP4
 #include "MP4Decoder.h"
 #endif
 #ifdef XP_WIN
@@ -308,17 +308,17 @@ already_AddRefed<DetailedPromise>
 MediaKeys::Init(ErrorResult& aRv)
 {
   RefPtr<DetailedPromise> promise(MakePromise(aRv,
     NS_LITERAL_CSTRING("MediaKeys::Init()")));
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  mProxy = new CDMProxy(this, mKeySystem);
+  mProxy = new GMPCDMProxy(this, mKeySystem, new MediaKeysGMPCrashHelper(this));
 
   // Determine principal (at creation time) of the MediaKeys object.
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetParentObject());
   if (!sop) {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
                          NS_LITERAL_CSTRING("Couldn't get script principal in MediaKeys::Init"));
     return promise.forget();
   }
@@ -382,18 +382,17 @@ MediaKeys::Init(ErrorResult& aRv)
   // rejected.
   MOZ_ASSERT(!mCreatePromiseId, "Should only be created once!");
   mCreatePromiseId = StorePromise(promise);
   AddRef();
   mProxy->Init(mCreatePromiseId,
                origin,
                topLevelOrigin,
                KeySystemToGMPName(mKeySystem),
-               inPrivateBrowsing,
-               new MediaKeysGMPCrashHelper(this));
+               inPrivateBrowsing);
 
   return promise.forget();
 }
 
 void
 MediaKeys::OnCDMCreated(PromiseId aId, const nsACString& aNodeId, const uint32_t aPluginId)
 {
   RefPtr<DetailedPromise> promise(RetrievePromise(aId));
--- a/dom/media/eme/moz.build
+++ b/dom/media/eme/moz.build
@@ -19,17 +19,16 @@ EXPORTS.mozilla += [
     'CDMCaps.h',
     'CDMProxy.h',
     'DetailedPromise.h',
     'EMEUtils.h',
 ]
 
 UNIFIED_SOURCES += [
     'CDMCaps.cpp',
-    'CDMProxy.cpp',
     'DetailedPromise.cpp',
     'EMEUtils.cpp',
     'MediaEncryptedEvent.cpp',
     'MediaKeyError.cpp',
     'MediaKeyMessageEvent.cpp',
     'MediaKeys.cpp',
     'MediaKeySession.cpp',
     'MediaKeyStatusMap.cpp',
--- a/dom/media/gmp/GMPCDMCallbackProxy.cpp
+++ b/dom/media/gmp/GMPCDMCallbackProxy.cpp
@@ -43,17 +43,17 @@ public:
   uint32_t mToken;
   nsString mSid;
 };
 
 void
 GMPCDMCallbackProxy::SetSessionId(uint32_t aToken,
                                   const nsCString& aSessionId)
 {
-  MOZ_ASSERT(mProxy->IsOnGMPThread());
+  MOZ_ASSERT(mProxy->IsOnOwnerThread());
 
   nsCOMPtr<nsIRunnable> task(new SetSessionIdTask(mProxy,
                                                   aToken,
                                                   aSessionId));
   NS_DispatchToMainThread(task);
 }
 
 class LoadSessionTask : public Runnable {
@@ -76,28 +76,28 @@ public:
   dom::PromiseId mPid;
   bool mSuccess;
 };
 
 void
 GMPCDMCallbackProxy::ResolveLoadSessionPromise(uint32_t aPromiseId,
                                                bool aSuccess)
 {
-  MOZ_ASSERT(mProxy->IsOnGMPThread());
+  MOZ_ASSERT(mProxy->IsOnOwnerThread());
 
   nsCOMPtr<nsIRunnable> task(new LoadSessionTask(mProxy,
                                                  aPromiseId,
                                                  aSuccess));
   NS_DispatchToMainThread(task);
 }
 
 void
 GMPCDMCallbackProxy::ResolvePromise(uint32_t aPromiseId)
 {
-  MOZ_ASSERT(mProxy->IsOnGMPThread());
+  MOZ_ASSERT(mProxy->IsOnOwnerThread());
 
   // Note: CDMProxy proxies this from non-main threads to main thread.
   mProxy->ResolvePromise(aPromiseId);
 }
 
 class RejectPromiseTask : public Runnable {
 public:
   RejectPromiseTask(CDMProxy* aProxy,
@@ -123,57 +123,68 @@ public:
 };
 
 
 void
 GMPCDMCallbackProxy::RejectPromise(uint32_t aPromiseId,
                                    nsresult aException,
                                    const nsCString& aMessage)
 {
-  MOZ_ASSERT(mProxy->IsOnGMPThread());
+  MOZ_ASSERT(mProxy->IsOnOwnerThread());
 
   nsCOMPtr<nsIRunnable> task;
   task = new RejectPromiseTask(mProxy,
                                aPromiseId,
                                aException,
                                aMessage);
   NS_DispatchToMainThread(task);
 }
 
+static dom::MediaKeyMessageType
+ToMediaKeyMessageType(GMPSessionMessageType aMessageType) {
+  switch (aMessageType) {
+    case kGMPLicenseRequest: return dom::MediaKeyMessageType::License_request;
+    case kGMPLicenseRenewal: return dom::MediaKeyMessageType::License_renewal;
+    case kGMPLicenseRelease: return dom::MediaKeyMessageType::License_release;
+    case kGMPIndividualizationRequest: return dom::MediaKeyMessageType::Individualization_request;
+    default: return dom::MediaKeyMessageType::License_request;
+  };
+};
+
 class SessionMessageTask : public Runnable {
 public:
   SessionMessageTask(CDMProxy* aProxy,
                      const nsCString& aSessionId,
                      GMPSessionMessageType aMessageType,
                      const nsTArray<uint8_t>& aMessage)
     : mProxy(aProxy)
     , mSid(NS_ConvertUTF8toUTF16(aSessionId))
     , mMsgType(aMessageType)
   {
     mMsg.AppendElements(aMessage);
   }
 
   NS_IMETHOD Run() {
-    mProxy->OnSessionMessage(mSid, mMsgType, mMsg);
+    mProxy->OnSessionMessage(mSid, ToMediaKeyMessageType(mMsgType), mMsg);
     return NS_OK;
   }
 
   RefPtr<CDMProxy> mProxy;
   dom::PromiseId mPid;
   nsString mSid;
   GMPSessionMessageType mMsgType;
   nsTArray<uint8_t> mMsg;
 };
 
 void
 GMPCDMCallbackProxy::SessionMessage(const nsCString& aSessionId,
                                     GMPSessionMessageType aMessageType,
                                     const nsTArray<uint8_t>& aMessage)
 {
-  MOZ_ASSERT(mProxy->IsOnGMPThread());
+  MOZ_ASSERT(mProxy->IsOnOwnerThread());
 
   nsCOMPtr<nsIRunnable> task;
   task = new SessionMessageTask(mProxy,
                                 aSessionId,
                                 aMessageType,
                                 aMessage);
   NS_DispatchToMainThread(task);
 }
@@ -197,29 +208,29 @@ public:
   nsString mSid;
   GMPTimestamp mTimestamp;
 };
 
 void
 GMPCDMCallbackProxy::ExpirationChange(const nsCString& aSessionId,
                                       GMPTimestamp aExpiryTime)
 {
-  MOZ_ASSERT(mProxy->IsOnGMPThread());
+  MOZ_ASSERT(mProxy->IsOnOwnerThread());
 
   nsCOMPtr<nsIRunnable> task;
   task = new ExpirationChangeTask(mProxy,
                                   aSessionId,
                                   aExpiryTime);
   NS_DispatchToMainThread(task);
 }
 
 void
 GMPCDMCallbackProxy::SessionClosed(const nsCString& aSessionId)
 {
-  MOZ_ASSERT(mProxy->IsOnGMPThread());
+  MOZ_ASSERT(mProxy->IsOnOwnerThread());
 
   bool keyStatusesChange = false;
   {
     CDMCaps::AutoLock caps(mProxy->Capabilites());
     keyStatusesChange = caps.RemoveKeysForSession(NS_ConvertUTF8toUTF16(aSessionId));
   }
   if (keyStatusesChange) {
     nsCOMPtr<nsIRunnable> task;
@@ -264,33 +275,33 @@ public:
 };
 
 void
 GMPCDMCallbackProxy::SessionError(const nsCString& aSessionId,
                                   nsresult aException,
                                   uint32_t aSystemCode,
                                   const nsCString& aMessage)
 {
-  MOZ_ASSERT(mProxy->IsOnGMPThread());
+  MOZ_ASSERT(mProxy->IsOnOwnerThread());
 
   nsCOMPtr<nsIRunnable> task;
   task = new SessionErrorTask(mProxy,
                               aSessionId,
                               aException,
                               aSystemCode,
                               aMessage);
   NS_DispatchToMainThread(task);
 }
 
 void
 GMPCDMCallbackProxy::KeyStatusChanged(const nsCString& aSessionId,
                                       const nsTArray<uint8_t>& aKeyId,
                                       GMPMediaKeyStatus aStatus)
 {
-  MOZ_ASSERT(mProxy->IsOnGMPThread());
+  MOZ_ASSERT(mProxy->IsOnOwnerThread());
 
   bool keyStatusesChange = false;
   {
     CDMCaps::AutoLock caps(mProxy->Capabilites());
     keyStatusesChange = caps.SetKeyStatus(aKeyId,
                                           NS_ConvertUTF8toUTF16(aSessionId),
                                           aStatus);
   }
@@ -298,27 +309,38 @@ GMPCDMCallbackProxy::KeyStatusChanged(co
     nsCOMPtr<nsIRunnable> task;
     task = NewRunnableMethod<nsString>(mProxy,
                                        &CDMProxy::OnKeyStatusesChange,
                                        NS_ConvertUTF8toUTF16(aSessionId));
     NS_DispatchToMainThread(task);
   }
 }
 
+DecryptStatus
+ToDecryptStatus(GMPErr aError)
+{
+  switch (aError) {
+    case GMPNoErr: return Ok;
+    case GMPNoKeyErr: return NoKeyErr;
+    case GMPAbortedErr: return AbortedErr;
+    default: return GenericErr;
+  }
+}
+
 void
 GMPCDMCallbackProxy::Decrypted(uint32_t aId,
                                GMPErr aResult,
                                const nsTArray<uint8_t>& aDecryptedData)
 {
-  MOZ_ASSERT(mProxy->IsOnGMPThread());
+  MOZ_ASSERT(mProxy->IsOnOwnerThread());
 
-  mProxy->gmp_Decrypted(aId, aResult, aDecryptedData);
+  mProxy->OnDecrypted(aId, ToDecryptStatus(aResult), aDecryptedData);
 }
 
 void
 GMPCDMCallbackProxy::Terminated()
 {
-  MOZ_ASSERT(mProxy->IsOnGMPThread());
+  MOZ_ASSERT(mProxy->IsOnOwnerThread());
   nsCOMPtr<nsIRunnable> task = NewRunnableMethod(mProxy, &CDMProxy::Terminated);
   NS_DispatchToMainThread(task);
 }
 
 } // namespace mozilla
--- a/dom/media/gmp/GMPCDMCallbackProxy.h
+++ b/dom/media/gmp/GMPCDMCallbackProxy.h
@@ -51,17 +51,17 @@ public:
                  GMPErr aResult,
                  const nsTArray<uint8_t>& aDecryptedData) override;
 
   void Terminated() override;
 
   ~GMPCDMCallbackProxy() {}
 
 private:
-  friend class CDMProxy;
+  friend class GMPCDMProxy;
   explicit GMPCDMCallbackProxy(CDMProxy* aProxy);
 
   // Warning: Weak ref.
   CDMProxy* mProxy;
 };
 
 } // namespace mozilla
 
rename from dom/media/eme/CDMProxy.cpp
rename to dom/media/gmp/GMPCDMProxy.cpp
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/gmp/GMPCDMProxy.cpp
@@ -1,188 +1,191 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "mozilla/CDMProxy.h"
-#include "nsString.h"
+#include "GMPCDMProxy.h"
+#include "mozilla/EMEUtils.h"
+#include "mozilla/PodOperations.h"
+
 #include "mozilla/dom/MediaKeys.h"
 #include "mozilla/dom/MediaKeySession.h"
+
 #include "mozIGeckoMediaPluginService.h"
 #include "nsContentCID.h"
+#include "nsIConsoleService.h"
+#include "nsPrintfCString.h"
 #include "nsServiceManagerUtils.h"
-#include "MainThreadUtils.h"
-#include "mozilla/EMEUtils.h"
-#include "nsIConsoleService.h"
+#include "nsString.h"
 #include "prenv.h"
-#include "mozilla/PodOperations.h"
 #include "GMPCDMCallbackProxy.h"
+#include "GMPService.h"
+#include "MainThreadUtils.h"
 #include "MediaData.h"
-#include "nsPrintfCString.h"
-#include "GMPService.h"
 
 namespace mozilla {
 
-CDMProxy::CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem)
-  : mKeys(aKeys)
-  , mKeySystem(aKeySystem)
+GMPCDMProxy::GMPCDMProxy(dom::MediaKeys* aKeys,
+                         const nsAString& aKeySystem,
+                         GMPCrashHelper* aCrashHelper)
+  : CDMProxy(aKeys, aKeySystem)
+  , mCrashHelper(aCrashHelper)
   , mCDM(nullptr)
   , mDecryptionJobCount(0)
   , mShutdownCalled(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_COUNT_CTOR(CDMProxy);
+  MOZ_COUNT_CTOR(GMPCDMProxy);
 }
 
-CDMProxy::~CDMProxy()
+GMPCDMProxy::~GMPCDMProxy()
 {
-  MOZ_COUNT_DTOR(CDMProxy);
+  MOZ_COUNT_DTOR(GMPCDMProxy);
 }
 
 void
-CDMProxy::Init(PromiseId aPromiseId,
-               const nsAString& aOrigin,
-               const nsAString& aTopLevelOrigin,
-               const nsAString& aGMPName,
-               bool aInPrivateBrowsing,
-               GMPCrashHelper* aCrashHelper)
+GMPCDMProxy::Init(PromiseId aPromiseId,
+                  const nsAString& aOrigin,
+                  const nsAString& aTopLevelOrigin,
+                  const nsAString& aGMPName,
+                  bool aInPrivateBrowsing)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
 
-  EME_LOG("CDMProxy::Init (%s, %s) %s",
+  EME_LOG("GMPCDMProxy::Init (%s, %s) %s",
           NS_ConvertUTF16toUTF8(aOrigin).get(),
           NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
           (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));
 
   nsCString pluginVersion;
-  if (!mGMPThread) {
+  if (!mOwnerThread) {
     nsCOMPtr<mozIGeckoMediaPluginService> mps =
       do_GetService("@mozilla.org/gecko-media-plugin-service;1");
     if (!mps) {
       RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
-                    NS_LITERAL_CSTRING("Couldn't get MediaPluginService in CDMProxy::Init"));
+                    NS_LITERAL_CSTRING("Couldn't get MediaPluginService in GMPCDMProxy::Init"));
       return;
     }
-    mps->GetThread(getter_AddRefs(mGMPThread));
-    if (!mGMPThread) {
+    mps->GetThread(getter_AddRefs(mOwnerThread));
+    if (!mOwnerThread) {
       RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
-                    NS_LITERAL_CSTRING("Couldn't get GMP thread CDMProxy::Init"));
+                    NS_LITERAL_CSTRING("Couldn't get GMP thread GMPCDMProxy::Init"));
       return;
     }
   }
 
   if (aGMPName.IsEmpty()) {
     RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
       nsPrintfCString("Unknown GMP for keysystem '%s'", NS_ConvertUTF16toUTF8(mKeySystem).get()));
     return;
   }
 
   nsAutoPtr<InitData> data(new InitData());
   data->mPromiseId = aPromiseId;
   data->mOrigin = aOrigin;
   data->mTopLevelOrigin = aTopLevelOrigin;
   data->mGMPName = aGMPName;
   data->mInPrivateBrowsing = aInPrivateBrowsing;
-  data->mCrashHelper = aCrashHelper;
+  data->mCrashHelper = mCrashHelper;
   nsCOMPtr<nsIRunnable> task(
     NewRunnableMethod<nsAutoPtr<InitData>>(this,
-                                           &CDMProxy::gmp_Init,
+                                           &GMPCDMProxy::gmp_Init,
                                            Move(data)));
-  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+  mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 #ifdef DEBUG
 bool
-CDMProxy::IsOnGMPThread()
+GMPCDMProxy::IsOnOwnerThread()
 {
-  return NS_GetCurrentThread() == mGMPThread;
+  return NS_GetCurrentThread() == mOwnerThread;
 }
 #endif
 
 void
-CDMProxy::gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData)
+GMPCDMProxy::gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData)
 {
-  EME_LOG("CDMProxy::gmp_InitDone");
+  EME_LOG("GMPCDMProxy::gmp_InitDone");
   if (mShutdownCalled) {
     if (aCDM) {
       aCDM->Close();
     }
     RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
-                  NS_LITERAL_CSTRING("CDMProxy was shut down before init could complete"));
+                  NS_LITERAL_CSTRING("GMPCDMProxy was shut down before init could complete"));
     return;
   }
   if (!aCDM) {
     RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("GetGMPDecryptor failed to return a CDM"));
     return;
   }
 
   mCDM = aCDM;
   mCallback = new GMPCDMCallbackProxy(this);
   mCDM->Init(mCallback);
   nsCOMPtr<nsIRunnable> task(
     NewRunnableMethod<uint32_t>(this,
-                                &CDMProxy::OnCDMCreated,
+                                &GMPCDMProxy::OnCDMCreated,
                                 aData->mPromiseId));
   NS_DispatchToMainThread(task);
 }
 
 class gmp_InitDoneCallback : public GetGMPDecryptorCallback
 {
 public:
-  gmp_InitDoneCallback(CDMProxy* aCDMProxy,
-                       nsAutoPtr<CDMProxy::InitData>&& aData)
-    : mCDMProxy(aCDMProxy),
+  gmp_InitDoneCallback(GMPCDMProxy* aGMPCDMProxy,
+                       nsAutoPtr<GMPCDMProxy::InitData>&& aData)
+    : mGMPCDMProxy(aGMPCDMProxy),
       mData(Move(aData))
   {
   }
 
   void Done(GMPDecryptorProxy* aCDM)
   {
-    mCDMProxy->gmp_InitDone(aCDM, Move(mData));
+    mGMPCDMProxy->gmp_InitDone(aCDM, Move(mData));
   }
 
 private:
-  RefPtr<CDMProxy> mCDMProxy;
-  nsAutoPtr<CDMProxy::InitData> mData;
+  RefPtr<GMPCDMProxy> mGMPCDMProxy;
+  nsAutoPtr<GMPCDMProxy::InitData> mData;
 };
 
 class gmp_InitGetGMPDecryptorCallback : public GetNodeIdCallback
 {
 public:
-  gmp_InitGetGMPDecryptorCallback(CDMProxy* aCDMProxy,
-                                  nsAutoPtr<CDMProxy::InitData>&& aData)
-    : mCDMProxy(aCDMProxy),
+  gmp_InitGetGMPDecryptorCallback(GMPCDMProxy* aGMPCDMProxy,
+                                  nsAutoPtr<GMPCDMProxy::InitData>&& aData)
+    : mGMPCDMProxy(aGMPCDMProxy),
       mData(aData)
   {
   }
 
   void Done(nsresult aResult, const nsACString& aNodeId)
   {
-    mCDMProxy->gmp_InitGetGMPDecryptor(aResult, aNodeId, Move(mData));
+    mGMPCDMProxy->gmp_InitGetGMPDecryptor(aResult, aNodeId, Move(mData));
   }
 
 private:
-  RefPtr<CDMProxy> mCDMProxy;
-  nsAutoPtr<CDMProxy::InitData> mData;
+  RefPtr<GMPCDMProxy> mGMPCDMProxy;
+  nsAutoPtr<GMPCDMProxy::InitData> mData;
 };
 
 void
-CDMProxy::gmp_Init(nsAutoPtr<InitData>&& aData)
+GMPCDMProxy::gmp_Init(nsAutoPtr<InitData>&& aData)
 {
-  MOZ_ASSERT(IsOnGMPThread());
+  MOZ_ASSERT(IsOnOwnerThread());
 
   nsCOMPtr<mozIGeckoMediaPluginService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (!mps) {
     RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
-                  NS_LITERAL_CSTRING("Couldn't get MediaPluginService in CDMProxy::gmp_Init"));
+                  NS_LITERAL_CSTRING("Couldn't get MediaPluginService in GMPCDMProxy::gmp_Init"));
     return;
   }
 
   // Make a copy before we transfer ownership of aData to the
   // gmp_InitGetGMPDecryptorCallback.
   InitData data(*aData);
   UniquePtr<GetNodeIdCallback> callback(
     new gmp_InitGetGMPDecryptorCallback(this, Move(aData)));
@@ -193,39 +196,39 @@ CDMProxy::gmp_Init(nsAutoPtr<InitData>&&
                                Move(callback));
   if (NS_FAILED(rv)) {
     RejectPromise(data.mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Call to GetNodeId() failed early"));
   }
 }
 
 void
-CDMProxy::gmp_InitGetGMPDecryptor(nsresult aResult,
-                                  const nsACString& aNodeId,
-                                  nsAutoPtr<InitData>&& aData)
+GMPCDMProxy::gmp_InitGetGMPDecryptor(nsresult aResult,
+                                     const nsACString& aNodeId,
+                                     nsAutoPtr<InitData>&& aData)
 {
   uint32_t promiseID = aData->mPromiseId;
   if (NS_FAILED(aResult)) {
     RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("GetNodeId() called back, but with a failure result"));
     return;
   }
 
   mNodeId = aNodeId;
   MOZ_ASSERT(!GetNodeId().IsEmpty());
 
   nsCOMPtr<mozIGeckoMediaPluginService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (!mps) {
     RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
-                  NS_LITERAL_CSTRING("Couldn't get MediaPluginService in CDMProxy::gmp_InitGetGMPDecryptor"));
+                  NS_LITERAL_CSTRING("Couldn't get MediaPluginService in GMPCDMProxy::gmp_InitGetGMPDecryptor"));
     return;
   }
 
-  EME_LOG("CDMProxy::gmp_Init (%s, %s) %s NodeId=%s",
+  EME_LOG("GMPCDMProxy::gmp_Init (%s, %s) %s NodeId=%s",
           NS_ConvertUTF16toUTF8(aData->mOrigin).get(),
           NS_ConvertUTF16toUTF8(aData->mTopLevelOrigin).get(),
           (aData->mInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"),
           GetNodeId().get());
 
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
 
@@ -237,467 +240,465 @@ CDMProxy::gmp_InitGetGMPDecryptor(nsresu
   nsresult rv = mps->GetGMPDecryptor(crashHelper, &tags, GetNodeId(), Move(callback));
   if (NS_FAILED(rv)) {
     RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Call to GetGMPDecryptor() failed early"));
   }
 }
 
 void
-CDMProxy::OnCDMCreated(uint32_t aPromiseId)
+GMPCDMProxy::OnCDMCreated(uint32_t aPromiseId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   MOZ_ASSERT(!GetNodeId().IsEmpty());
   if (mCDM) {
     mKeys->OnCDMCreated(aPromiseId, GetNodeId(), mCDM->GetPluginId());
   } else {
     // No CDM? Just reject the promise.
     mKeys->RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                          NS_LITERAL_CSTRING("Null CDM in OnCDMCreated()"));
   }
 }
 
 void
-CDMProxy::CreateSession(uint32_t aCreateSessionToken,
-                        dom::SessionType aSessionType,
-                        PromiseId aPromiseId,
-                        const nsAString& aInitDataType,
-                        nsTArray<uint8_t>& aInitData)
+GMPCDMProxy::CreateSession(uint32_t aCreateSessionToken,
+                           dom::SessionType aSessionType,
+                           PromiseId aPromiseId,
+                           const nsAString& aInitDataType,
+                           nsTArray<uint8_t>& aInitData)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mGMPThread);
+  MOZ_ASSERT(mOwnerThread);
 
   nsAutoPtr<CreateSessionData> data(new CreateSessionData());
   data->mSessionType = aSessionType;
   data->mCreateSessionToken = aCreateSessionToken;
   data->mPromiseId = aPromiseId;
   data->mInitDataType = NS_ConvertUTF16toUTF8(aInitDataType);
   data->mInitData = Move(aInitData);
 
   nsCOMPtr<nsIRunnable> task(
-    NewRunnableMethod<nsAutoPtr<CreateSessionData>>(this, &CDMProxy::gmp_CreateSession, data));
-  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+    NewRunnableMethod<nsAutoPtr<CreateSessionData>>(this, &GMPCDMProxy::gmp_CreateSession, data));
+  mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 GMPSessionType
 ToGMPSessionType(dom::SessionType aSessionType) {
   switch (aSessionType) {
     case dom::SessionType::Temporary: return kGMPTemporySession;
     case dom::SessionType::Persistent: return kGMPPersistentSession;
     default: return kGMPTemporySession;
   };
 };
 
 void
-CDMProxy::gmp_CreateSession(nsAutoPtr<CreateSessionData> aData)
+GMPCDMProxy::gmp_CreateSession(nsAutoPtr<CreateSessionData> aData)
 {
-  MOZ_ASSERT(IsOnGMPThread());
+  MOZ_ASSERT(IsOnOwnerThread());
   if (!mCDM) {
     RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Null CDM in gmp_CreateSession"));
     return;
   }
   mCDM->CreateSession(aData->mCreateSessionToken,
                       aData->mPromiseId,
                       aData->mInitDataType,
                       aData->mInitData,
                       ToGMPSessionType(aData->mSessionType));
 }
 
 void
-CDMProxy::LoadSession(PromiseId aPromiseId,
-                      const nsAString& aSessionId)
+GMPCDMProxy::LoadSession(PromiseId aPromiseId,
+                         const nsAString& aSessionId)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mGMPThread);
+  MOZ_ASSERT(mOwnerThread);
 
   nsAutoPtr<SessionOpData> data(new SessionOpData());
   data->mPromiseId = aPromiseId;
   data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
   nsCOMPtr<nsIRunnable> task(
-    NewRunnableMethod<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_LoadSession, data));
-  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+    NewRunnableMethod<nsAutoPtr<SessionOpData>>(this, &GMPCDMProxy::gmp_LoadSession, data));
+  mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 void
-CDMProxy::gmp_LoadSession(nsAutoPtr<SessionOpData> aData)
+GMPCDMProxy::gmp_LoadSession(nsAutoPtr<SessionOpData> aData)
 {
-  MOZ_ASSERT(IsOnGMPThread());
+  MOZ_ASSERT(IsOnOwnerThread());
 
   if (!mCDM) {
     RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Null CDM in gmp_LoadSession"));
     return;
   }
   mCDM->LoadSession(aData->mPromiseId, aData->mSessionId);
 }
 
 void
-CDMProxy::SetServerCertificate(PromiseId aPromiseId,
-                               nsTArray<uint8_t>& aCert)
+GMPCDMProxy::SetServerCertificate(PromiseId aPromiseId,
+                                  nsTArray<uint8_t>& aCert)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mGMPThread);
+  MOZ_ASSERT(mOwnerThread);
 
   nsAutoPtr<SetServerCertificateData> data(new SetServerCertificateData());
   data->mPromiseId = aPromiseId;
   data->mCert = Move(aCert);
   nsCOMPtr<nsIRunnable> task(
-    NewRunnableMethod<nsAutoPtr<SetServerCertificateData>>(this, &CDMProxy::gmp_SetServerCertificate, data));
-  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+    NewRunnableMethod<nsAutoPtr<SetServerCertificateData>>(this, &GMPCDMProxy::gmp_SetServerCertificate, data));
+  mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 void
-CDMProxy::gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData)
+GMPCDMProxy::gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData)
 {
-  MOZ_ASSERT(IsOnGMPThread());
+  MOZ_ASSERT(IsOnOwnerThread());
   if (!mCDM) {
     RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Null CDM in gmp_SetServerCertificate"));
     return;
   }
   mCDM->SetServerCertificate(aData->mPromiseId, aData->mCert);
 }
 
 void
-CDMProxy::UpdateSession(const nsAString& aSessionId,
-                        PromiseId aPromiseId,
-                        nsTArray<uint8_t>& aResponse)
+GMPCDMProxy::UpdateSession(const nsAString& aSessionId,
+                           PromiseId aPromiseId,
+                           nsTArray<uint8_t>& aResponse)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mGMPThread);
+  MOZ_ASSERT(mOwnerThread);
   NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
 
   nsAutoPtr<UpdateSessionData> data(new UpdateSessionData());
   data->mPromiseId = aPromiseId;
   data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
   data->mResponse = Move(aResponse);
   nsCOMPtr<nsIRunnable> task(
-    NewRunnableMethod<nsAutoPtr<UpdateSessionData>>(this, &CDMProxy::gmp_UpdateSession, data));
-  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+    NewRunnableMethod<nsAutoPtr<UpdateSessionData>>(this, &GMPCDMProxy::gmp_UpdateSession, data));
+  mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 void
-CDMProxy::gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData)
+GMPCDMProxy::gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData)
 {
-  MOZ_ASSERT(IsOnGMPThread());
+  MOZ_ASSERT(IsOnOwnerThread());
   if (!mCDM) {
     RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Null CDM in gmp_UpdateSession"));
     return;
   }
   mCDM->UpdateSession(aData->mPromiseId,
                       aData->mSessionId,
                       aData->mResponse);
 }
 
 void
-CDMProxy::CloseSession(const nsAString& aSessionId,
-                       PromiseId aPromiseId)
+GMPCDMProxy::CloseSession(const nsAString& aSessionId,
+                          PromiseId aPromiseId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
 
   nsAutoPtr<SessionOpData> data(new SessionOpData());
   data->mPromiseId = aPromiseId;
   data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
   nsCOMPtr<nsIRunnable> task(
-    NewRunnableMethod<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_CloseSession, data));
-  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+    NewRunnableMethod<nsAutoPtr<SessionOpData>>(this, &GMPCDMProxy::gmp_CloseSession, data));
+  mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 void
-CDMProxy::gmp_CloseSession(nsAutoPtr<SessionOpData> aData)
+GMPCDMProxy::gmp_CloseSession(nsAutoPtr<SessionOpData> aData)
 {
-  MOZ_ASSERT(IsOnGMPThread());
+  MOZ_ASSERT(IsOnOwnerThread());
   if (!mCDM) {
     RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Null CDM in gmp_CloseSession"));
     return;
   }
   mCDM->CloseSession(aData->mPromiseId, aData->mSessionId);
 }
 
 void
-CDMProxy::RemoveSession(const nsAString& aSessionId,
-                        PromiseId aPromiseId)
+GMPCDMProxy::RemoveSession(const nsAString& aSessionId,
+                           PromiseId aPromiseId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
 
   nsAutoPtr<SessionOpData> data(new SessionOpData());
   data->mPromiseId = aPromiseId;
   data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
   nsCOMPtr<nsIRunnable> task(
-    NewRunnableMethod<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_RemoveSession, data));
-  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+    NewRunnableMethod<nsAutoPtr<SessionOpData>>(this, &GMPCDMProxy::gmp_RemoveSession, data));
+  mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 void
-CDMProxy::gmp_RemoveSession(nsAutoPtr<SessionOpData> aData)
+GMPCDMProxy::gmp_RemoveSession(nsAutoPtr<SessionOpData> aData)
 {
-  MOZ_ASSERT(IsOnGMPThread());
+  MOZ_ASSERT(IsOnOwnerThread());
   if (!mCDM) {
     RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Null CDM in gmp_RemoveSession"));
     return;
   }
   mCDM->RemoveSession(aData->mPromiseId, aData->mSessionId);
 }
 
 void
-CDMProxy::Shutdown()
+GMPCDMProxy::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mKeys.Clear();
-  // Note: This may end up being the last owning reference to the CDMProxy.
-  nsCOMPtr<nsIRunnable> task(NewRunnableMethod(this, &CDMProxy::gmp_Shutdown));
-  if (mGMPThread) {
-    mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+  // Note: This may end up being the last owning reference to the GMPCDMProxy.
+  nsCOMPtr<nsIRunnable> task(NewRunnableMethod(this, &GMPCDMProxy::gmp_Shutdown));
+  if (mOwnerThread) {
+    mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
   }
 }
 
 void
-CDMProxy::gmp_Shutdown()
+GMPCDMProxy::gmp_Shutdown()
 {
-  MOZ_ASSERT(IsOnGMPThread());
+  MOZ_ASSERT(IsOnOwnerThread());
 
   mShutdownCalled = true;
 
   // Abort any pending decrypt jobs, to awaken any clients waiting on a job.
   for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
     DecryptJob* job = mDecryptionJobs[i];
-    job->PostResult(GMPAbortedErr);
+    job->PostResult(AbortedErr);
   }
   mDecryptionJobs.Clear();
 
   if (mCDM) {
     mCDM->Close();
     mCDM = nullptr;
   }
 }
 
 void
-CDMProxy::RejectPromise(PromiseId aId, nsresult aCode,
-                        const nsCString& aReason)
+GMPCDMProxy::RejectPromise(PromiseId aId, nsresult aCode,
+                           const nsCString& aReason)
 {
   if (NS_IsMainThread()) {
     if (!mKeys.IsNull()) {
       mKeys->RejectPromise(aId, aCode, aReason);
     }
   } else {
     nsCOMPtr<nsIRunnable> task(new RejectPromiseTask(this, aId, aCode,
                                                      aReason));
     NS_DispatchToMainThread(task);
   }
 }
 
 void
-CDMProxy::ResolvePromise(PromiseId aId)
+GMPCDMProxy::ResolvePromise(PromiseId aId)
 {
   if (NS_IsMainThread()) {
     if (!mKeys.IsNull()) {
       mKeys->ResolvePromise(aId);
     } else {
-      NS_WARNING("CDMProxy unable to resolve promise!");
+      NS_WARNING("GMPCDMProxy unable to resolve promise!");
     }
   } else {
     nsCOMPtr<nsIRunnable> task;
     task = NewRunnableMethod<PromiseId>(this,
-                                        &CDMProxy::ResolvePromise,
+                                        &GMPCDMProxy::ResolvePromise,
                                         aId);
     NS_DispatchToMainThread(task);
   }
 }
 
 const nsCString&
-CDMProxy::GetNodeId() const
+GMPCDMProxy::GetNodeId() const
 {
   return mNodeId;
 }
 
 void
-CDMProxy::OnSetSessionId(uint32_t aCreateSessionToken,
-                         const nsAString& aSessionId)
+GMPCDMProxy::OnSetSessionId(uint32_t aCreateSessionToken,
+                            const nsAString& aSessionId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
 
   RefPtr<dom::MediaKeySession> session(mKeys->GetPendingSession(aCreateSessionToken));
   if (session) {
     session->SetSessionId(aSessionId);
   }
 }
 
 void
-CDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess)
+GMPCDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   mKeys->OnSessionLoaded(aPromiseId, aSuccess);
 }
 
-static dom::MediaKeyMessageType
-ToMediaKeyMessageType(GMPSessionMessageType aMessageType) {
-  switch (aMessageType) {
-    case kGMPLicenseRequest: return dom::MediaKeyMessageType::License_request;
-    case kGMPLicenseRenewal: return dom::MediaKeyMessageType::License_renewal;
-    case kGMPLicenseRelease: return dom::MediaKeyMessageType::License_release;
-    case kGMPIndividualizationRequest: return dom::MediaKeyMessageType::Individualization_request;
-    default: return dom::MediaKeyMessageType::License_request;
-  };
-};
-
 void
-CDMProxy::OnSessionMessage(const nsAString& aSessionId,
-                           GMPSessionMessageType aMessageType,
-                           nsTArray<uint8_t>& aMessage)
+GMPCDMProxy::OnSessionMessage(const nsAString& aSessionId,
+                              dom::MediaKeyMessageType aMessageType,
+                              nsTArray<uint8_t>& aMessage)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
   if (session) {
-    session->DispatchKeyMessage(ToMediaKeyMessageType(aMessageType), aMessage);
+    session->DispatchKeyMessage(aMessageType, aMessage);
   }
 }
 
 void
-CDMProxy::OnKeyStatusesChange(const nsAString& aSessionId)
+GMPCDMProxy::OnKeyStatusesChange(const nsAString& aSessionId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
   if (session) {
     session->DispatchKeyStatusesChange();
   }
 }
 
 void
-CDMProxy::OnExpirationChange(const nsAString& aSessionId,
-                             GMPTimestamp aExpiryTime)
+GMPCDMProxy::OnExpirationChange(const nsAString& aSessionId,
+                                GMPTimestamp aExpiryTime)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
   if (session) {
     session->SetExpiration(static_cast<double>(aExpiryTime));
   }
 }
 
 void
-CDMProxy::OnSessionClosed(const nsAString& aSessionId)
+GMPCDMProxy::OnSessionClosed(const nsAString& aSessionId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
   if (session) {
     session->OnClosed();
   }
 }
 
+void
+GMPCDMProxy::OnDecrypted(uint32_t aId,
+                         DecryptStatus aResult,
+                         const nsTArray<uint8_t>& aDecryptedData)
+{
+  MOZ_ASSERT(IsOnOwnerThread());
+  gmp_Decrypted(aId, aResult, aDecryptedData);
+}
+
 static void
 LogToConsole(const nsAString& aMsg)
 {
   nsCOMPtr<nsIConsoleService> console(
     do_GetService("@mozilla.org/consoleservice;1"));
   if (!console) {
     NS_WARNING("Failed to log message to console.");
     return;
   }
   nsAutoString msg(aMsg);
   console->LogStringMessage(msg.get());
 }
 
 void
-CDMProxy::OnSessionError(const nsAString& aSessionId,
+GMPCDMProxy::OnSessionError(const nsAString& aSessionId,
                          nsresult aException,
                          uint32_t aSystemCode,
                          const nsAString& aMsg)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
   if (session) {
     session->DispatchKeyError(aSystemCode);
   }
   LogToConsole(aMsg);
 }
 
 void
-CDMProxy::OnRejectPromise(uint32_t aPromiseId,
+GMPCDMProxy::OnRejectPromise(uint32_t aPromiseId,
                           nsresult aDOMException,
                           const nsCString& aMsg)
 {
   MOZ_ASSERT(NS_IsMainThread());
   RejectPromise(aPromiseId, aDOMException, aMsg);
 }
 
 const nsString&
-CDMProxy::KeySystem() const
+GMPCDMProxy::KeySystem() const
 {
   return mKeySystem;
 }
 
 CDMCaps&
-CDMProxy::Capabilites() {
+GMPCDMProxy::Capabilites() {
   return mCapabilites;
 }
 
-RefPtr<CDMProxy::DecryptPromise>
-CDMProxy::Decrypt(MediaRawData* aSample)
+RefPtr<GMPCDMProxy::DecryptPromise>
+GMPCDMProxy::Decrypt(MediaRawData* aSample)
 {
   RefPtr<DecryptJob> job(new DecryptJob(aSample));
   RefPtr<DecryptPromise> promise(job->Ensure());
 
   nsCOMPtr<nsIRunnable> task(
-    NewRunnableMethod<RefPtr<DecryptJob>>(this, &CDMProxy::gmp_Decrypt, job));
-  mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
+    NewRunnableMethod<RefPtr<DecryptJob>>(this, &GMPCDMProxy::gmp_Decrypt, job));
+  mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
   return promise;
 }
 
 void
-CDMProxy::gmp_Decrypt(RefPtr<DecryptJob> aJob)
+GMPCDMProxy::gmp_Decrypt(RefPtr<DecryptJob> aJob)
 {
-  MOZ_ASSERT(IsOnGMPThread());
+  MOZ_ASSERT(IsOnOwnerThread());
 
   if (!mCDM) {
-    aJob->PostResult(GMPAbortedErr);
+    aJob->PostResult(AbortedErr);
     return;
   }
 
   aJob->mId = ++mDecryptionJobCount;
   nsTArray<uint8_t> data;
   data.AppendElements(aJob->mSample->Data(), aJob->mSample->Size());
   mCDM->Decrypt(aJob->mId, aJob->mSample->mCrypto, data);
   mDecryptionJobs.AppendElement(aJob.forget());
 }
 
 void
-CDMProxy::gmp_Decrypted(uint32_t aId,
-                        GMPErr aResult,
-                        const nsTArray<uint8_t>& aDecryptedData)
+GMPCDMProxy::gmp_Decrypted(uint32_t aId,
+                           DecryptStatus aResult,
+                           const nsTArray<uint8_t>& aDecryptedData)
 {
-  MOZ_ASSERT(IsOnGMPThread());
+  MOZ_ASSERT(IsOnOwnerThread());
 #ifdef DEBUG
   bool jobIdFound = false;
 #endif
   for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
     DecryptJob* job = mDecryptionJobs[i];
     if (job->mId == aId) {
 #ifdef DEBUG
       jobIdFound = true;
@@ -709,55 +710,56 @@ CDMProxy::gmp_Decrypted(uint32_t aId,
 #ifdef DEBUG
   if (!jobIdFound) {
     NS_WARNING("GMPDecryptorChild returned incorrect job ID");
   }
 #endif
 }
 
 void
-CDMProxy::DecryptJob::PostResult(GMPErr aResult)
+GMPCDMProxy::DecryptJob::PostResult(DecryptStatus aResult)
 {
   nsTArray<uint8_t> empty;
   PostResult(aResult, empty);
 }
 
 void
-CDMProxy::DecryptJob::PostResult(GMPErr aResult, const nsTArray<uint8_t>& aDecryptedData)
+GMPCDMProxy::DecryptJob::PostResult(DecryptStatus aResult,
+                                    const nsTArray<uint8_t>& aDecryptedData)
 {
   if (aDecryptedData.Length() != mSample->Size()) {
     NS_WARNING("CDM returned incorrect number of decrypted bytes");
   }
-  if (GMP_SUCCEEDED(aResult)) {
+  if (aResult == Ok) {
     nsAutoPtr<MediaRawDataWriter> writer(mSample->CreateWriter());
     PodCopy(writer->Data(),
             aDecryptedData.Elements(),
             std::min<size_t>(aDecryptedData.Length(), mSample->Size()));
-  } else if (aResult == GMPNoKeyErr) {
-    NS_WARNING("CDM returned GMPNoKeyErr");
+  } else if (aResult == NoKeyErr) {
+    NS_WARNING("CDM returned NoKeyErr");
     // We still have the encrypted sample, so we can re-enqueue it to be
     // decrypted again once the key is usable again.
   } else {
-    nsAutoCString str("CDM returned decode failure GMPErr=");
+    nsAutoCString str("CDM returned decode failure DecryptStatus=");
     str.AppendInt(aResult);
     NS_WARNING(str.get());
   }
   mPromise.Resolve(DecryptResult(aResult, mSample), __func__);
 }
 
 void
-CDMProxy::GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
-                                nsTArray<nsCString>& aSessionIds)
+GMPCDMProxy::GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
+                                   nsTArray<nsCString>& aSessionIds)
 {
   CDMCaps::AutoLock caps(Capabilites());
   caps.GetSessionIdsForKeyId(aKeyId, aSessionIds);
 }
 
 void
-CDMProxy::Terminated()
+GMPCDMProxy::Terminated()
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_WARNING("CDM terminated");
   if (!mKeys.IsNull()) {
     mKeys->Terminated();
   }
 }
 
copy from dom/media/eme/CDMProxy.h
copy to dom/media/gmp/GMPCDMProxy.h
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/gmp/GMPCDMProxy.h
@@ -1,188 +1,115 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef CDMProxy_h_
-#define CDMProxy_h_
+#ifndef GMPCDMProxy_h_
+#define GMPCDMProxy_h_
 
-#include "mozilla/CDMCaps.h"
-#include "mozilla/Monitor.h"
-#include "mozilla/MozPromise.h"
-
-#include "mozilla/dom/MediaKeys.h"
-
-#include "nsIThread.h"
-#include "nsString.h"
-#include "nsAutoPtr.h"
+#include "mozilla/CDMProxy.h"
+#include "GMPCDMCallbackProxy.h"
 #include "GMPDecryptorProxy.h"
 
 namespace mozilla {
 class MediaRawData;
-class GMPCDMCallbackProxy;
 
-namespace dom {
-class MediaKeySession;
-} // namespace dom
-
-struct DecryptResult {
-  DecryptResult(GMPErr aStatus, MediaRawData* aSample)
-    : mStatus(aStatus)
-    , mSample(aSample)
-  {}
-  GMPErr mStatus;
-  RefPtr<MediaRawData> mSample;
-};
-
-// Proxies calls GMP/CDM, and proxies calls back.
-// Note: Promises are passed in via a PromiseId, so that the ID can be
-// passed via IPC to the CDM, which can then signal when to reject or
-// resolve the promise using its PromiseId.
-class CDMProxy {
-  typedef dom::PromiseId PromiseId;
-  typedef dom::SessionType SessionType;
+// Implementation of CDMProxy which is based on GMP architecture.
+class GMPCDMProxy : public CDMProxy {
 public:
 
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CDMProxy)
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPCDMProxy, override)
 
   typedef MozPromise<DecryptResult, DecryptResult, /* IsExclusive = */ true> DecryptPromise;
 
-  // Main thread only.
-  CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem);
+  GMPCDMProxy(dom::MediaKeys* aKeys,
+              const nsAString& aKeySystem,
+              GMPCrashHelper* aCrashHelper);
 
-  // Main thread only.
-  // Loads the CDM corresponding to mKeySystem.
-  // Calls MediaKeys::OnCDMCreated() when the CDM is created.
   void Init(PromiseId aPromiseId,
             const nsAString& aOrigin,
             const nsAString& aTopLevelOrigin,
             const nsAString& aGMPName,
-            bool aInPrivateBrowsing,
-            GMPCrashHelper* aHelper);
+            bool aInPrivateBrowsing) override;
 
-  // Main thread only.
-  // Uses the CDM to create a key session.
-  // Calls MediaKeys::OnSessionActivated() when session is created.
-  // Assumes ownership of (Move()s) aInitData's contents.
   void CreateSession(uint32_t aCreateSessionToken,
                      dom::SessionType aSessionType,
                      PromiseId aPromiseId,
                      const nsAString& aInitDataType,
-                     nsTArray<uint8_t>& aInitData);
-
-  // Main thread only.
-  // Uses the CDM to load a presistent session stored on disk.
-  // Calls MediaKeys::OnSessionActivated() when session is loaded.
-  void LoadSession(PromiseId aPromiseId,
-                   const nsAString& aSessionId);
+                     nsTArray<uint8_t>& aInitData) override;
 
-  // Main thread only.
-  // Sends a new certificate to the CDM.
-  // Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
-  // processed the request.
-  // Assumes ownership of (Move()s) aCert's contents.
+  void LoadSession(PromiseId aPromiseId,
+                   const nsAString& aSessionId) override;
+
   void SetServerCertificate(PromiseId aPromiseId,
-                            nsTArray<uint8_t>& aCert);
+                            nsTArray<uint8_t>& aCert) override;
 
-  // Main thread only.
-  // Sends an update to the CDM.
-  // Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
-  // processed the request.
-  // Assumes ownership of (Move()s) aResponse's contents.
   void UpdateSession(const nsAString& aSessionId,
                      PromiseId aPromiseId,
-                     nsTArray<uint8_t>& aResponse);
+                     nsTArray<uint8_t>& aResponse) override;
 
-  // Main thread only.
-  // Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
-  // processed the request.
-  // If processing this operation results in the session actually closing,
-  // we also call MediaKeySession::OnClosed(), which in turn calls
-  // MediaKeys::OnSessionClosed().
   void CloseSession(const nsAString& aSessionId,
-                    PromiseId aPromiseId);
+                    PromiseId aPromiseId) override;
 
-  // Main thread only.
-  // Removes all data for a persisent session.
-  // Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
-  // processed the request.
   void RemoveSession(const nsAString& aSessionId,
-                     PromiseId aPromiseId);
+                     PromiseId aPromiseId) override;
+
+  void Shutdown() override;
 
-  // Main thread only.
-  void Shutdown();
+  void Terminated() override;
 
-  // Main thread only.
-  void Terminated();
+  const nsCString& GetNodeId() const override;
 
-  // Threadsafe.
-  const nsCString& GetNodeId() const;
+  void OnSetSessionId(uint32_t aCreateSessionToken,
+                      const nsAString& aSessionId) override;
 
-  // Main thread only.
-  void OnSetSessionId(uint32_t aCreateSessionToken,
-                      const nsAString& aSessionId);
+  void OnResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess) override;
 
-  // Main thread only.
-  void OnResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess);
-
-  // Main thread only.
   void OnSessionMessage(const nsAString& aSessionId,
-                        GMPSessionMessageType aMessageType,
-                        nsTArray<uint8_t>& aMessage);
+                        dom::MediaKeyMessageType aMessageType,
+                        nsTArray<uint8_t>& aMessage) override;
 
-  // Main thread only.
   void OnExpirationChange(const nsAString& aSessionId,
-                          GMPTimestamp aExpiryTime);
+                          GMPTimestamp aExpiryTime) override;
 
-  // Main thread only.
-  void OnSessionClosed(const nsAString& aSessionId);
+  void OnSessionClosed(const nsAString& aSessionId) override;
 
-  // Main thread only.
   void OnSessionError(const nsAString& aSessionId,
                       nsresult aException,
                       uint32_t aSystemCode,
-                      const nsAString& aMsg);
+                      const nsAString& aMsg) override;
 
-  // Main thread only.
   void OnRejectPromise(uint32_t aPromiseId,
                        nsresult aDOMException,
-                       const nsCString& aMsg);
+                       const nsCString& aMsg) override;
 
-  RefPtr<DecryptPromise> Decrypt(MediaRawData* aSample);
+  RefPtr<DecryptPromise> Decrypt(MediaRawData* aSample) override;
 
-  // Reject promise with DOMException corresponding to aExceptionCode.
-  // Can be called from any thread.
-  void RejectPromise(PromiseId aId, nsresult aExceptionCode,
-                     const nsCString& aReason);
-
-  // Resolves promise with "undefined".
-  // Can be called from any thread.
-  void ResolvePromise(PromiseId aId);
+  void OnDecrypted(uint32_t aId,
+                   DecryptStatus aResult,
+                   const nsTArray<uint8_t>& aDecryptedData) override;
 
-  // Threadsafe.
-  const nsString& KeySystem() const;
+  void RejectPromise(PromiseId aId, nsresult aExceptionCode,
+                     const nsCString& aReason) override;
+
+  void ResolvePromise(PromiseId aId) override;
 
-  // GMP thread only.
-  void gmp_Decrypted(uint32_t aId,
-                     GMPErr aResult,
-                     const nsTArray<uint8_t>& aDecryptedData);
+  const nsString& KeySystem() const override;
 
-  CDMCaps& Capabilites();
+  CDMCaps& Capabilites() override;
 
-  // Main thread only.
-  void OnKeyStatusesChange(const nsAString& aSessionId);
+  void OnKeyStatusesChange(const nsAString& aSessionId) override;
 
   void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
-                             nsTArray<nsCString>& aSessionIds);
+                             nsTArray<nsCString>& aSessionIds) override;
 
 #ifdef DEBUG
-  bool IsOnGMPThread();
+  bool IsOnOwnerThread() override;
 #endif
 
 private:
   friend class gmp_InitDoneCallback;
   friend class gmp_InitGetGMPDecryptorCallback;
 
   struct InitData {
     uint32_t mPromiseId;
@@ -249,114 +176,83 @@ private:
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecryptJob)
 
     explicit DecryptJob(MediaRawData* aSample)
       : mId(0)
       , mSample(aSample)
     {
     }
 
-    void PostResult(GMPErr aResult, const nsTArray<uint8_t>& aDecryptedData);
-    void PostResult(GMPErr aResult);
+    void PostResult(DecryptStatus aResult,
+                    const nsTArray<uint8_t>& aDecryptedData);
+    void PostResult(DecryptStatus aResult);
 
     RefPtr<DecryptPromise> Ensure() {
       return mPromise.Ensure(__func__);
     }
 
     uint32_t mId;
     RefPtr<MediaRawData> mSample;
   private:
     ~DecryptJob() {}
     MozPromiseHolder<DecryptPromise> mPromise;
   };
   // GMP thread only.
   void gmp_Decrypt(RefPtr<DecryptJob> aJob);
 
+  // GMP thread only.
+  void gmp_Decrypted(uint32_t aId,
+                     DecryptStatus aResult,
+                     const nsTArray<uint8_t>& aDecryptedData);
+
   class RejectPromiseTask : public Runnable {
   public:
-    RejectPromiseTask(CDMProxy* aProxy,
+    RejectPromiseTask(GMPCDMProxy* aProxy,
                       PromiseId aId,
                       nsresult aCode,
                       const nsCString& aReason)
       : mProxy(aProxy)
       , mId(aId)
       , mCode(aCode)
       , mReason(aReason)
     {
     }
     NS_METHOD Run() {
       mProxy->RejectPromise(mId, mCode, mReason);
       return NS_OK;
     }
   private:
-    RefPtr<CDMProxy> mProxy;
+    RefPtr<GMPCDMProxy> mProxy;
     PromiseId mId;
     nsresult mCode;
     nsCString mReason;
   };
 
-  ~CDMProxy();
-
-  // Helper to enforce that a raw pointer is only accessed on the main thread.
-  template<class Type>
-  class MainThreadOnlyRawPtr {
-  public:
-    explicit MainThreadOnlyRawPtr(Type* aPtr)
-      : mPtr(aPtr)
-    {
-      MOZ_ASSERT(NS_IsMainThread());
-    }
-
-    bool IsNull() const {
-      MOZ_ASSERT(NS_IsMainThread());
-      return !mPtr;
-    }
+  ~GMPCDMProxy();
 
-    void Clear() {
-      MOZ_ASSERT(NS_IsMainThread());
-      mPtr = nullptr;
-    }
-
-    Type* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN {
-      MOZ_ASSERT(NS_IsMainThread());
-      return mPtr;
-    }
-  private:
-    Type* mPtr;
-  };
-
-  // Our reference back to the MediaKeys object.
-  // WARNING: This is a non-owning reference that is cleared by MediaKeys
-  // destructor. only use on main thread, and always nullcheck before using!
-  MainThreadOnlyRawPtr<dom::MediaKeys> mKeys;
-
-  const nsString mKeySystem;
-
-  // Gecko Media Plugin thread. All interactions with the out-of-process
-  // EME plugin must come from this thread.
-  RefPtr<nsIThread> mGMPThread;
-
-  nsCString mNodeId;
+  GMPCrashHelper* mCrashHelper;
 
   GMPDecryptorProxy* mCDM;
+
   CDMCaps mCapabilites;
+
   nsAutoPtr<GMPCDMCallbackProxy> mCallback;
 
   // Decryption jobs sent to CDM, awaiting result.
   // GMP thread only.
   nsTArray<RefPtr<DecryptJob>> mDecryptionJobs;
 
   // Number of buffers we've decrypted. Used to uniquely identify
   // decryption jobs sent to CDM. Note we can't just use the length of
   // mDecryptionJobs as that shrinks as jobs are completed and removed
   // from it.
   // GMP thread only.
   uint32_t mDecryptionJobCount;
 
-  // True if CDMProxy::gmp_Shutdown was called.
+  // True if GMPCDMProxy::gmp_Shutdown was called.
   // GMP thread only.
   bool mShutdownCalled;
 };
 
 
 } // namespace mozilla
 
-#endif // CDMProxy_h_
+#endif // GMPCDMProxy_h_
--- a/dom/media/gmp/moz.build
+++ b/dom/media/gmp/moz.build
@@ -31,16 +31,17 @@ EXPORTS += [
     'gmp-api/gmp-video-host.h',
     'gmp-api/gmp-video-plane.h',
     'GMPAudioDecoderChild.h',
     'GMPAudioDecoderParent.h',
     'GMPAudioDecoderProxy.h',
     'GMPAudioHost.h',
     'GMPCallbackBase.h',
     'GMPCDMCallbackProxy.h',
+    'GMPCDMProxy.h',
     'GMPChild.h',
     'GMPContentChild.h',
     'GMPContentParent.h',
     'GMPCrashHelperHolder.h',
     'GMPDecryptorChild.h',
     'GMPDecryptorParent.h',
     'GMPDecryptorProxy.h',
     'GMPEncryptedBufferDataImpl.h',
@@ -82,16 +83,17 @@ if CONFIG['OS_TARGET'] == 'Android':
         'rlz',
     ]
 
 UNIFIED_SOURCES += [
     'GMPAudioDecoderChild.cpp',
     'GMPAudioDecoderParent.cpp',
     'GMPAudioHost.cpp',
     'GMPCDMCallbackProxy.cpp',
+    'GMPCDMProxy.cpp',
     'GMPChild.cpp',
     'GMPContentChild.cpp',
     'GMPContentParent.cpp',
     'GMPDecryptorChild.cpp',
     'GMPDecryptorParent.cpp',
     'GMPDiskStorage.cpp',
     'GMPEncryptedBufferDataImpl.cpp',
     'GMPMemoryStorage.cpp',
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -187,42 +187,42 @@ MediaSource::ReadyState()
 double
 MediaSource::Duration()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mReadyState == MediaSourceReadyState::Closed) {
     return UnspecifiedNaN<double>();
   }
   MOZ_ASSERT(mDecoder);
-  return mDecoder->GetMediaSourceDuration();
+  return mDecoder->GetDuration();
 }
 
 void
 MediaSource::SetDuration(double aDuration, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("SetDuration(aDuration=%f, ErrorResult)", aDuration);
   if (aDuration < 0 || IsNaN(aDuration)) {
     aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
     return;
   }
   if (mReadyState != MediaSourceReadyState::Open ||
       mSourceBuffers->AnyUpdating()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
-  SetDuration(aDuration, MSRangeRemovalAction::RUN);
+  DurationChange(aDuration, aRv);
 }
 
 void
-MediaSource::SetDuration(double aDuration, MSRangeRemovalAction aAction)
+MediaSource::SetDuration(double aDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_API("SetDuration(aDuration=%f)", aDuration);
-  mDecoder->SetMediaSourceDuration(aDuration, aAction);
+  mDecoder->SetMediaSourceDuration(aDuration);
 }
 
 already_AddRefed<SourceBuffer>
 MediaSource::AddSourceBuffer(const nsAString& aType, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   DecoderDoctorDiagnostics diagnostics;
   nsresult rv = mozilla::IsTypeSupported(aType, &diagnostics);
@@ -318,21 +318,17 @@ MediaSource::EndOfStream(const Optional<
       mSourceBuffers->AnyUpdating()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   SetReadyState(MediaSourceReadyState::Ended);
   mSourceBuffers->Ended();
   if (!aError.WasPassed()) {
-    mDecoder->SetMediaSourceDuration(mSourceBuffers->GetHighestBufferedEndTime(),
-                                     MSRangeRemovalAction::SKIP);
-    if (aRv.Failed()) {
-      return;
-    }
+    DurationChange(mSourceBuffers->GetHighestBufferedEndTime(), aRv);
     // Notify reader that all data is now available.
     mDecoder->Ended(true);
     return;
   }
   switch (aError.Value()) {
   case MediaSourceEndOfStreamError::Network:
     mDecoder->NetworkError();
     break;
@@ -520,26 +516,48 @@ void
 MediaSource::QueueAsyncSimpleEvent(const char* aName)
 {
   MSE_DEBUG("Queuing event '%s'", aName);
   nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<MediaSource>(this, aName);
   NS_DispatchToMainThread(event);
 }
 
 void
-MediaSource::DurationChange(double aOldDuration, double aNewDuration)
+MediaSource::DurationChange(double aNewDuration, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("DurationChange(aOldDuration=%f, aNewDuration=%f)", aOldDuration, aNewDuration);
+  MSE_DEBUG("DurationChange(aNewDuration=%f)", aNewDuration);
+
+  // 1. If the current value of duration is equal to new duration, then return.
+  if (mDecoder->GetDuration() == aNewDuration) {
+    return;
+  }
+
+  // 2. If new duration is less than the highest starting presentation timestamp
+  // of any buffered coded frames for all SourceBuffer objects in sourceBuffers,
+  // then throw an InvalidStateError exception and abort these steps.
+  if (aNewDuration < mSourceBuffers->HighestStartTime()) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
 
-  if (aNewDuration < aOldDuration) {
-    // Remove all buffered data from aNewDuration.
-    mSourceBuffers->RangeRemoval(aNewDuration, PositiveInfinity<double>());
-  }
-  // TODO: If partial audio frames/text cues exist, clamp duration based on mSourceBuffers.
+  // 3. Update duration to new duration.
+  // 4. If a user agent is unable to partially render audio frames or text cues
+  // that start before and end after the duration, then run the following steps:
+  // Update new duration to the highest end time reported by the buffered
+  // attribute across all SourceBuffer objects in sourceBuffers.
+  //   1. Update new duration to the highest end time reported by the buffered
+  //      attribute across all SourceBuffer objects in sourceBuffers.
+  //   2. Update duration to new duration.
+  aNewDuration =
+    std::max(aNewDuration, mSourceBuffers->GetHighestBufferedEndTime());
+
+  // 5. Update the media duration to new duration and run the HTMLMediaElement
+  // duration change algorithm.
+  mDecoder->SetMediaSourceDuration(aNewDuration);
 }
 
 void
 MediaSource::GetMozDebugReaderData(nsAString& aString)
 {
   mDecoder->GetMozDebugReaderData(aString);
 }
 
--- a/dom/media/mediasource/MediaSource.h
+++ b/dom/media/mediasource/MediaSource.h
@@ -25,21 +25,16 @@ struct JSContext;
 class JSObject;
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 
 class ErrorResult;
 template <typename T> class AsyncEventRunner;
 
-enum MSRangeRemovalAction: uint8_t {
-  RUN = 0,
-  SKIP = 1
-};
-
 namespace dom {
 
 class GlobalObject;
 class SourceBuffer;
 class SourceBufferList;
 template <typename T> class Optional;
 
 #define MOZILLA_DOM_MEDIASOURCE_IMPLEMENTATION_IID \
@@ -111,34 +106,31 @@ public:
 
   bool HasLiveSeekableRange() const { return mLiveSeekableRange.isSome(); }
   media::TimeInterval LiveSeekableRange() const
   {
     return mLiveSeekableRange.value();
   }
 
 private:
-  // MediaSourceDecoder uses DurationChange to set the duration
-  // without hitting the checks in SetDuration.
-  friend class mozilla::MediaSourceDecoder;
   // SourceBuffer uses SetDuration and SourceBufferIsActive
   friend class mozilla::dom::SourceBuffer;
 
   ~MediaSource();
 
   explicit MediaSource(nsPIDOMWindowInner* aWindow);
 
   friend class AsyncEventRunner<MediaSource>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
-  void DurationChange(double aOldDuration, double aNewDuration);
+  void DurationChange(double aNewDuration, ErrorResult& aRv);
 
   // SetDuration with no checks.
-  void SetDuration(double aDuration, MSRangeRemovalAction aAction);
+  void SetDuration(double aDuration);
 
   // Mark SourceBuffer as active and rebuild ActiveSourceBuffers.
   void SourceBufferIsActive(SourceBuffer* aSourceBuffer);
 
   RefPtr<SourceBufferList> mSourceBuffers;
   RefPtr<SourceBufferList> mActiveSourceBuffers;
 
   RefPtr<MediaSourceDecoder> mDecoder;
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -213,46 +213,34 @@ MediaSourceDecoder::SetInitialDuration(i
   if (!mMediaSource || !IsNaN(ExplicitDuration())) {
     return;
   }
   double duration = aDuration;
   // A duration of -1 is +Infinity.
   if (aDuration >= 0) {
     duration /= USECS_PER_S;
   }
-  SetMediaSourceDuration(duration, MSRangeRemovalAction::SKIP);
+  SetMediaSourceDuration(duration);
 }
 
 void
-MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction)
+MediaSourceDecoder::SetMediaSourceDuration(double aDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  double oldDuration = ExplicitDuration();
   if (aDuration >= 0) {
     int64_t checkedDuration;
     if (NS_FAILED(SecondsToUsecs(aDuration, checkedDuration))) {
       // INT64_MAX is used as infinity by the state machine.
       // We want a very bigger number, but not infinity.
       checkedDuration = INT64_MAX - 1;
     }
     SetExplicitDuration(aDuration);
   } else {
     SetExplicitDuration(PositiveInfinity<double>());
   }
-
-  if (mMediaSource && aAction != MSRangeRemovalAction::SKIP) {
-    mMediaSource->DurationChange(oldDuration, aDuration);
-  }
-}
-
-double
-MediaSourceDecoder::GetMediaSourceDuration()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return ExplicitDuration();
 }
 
 void
 MediaSourceDecoder::GetMozDebugReaderData(nsAString& aString)
 {
   if (mReader && mDemuxer) {
     mReader->GetMozDebugReaderData(aString);
     mDemuxer->GetMozDebugReaderData(aString);
--- a/dom/media/mediasource/MediaSourceDecoder.h
+++ b/dom/media/mediasource/MediaSourceDecoder.h
@@ -59,18 +59,17 @@ public:
   void DetachMediaSource();
 
   void Ended(bool aEnded);
 
   // Return the duration of the video in seconds.
   double GetDuration() override;
 
   void SetInitialDuration(int64_t aDuration);
-  void SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction);
-  double GetMediaSourceDuration();
+  void SetMediaSourceDuration(double aDuration);
 
   MediaSourceDemuxer* GetDemuxer()
   {
     return mDemuxer;
   }
 
   // Returns a string describing the state of the MediaSource internal
   // buffered data. Used for debugging purposes.
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -187,16 +187,20 @@ SourceBuffer::Abort(ErrorResult& aRv)
   if (!IsAttached()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   if (mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
+  if (mPendingRemoval.Exists()) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return;
+  }
   AbortBufferAppend();
   ResetParserState();
   mCurrentAttributes.SetAppendWindowStart(0);
   mCurrentAttributes.SetAppendWindowEnd(PositiveInfinity<double>());
 }
 
 void
 SourceBuffer::AbortBufferAppend()
@@ -243,21 +247,25 @@ SourceBuffer::Remove(double aStart, doub
 }
 
 void
 SourceBuffer::RangeRemoval(double aStart, double aEnd)
 {
   StartUpdating();
 
   RefPtr<SourceBuffer> self = this;
-  mTrackBuffersManager->RangeRemoval(TimeUnit::FromSeconds(aStart),
-                                     TimeUnit::FromSeconds(aEnd))
-    ->Then(AbstractThread::MainThread(), __func__,
-           [self] (bool) { self->StopUpdating(); },
-           []() { MOZ_ASSERT(false); });
+  mPendingRemoval.Begin(
+    mTrackBuffersManager->RangeRemoval(TimeUnit::FromSeconds(aStart),
+                                       TimeUnit::FromSeconds(aEnd))
+      ->Then(AbstractThread::MainThread(), __func__,
+             [self] (bool) {
+               self->mPendingRemoval.Complete();
+               self->StopUpdating();
+             },
+             []() { MOZ_ASSERT(false); }));
 }
 
 void
 SourceBuffer::Detach()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("Detach");
   if (!mMediaSource) {
@@ -387,17 +395,17 @@ SourceBuffer::AbortUpdating()
 void
 SourceBuffer::CheckEndTime()
 {
   MOZ_ASSERT(NS_IsMainThread());
   // Check if we need to update mMediaSource duration
   double endTime = mCurrentAttributes.GetGroupEndTimestamp().ToSeconds();
   double duration = mMediaSource->Duration();
   if (endTime > duration) {
-    mMediaSource->SetDuration(endTime, MSRangeRemovalAction::SKIP);
+    mMediaSource->SetDuration(endTime);
   }
 }
 
 void
 SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
 {
   MSE_DEBUG("AppendData(aLength=%u)", aLength);
 
@@ -535,16 +543,25 @@ double
 SourceBuffer::GetBufferedEnd()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ErrorResult dummy;
   RefPtr<TimeRanges> ranges = GetBuffered(dummy);
   return ranges->Length() > 0 ? ranges->GetEndTime() : 0;
 }
 
+double
+SourceBuffer::HighestStartTime()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return mTrackBuffersManager
+         ? mTrackBuffersManager->HighestStartTime().ToSeconds()
+         : 0.0;
+}
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(SourceBuffer)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SourceBuffer)
   // Tell the TrackBuffer to end its current SourceBufferResource.
   TrackBuffersManager* manager = tmp->mTrackBuffersManager;
   if (manager) {
     manager->Detach();
   }
--- a/dom/media/mediasource/SourceBuffer.h
+++ b/dom/media/mediasource/SourceBuffer.h
@@ -112,16 +112,17 @@ public:
   {
     return mMediaSource != nullptr;
   }
 
   void Ended();
 
   double GetBufferedStart();
   double GetBufferedEnd();
+  double HighestStartTime();
 
   // Runs the range removal algorithm as defined by the MSE spec.
   void RangeRemoval(double aStart, double aEnd);
 
   bool IsActive() const
   {
     return mActive;
   }
@@ -169,16 +170,17 @@ private:
   RefPtr<TrackBuffersManager> mTrackBuffersManager;
   SourceBufferAttributes mCurrentAttributes;
 
   bool mUpdating;
 
   mozilla::Atomic<bool> mActive;
 
   MozPromiseRequestHolder<SourceBufferTask::AppendPromise> mPendingAppend;
+  MozPromiseRequestHolder<SourceBufferTask::RangeRemovalPromise> mPendingRemoval;
   const nsCString mType;
 
   RefPtr<TimeRanges> mBuffered;
 };
 
 } // namespace dom
 
 } // namespace mozilla
--- a/dom/media/mediasource/SourceBufferList.cpp
+++ b/dom/media/mediasource/SourceBufferList.cpp
@@ -174,16 +174,28 @@ SourceBufferList::SourceBufferList(Media
 }
 
 MediaSource*
 SourceBufferList::GetParentObject() const
 {
   return mMediaSource;
 }
 
+double
+SourceBufferList::HighestStartTime()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  double highestStartTime = 0;
+  for (auto& sourceBuffer : mSourceBuffers) {
+    highestStartTime =
+      std::max(sourceBuffer->HighestStartTime(), highestStartTime);
+  }
+  return highestStartTime;
+}
+
 JSObject*
 SourceBufferList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return SourceBufferListBinding::Wrap(aCx, this, aGivenProto);
 }
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(SourceBufferList, DOMEventTargetHelper,
                                    mMediaSource, mSourceBuffers)
--- a/dom/media/mediasource/SourceBufferList.h
+++ b/dom/media/mediasource/SourceBufferList.h
@@ -80,16 +80,18 @@ public:
 
   // Append a SourceBuffer to the list. No event is fired.
   void AppendSimple(SourceBuffer* aSourceBuffer);
 
   // Remove all SourceBuffers from mSourceBuffers.
   //  No event is fired and no action is performed on the sourcebuffers.
   void ClearSimple();
 
+  double HighestStartTime();
+
 private:
   ~SourceBufferList();
 
   friend class AsyncEventRunner<SourceBufferList>;
   void DispatchSimpleEvent(const char* aName);
   void QueueAsyncSimpleEvent(const char* aName);
 
   RefPtr<MediaSource> mMediaSource;
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -1359,16 +1359,19 @@ TrackBuffersManager::ProcessFrames(Track
   TrackBuffer samples; // array that will contain the frames to be added
                        // to our track buffer.
 
   // We assume that no frames are contiguous within a media segment and as such
   // don't need to check for discontinuity except for the first frame and should
   // a frame be ignored due to the target window.
   bool needDiscontinuityCheck = true;
 
+  // Highest presentation time seen in samples block.
+  TimeUnit highestSampleTime;
+
   if (aSamples.Length()) {
     aTrackData.mLastParsedEndTime = TimeUnit();
   }
 
   for (auto& sample : aSamples) {
     SAMPLE_DEBUG("Processing %s frame(pts:%lld end:%lld, dts:%lld, duration:%lld, "
                "kf:%d)",
                aTrackData.mInfo->mMimeType.get(),
@@ -1491,16 +1494,17 @@ TrackBuffersManager::ProcessFrames(Track
       if (samples.Length()) {
         // We are creating a discontinuity in the samples.
         // Insert the samples processed so far.
         InsertFrames(samples, samplesRange, trackBuffer);
         samples.Clear();
         samplesRange = TimeIntervals();
         trackBuffer.mSizeBuffer += sizeNewSamples;
         sizeNewSamples = 0;
+        UpdateHighestTimestamp(trackBuffer, highestSampleTime);
       }
       trackBuffer.mNeedRandomAccessPoint = true;
       needDiscontinuityCheck = true;
       continue;
     }
 
     samplesRange += sampleInterval;
     sizeNewSamples += sample->ComputedSizeOfIncludingThis();
@@ -1523,29 +1527,33 @@ TrackBuffersManager::ProcessFrames(Track
     // 18. Set last frame duration for track buffer to frame duration.
     trackBuffer.mLastFrameDuration = Some(sampleDuration);
 
     // 19. If highest end timestamp for track buffer is unset or frame end timestamp is greater than highest end timestamp, then set highest end timestamp for track buffer to frame end timestamp.
     if (trackBuffer.mHighestEndTimestamp.isNothing() ||
         sampleInterval.mEnd > trackBuffer.mHighestEndTimestamp.ref()) {
       trackBuffer.mHighestEndTimestamp = Some(sampleInterval.mEnd);
     }
+    if (sampleInterval.mStart > highestSampleTime) {
+      highestSampleTime = sampleInterval.mStart;
+    }
     // 20. If frame end timestamp is greater than group end timestamp, then set group end timestamp equal to frame end timestamp.
     if (sampleInterval.mEnd > mSourceBufferAttributes->GetGroupEndTimestamp()) {
       mSourceBufferAttributes->SetGroupEndTimestamp(sampleInterval.mEnd);
     }
     // 21. If generate timestamps flag equals true, then set timestampOffset equal to frame end timestamp.
     if (mSourceBufferAttributes->mGenerateTimestamps) {
       mSourceBufferAttributes->SetTimestampOffset(sampleInterval.mEnd);
     }
   }
 
   if (samples.Length()) {
     InsertFrames(samples, samplesRange, trackBuffer);
     trackBuffer.mSizeBuffer += sizeNewSamples;
+    UpdateHighestTimestamp(trackBuffer, highestSampleTime);
   }
 }
 
 bool
 TrackBuffersManager::CheckNextInsertionIndex(TrackData& aTrackData,
                                              const TimeUnit& aSampleTime)
 {
   if (aTrackData.mNextInsertionIndex.isSome()) {
@@ -1674,16 +1682,26 @@ TrackBuffersManager::InsertFrames(TrackB
   // length.
   if (aIntervals.Length()) {
     TimeIntervals range(aIntervals);
     range.SetFuzz(trackBuffer.mLongestFrameDuration / 2);
     trackBuffer.mSanitizedBufferedRanges += range;
   }
 }
 
+void
+TrackBuffersManager::UpdateHighestTimestamp(TrackData& aTrackData,
+                                            const media::TimeUnit& aHighestTime)
+{
+  if (aHighestTime > aTrackData.mHighestStartTimestamp) {
+    MonitorAutoLock mon(mMonitor);
+    aTrackData.mHighestStartTimestamp = aHighestTime;
+  }
+}
+
 size_t
 TrackBuffersManager::RemoveFrames(const TimeIntervals& aIntervals,
                                   TrackData& aTrackData,
                                   uint32_t aStartIndex)
 {
   TrackBuffer& data = aTrackData.mBuffers.LastElement();
   Maybe<uint32_t> firstRemovedIndex;
   uint32_t lastRemovedIndex = 0;
@@ -1771,16 +1789,30 @@ TrackBuffersManager::RemoveFrames(const 
 
   // Recalculate sanitized buffered ranges.
   aTrackData.mSanitizedBufferedRanges = aTrackData.mBufferedRanges;
   aTrackData.mSanitizedBufferedRanges.SetFuzz(TimeUnit::FromMicroseconds(maxSampleDuration/2));
 
   data.RemoveElementsAt(firstRemovedIndex.ref(),
                         lastRemovedIndex - firstRemovedIndex.ref() + 1);
 
+  if (aIntervals.GetEnd() >= aTrackData.mHighestStartTimestamp) {
+    // The sample with the highest presentation time got removed.
+    // Rescan the trackbuffer to determine the new one.
+    int64_t highestStartTime = 0;
+    for (const auto& sample : data) {
+      if (sample->mTime > highestStartTime) {
+        highestStartTime = sample->mTime;
+      }
+    }
+    MonitorAutoLock mon(mMonitor);
+    aTrackData.mHighestStartTimestamp =
+      TimeUnit::FromMicroseconds(highestStartTime);
+  }
+
   return firstRemovedIndex.ref();
 }
 
 void
 TrackBuffersManager::RecreateParser(bool aReuseInitData)
 {
   MOZ_ASSERT(OnTaskQueue());
   // Recreate our parser for only the data remaining. This is required
@@ -1795,17 +1827,16 @@ TrackBuffersManager::RecreateParser(bool
   } else {
     mProcessedInput = 0;
   }
 }
 
 nsTArray<TrackBuffersManager::TrackData*>
 TrackBuffersManager::GetTracksList()
 {
-  MOZ_ASSERT(OnTaskQueue());
   nsTArray<TrackData*> tracks;
   if (HasVideo()) {
     tracks.AppendElement(&mVideoTracks);
   }
   if (HasAudio()) {
     tracks.AppendElement(&mAudioTracks);
   }
   return tracks;
@@ -1837,16 +1868,28 @@ TimeIntervals
 TrackBuffersManager::SafeBuffered(TrackInfo::TrackType aTrack) const
 {
   MonitorAutoLock mon(mMonitor);
   return aTrack == TrackInfo::kVideoTrack
     ? mVideoBufferedRanges
     : mAudioBufferedRanges;
 }
 
+TimeUnit
+TrackBuffersManager::HighestStartTime()
+{
+  MonitorAutoLock mon(mMonitor);
+  TimeUnit highestStartTime;
+  for (auto& track : GetTracksList()) {
+    highestStartTime =
+      std::max(track->mHighestStartTimestamp, highestStartTime);
+  }
+  return highestStartTime;
+}
+
 const TrackBuffersManager::TrackBuffer&
 TrackBuffersManager::GetTrackBuffer(TrackInfo::TrackType aTrack)
 {
   MOZ_ASSERT(OnTaskQueue());
   return GetTracksData(aTrack).mBuffers.LastElement();
 }
 
 uint32_t TrackBuffersManager::FindSampleIndex(const TrackBuffer& aTrackBuffer,
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -117,16 +117,17 @@ public:
   // Eviction is done in two steps, first remove data up to aPlaybackTime
   // and if still more space is needed remove from the end.
   EvictDataResult EvictData(const media::TimeUnit& aPlaybackTime, int64_t aSize);
 
   // Returns the buffered range currently managed.
   // This may be called on any thread.
   // Buffered must conform to http://w3c.github.io/media-source/index.html#widl-SourceBuffer-buffered
   media::TimeIntervals Buffered();
+  media::TimeUnit HighestStartTime();
 
   // Return the size of the data managed by this SourceBufferContentManager.
   int64_t GetSize() const;
 
   // Indicate that the MediaSource parent object got into "ended" state.
   void Ended();
 
   // The parent SourceBuffer is about to be destroyed.
@@ -275,16 +276,20 @@ private:
     // been appended yet.
     Maybe<media::TimeUnit> mLastFrameDuration;
     // Highest end timestamp variable that stores the highest coded frame end
     // timestamp across all coded frames in the current coded frame group that
     // were appended to this track buffer.
     // The variable is initially unset to indicate that no coded frames have
     // been appended yet.
     Maybe<media::TimeUnit> mHighestEndTimestamp;
+    // Highest presentation timestamp in track buffer.
+    // Protected by global monitor, except when reading on the task queue as it
+    // is only written there.
+    media::TimeUnit mHighestStartTimestamp;
     // Longest frame duration seen since last random access point.
     // Only ever accessed when mLastDecodeTimestamp and mLastFrameDuration are
     // set.
     media::TimeUnit mLongestFrameDuration;
     // Need random access point flag variable that keeps track of whether the
     // track buffer is waiting for a random access point coded frame.
     // The variable is initially set to true to indicate that random access
     // point coded frame is needed before anything can be added to the track
@@ -340,16 +345,18 @@ private:
 
   void CheckSequenceDiscontinuity(const media::TimeUnit& aPresentationTime);
   void ProcessFrames(TrackBuffer& aSamples, TrackData& aTrackData);
   bool CheckNextInsertionIndex(TrackData& aTrackData,
                                const media::TimeUnit& aSampleTime);
   void InsertFrames(TrackBuffer& aSamples,
                     const media::TimeIntervals& aIntervals,
                     TrackData& aTrackData);
+  void UpdateHighestTimestamp(TrackData& aTrackData,
+                              const media::TimeUnit& aHighestTime);
   // Remove all frames and their dependencies contained in aIntervals.
   // Return the index at which frames were first removed or 0 if no frames
   // removed.
   size_t RemoveFrames(const media::TimeIntervals& aIntervals,
                       TrackData& aTrackData,
                       uint32_t aStartIndex);
   // Find index of sample. Return a negative value if not found.
   uint32_t FindSampleIndex(const TrackBuffer& aTrackBuffer,
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -54,16 +54,17 @@ skip-if = toolkit == 'android' #timeout 
 [test_BufferingWait_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_DrainOnMissingData_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_EndOfStream.html]
 skip-if = (true || toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet only bug 1101187 and bug 1182946
 [test_EndOfStream_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android' || buildapp == 'mulet')) # Not supported on xp and android 2.3
+[test_DurationChange.html]
 [test_DurationUpdated.html]
 [test_DurationUpdated_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_FrameSelection.html]
 [test_FrameSelection_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_HaveMetadataUnbufferedSeek.html]
 [test_HaveMetadataUnbufferedSeek_mp4.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasource/test/test_DurationChange.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>MSE: check that duration change behaves properly</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="mediasource.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+runWithMSE(function (ms, v) {
+  ms.addEventListener("sourceopen", function () {
+    var sb = ms.addSourceBuffer("video/webm");
+
+    fetchWithXHR("seek.webm", function (arrayBuffer) {
+      sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 318));
+      once(v, "loadedmetadata")
+      .then(function() {
+        is(v.duration, ms.duration, "video duration is mediasource one");
+        try {
+          ms.duration = 0;
+        } catch (e) { ok(false, "must not throw as operation is valid"); }
+        is(v.duration, 0, "reducing duration with no data buffered is valid");
+        sb.appendBuffer(new Uint8Array(arrayBuffer, 318));
+        // Adding more data will fire durationchange.
+        once(sb, "updateend")
+        .then(function() {
+          ok(true, "got updateend");
+          // XXX: Duration should be exactly 4.0, see bug 1065207.
+          ok(Math.abs(v.duration - 4) <= 0.002, "Video has correct duration");
+          var error = false;
+          try {
+            ms.duration = 0;
+          } catch (e) {
+            ok(true, "must use remove for range removal");
+            is(e.name, "InvalidStateError", "Error is InvalidStateError");
+            error = true;
+          }
+          ok(error, "got an error");
+          ok(Math.abs(v.duration - 4) <= 0.002, "Video has correct duration");
+          try {
+            ms.duration = 10;
+          } catch (e) { ok(false, "must not throw as setting duration past data is valid"); }
+          is(v.duration, 10, "extending duration is always valid");
+          // The last sample has a start time of 3.967000s and a end time of 4.001 (see bug 1065207).
+          try {
+            ms.duration = 3.967000;
+          } catch (e) { ok(false, "setting duration with >= highest frame presentation time is valid"); }
+          is(v.duration, sb.buffered.end(0), "duration is the highest end time reported by the buffered attribute ");
+          try {
+            ms.duration = 3.97;
+          } catch (e) { ok(false, "setting duration with >= highest frame presentation time is valid"); }
+          is(v.duration, sb.buffered.end(0), "duration is the highest end time reported by the buffered attribute ");
+          error = false;
+          try {
+            ms.duration = 3.96;
+          } catch (e) {
+            ok(true, "setting duration with < highest frame presentation time is not valid");
+            is(e.name, "InvalidStateError", "Error is InvalidStateError");
+            error = true;
+          }
+          ok(error, "got an error");
+          is(v.duration, sb.buffered.end(0), "duration is the highest end time reported by the buffered attribute ");
+          error = false;
+          try {
+            ms.duration = -1;
+          } catch (e) {
+            ok(true, "can't set a negative duration");
+            is(e.name, "TypeError", "Error is TypeError");
+            error = true;
+          }
+          ok(error, "got an error");
+          sb.remove(sb.buffered.end(0), Infinity);
+          is(sb.updating, true, "updating is true")
+          error = false;
+          try {
+            ms.duration = Infinity;
+          } catch (e) {
+            ok(true, "setting the duration while updating is not allowed");
+            is(e.name, "InvalidStateError", "Error is InvalidStateError");
+            error = true;
+          }
+          ok(error, "got an error");
+          error = false;
+          try {
+            sb.abort();
+          } catch (e) {
+            ok(true, "Can't use abort while range removal is in progress");
+            is(e.name, "InvalidStateError", "Error is InvalidStateError");
+            error = true;
+          }
+          ok(error, "got an error");
+          is(v.duration, sb.buffered.end(0), "duration is the highest end time reported by the buffered attribute ");
+          once(sb, "updateend", () => ms.endOfStream());
+        });
+      });
+    });
+  });
+  ms.addEventListener("sourceended", function () {
+    SimpleTest.finish();
+  });
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/mediasource/test/test_DurationUpdated.html
+++ b/dom/media/mediasource/test/test_DurationUpdated.html
@@ -7,52 +7,50 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
-var updateCount = 0;
 var durationChangeCount = 0;
 
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/webm");
 
+    v.addEventListener("durationchange", function () {
+      durationChangeCount++;
+    });
+
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 318));
-      sb.addEventListener("updateend", function () {
-        updateCount++;
-        if (updateCount == 1) {
-          v.addEventListener("loadedmetadata", function () {
-            v.addEventListener("durationchange", function () {
-              durationChangeCount++;
-            });
-            // Set mediasource duration to 0, so future appendBuffer
-            // will update the mediasource duration
-            // setting ms.duration will fire updatestart/update/updateend
-            // event as per w3c spec followed by a durationchange
-            ms.duration = 0;
-          });
-        } else if (updateCount == 2) {
-          // will fire updatestart/update/updateend
-          // and a durationchange
-          sb.appendBuffer(new Uint8Array(arrayBuffer, 318));
-        } else if (updateCount == 3) {
+      // Adding the first init segment will fire a durationchange.
+      once(v, "loadedmetadata")
+      .then(function() {
+        ok(true, "got loadedmetadata");
+        // Set mediasource duration to 0, so future appendBuffer
+        // will update the mediasource duration.
+        // Changing the duration will fire a durationchange.
+        ms.duration = 0;
+        sb.appendBuffer(new Uint8Array(arrayBuffer, 318));
+        // Adding more data will fire durationchange.
+        once(sb, "updateend")
+        .then(function() {
+          ok(true, "got updateend");
           // this will not fire durationchange as new duration == old duration
           ms.endOfStream();
-        }
+        });
       });
     });
   });
   ms.addEventListener("sourceended", function () {
+    is(durationChangeCount, 3, "durationchange not fired as many times as expected");
     // XXX: Duration should be exactly 4.0, see bug 1065207.
-    is(durationChangeCount, 2, "durationchange not fired as many times as expected");
     ok(Math.abs(v.duration - 4) <= 0.002, "Video has correct duration");
     SimpleTest.finish();
   });
 });
 
 </script>
 </pre>
 </body>
--- a/dom/media/mediasource/test/test_DurationUpdated_mp4.html
+++ b/dom/media/mediasource/test/test_DurationUpdated_mp4.html
@@ -7,54 +7,50 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
-var updateCount = 0;
 var durationChangeCount = 0;
 
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/mp4");
 
+    v.addEventListener("durationchange", function () {
+      durationChangeCount++;
+    });
+
     fetchWithXHR("bipbop/bipbop2s.mp4", function (arrayBuffer) {
       sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 1395));
-      sb.addEventListener("updateend", function () {
-        updateCount++;
-        if (updateCount == 1) {
-          v.addEventListener("loadedmetadata", function () {
-            v.addEventListener("durationchange", function () {
-              durationChangeCount++;
-            });
-            // Set mediasource duration to 0, so future appendBuffer
-            // will update the mediasource duration
-            // setting ms.duration will fire updatestart/update/updateend
-            // event as per w3c spec followed by a durationchange
-            ms.duration = 0;
-          });
-        } else if (updateCount == 2) {
-          // will fire updatestart/update/updateend
-          // and a durationchange
-          sb.appendBuffer(new Uint8Array(arrayBuffer, 1395));
-        } else if (updateCount == 3) {
+      // Adding the first init segment will fire a durationchange.
+      once(v, "loadedmetadata")
+      .then(function() {
+        ok(true, "got loadedmetadata");
+        // Set mediasource duration to 0, so future appendBuffer
+        // will update the mediasource duration.
+        // Changing the duration will fire a durationchange.
+        ms.duration = 0;
+        sb.appendBuffer(new Uint8Array(arrayBuffer, 1395));
+        // Adding more data will fire durationchange.
+        once(sb, "updateend")
+        .then(function() {
+          ok(true, "got updateend");
           // this will not fire durationchange as new duration == old duration
           ms.endOfStream();
-        }
+        });
       });
     });
   });
   ms.addEventListener("sourceended", function () {
-    is(durationChangeCount, 2, "durationchange not fired as many times as expected");
-    // The bipbop video doesn't start at 0. The old MSE code adjust the
-    // timestamps and ignore the audio track. The new one doesn't.
-    isfuzzy(v.duration, 1.696, 0.166, "Video has correct duration");
+    is(durationChangeCount, 3, "durationchange not fired as many times as expected");
+    is(v.duration, 1.696666, "Video has correct duration");
     SimpleTest.finish();
   });
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/media/mediasource/test/test_TimestampOffset_mp4.html
+++ b/dom/media/mediasource/test/test_TimestampOffset_mp4.html
@@ -61,17 +61,18 @@ runWithMSE(function(ms, el) {
       audiosb.timestampOffset = 3;
     }).then(fetchAndLoad.bind(null, audiosb, 'bipbop/bipbop_audio', ['init'], '.mp4'))
     .then(fetchAndLoad.bind(null, audiosb, 'bipbop/bipbop_audio', range(1, 12), '.m4s'))
     .then(function() {
       is(audiosb.buffered.length, 1, "No audio discontinuity");
       isfuzzy(audiosb.buffered.start(0), 3, eps, "Audio starts at 3");
 
       // Trim the rest of the audio.
-      ms.duration = videosb.buffered.end(0);
+      audiosb.remove(videosb.buffered.end(0), Infinity);
+      videosb.remove(videosb.buffered.end(0), Infinity);
       return Promise.all([audiosb.updating ? once(audiosb, 'updateend') : Promise.resolve(),
                           videosb.updating ? once(videosb, 'updateend') : Promise.resolve()]);
     }).then(function() {
       ms.endOfStream();
       once(el, 'ended').then(SimpleTest.finish.bind(SimpleTest));
       el.play();
     });
   });
--- a/dom/media/mediasource/test/test_TruncatedDuration.html
+++ b/dom/media/mediasource/test/test_TruncatedDuration.html
@@ -29,25 +29,30 @@ function do_seeking(e) {
   v.removeEventListener("seeking", do_seeking, false);
   SimpleTest.finish();
 }
 
 function do_seeked(e) {
   var v = e.target;
   v.removeEventListener("seeked", do_seeked, false);
   var duration = round(v.duration / 3);
-  v._ms.duration = duration
-  v._sb.abort(); // this shouldn't abort updating the duration (bug 1130826).
-  ok(v.seeking, "seeking is true");
-  // test playback position was updated (bug 1130839).
-  is(v.currentTime, duration, "current time was updated");
-  is(v.duration, duration, "element duration was updated");
-  is(v._sb.buffered.length, 1, "One buffered range");
-  // Truncated mediasource duration will cause the video element to seek.
-  v.addEventListener("seeking", do_seeking, false);
+  is(v._sb.updating, false, "sourcebuffer isn't updating");
+  v._sb.remove(duration, Infinity);
+  once(v._sb, "updateend", function() {
+    v._ms.duration = duration
+    // frames aren't truncated, so duration may be slightly more.
+    isfuzzy(v.duration, duration, 1/30, "element duration was updated");
+    v._sb.abort(); // this shouldn't abort updating the duration (bug 1130826).
+    ok(v.seeking, "seeking is true");
+    // test playback position was updated (bug 1130839).
+    is(v.currentTime, v.duration, "current time was updated");
+    is(v._sb.buffered.length, 1, "One buffered range");
+    // Truncated mediasource duration will cause the video element to seek.
+    v.addEventListener("seeking", do_seeking, false);
+  });
 }
 
 function do_loaded(e) {
   var v = e.target;
   v.removeEventListener("loadeddata", do_loaded, false);
   v.currentTime = v.duration / 2;
   is(v.currentTime, v.duration / 2, "current time was updated");
   ok(v.seeking, "seeking is true");
--- a/dom/media/mediasource/test/test_TruncatedDuration_mp4.html
+++ b/dom/media/mediasource/test/test_TruncatedDuration_mp4.html
@@ -7,18 +7,17 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 // This test append data to a mediasource and then seek to half the duration
 // of the video.
-// We then shorten the video to 1/3rd of its original size by modifying the
-// mediasource.duration attribute.
+// We then shorten the video to 1/3rd of its original size.
 // We ensure that the buffered range immediately reflect the truncation
 // and that we've seeked to the new end of the media as per W3C spec and
 // video.currentTime got updated.
 
 SimpleTest.waitForExplicitFinish();
 
 function round(n) {
   return Math.round(n * 1000) / 1000;
@@ -29,37 +28,45 @@ function do_seeking(e) {
   v.removeEventListener("seeking", do_seeking, false);
   SimpleTest.finish();
 }
 
 function do_seeked(e) {
   var v = e.target;
   v.removeEventListener("seeked", do_seeked, false);
   var duration = round(v.duration / 3);
-  v._ms.duration = duration
-  v._sb.abort(); // this shouldn't abort updating the duration (bug 1130826).
-  ok(v.seeking, "seeking is true");
-  // test playback position was updated (bug 1130839).
-  is(v.currentTime, duration, "current time was updated");
-  is(v.duration, duration, "element duration was updated");
-  is(v._sb.buffered.length, 1, "One buffered range");
-  // Truncated mediasource duration will cause the video element to seek.
-  v.addEventListener("seeking", do_seeking, false);
+  is(v._sb.updating, false, "sourcebuffer isn't updating");
+  v._sb.remove(duration, Infinity);
+  once(v._sb, "updateend", function() {
+    v._ms.duration = duration
+    // frames aren't truncated, so duration may be slightly more.
+    isfuzzy(v.duration, duration, 1/30, "element duration was updated");
+    v._sb.abort(); // this shouldn't abort updating the duration (bug 1130826).
+    ok(v.seeking, "seeking is true");
+    // test playback position was updated (bug 1130839).
+    is(v.currentTime, v.duration, "current time was updated");
+    is(v._sb.buffered.length, 1, "One buffered range");
+    // Truncated mediasource duration will cause the video element to seek.
+    v.addEventListener("seeking", do_seeking, false);
+  });
 }
 
 function do_loaded(e) {
   var v = e.target;
   v.removeEventListener("loadeddata", do_loaded, false);
   // mp4 metadata states 10s when we only have 1.6s worth of video.
-  v._ms.duration = v._sb.buffered.end(0);
-  is(v.duration, v._ms.duration, "current time updated with mediasource duration");
-  v.currentTime = v.duration / 2;
-  is(v.currentTime, v.duration / 2, "current time was updated");
-  ok(v.seeking, "seeking is true");
-  v.addEventListener("seeked", do_seeked, false);
+  v._sb.remove(v._sb.buffered.end(0), Infinity);
+  once(v._sb, "updateend", function() {
+    v._ms.duration = v._sb.buffered.end(0);
+    is(v.duration, v._ms.duration, "current time updated with mediasource duration");
+    v.currentTime = v.duration / 2;
+    is(v.currentTime, v.duration / 2, "current time was updated");
+    ok(v.seeking, "seeking is true");
+    v.addEventListener("seeked", do_seeked, false);
+  });
 }
 
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/mp4");
     v._sb = sb;
     v._ms = ms;
 
--- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
@@ -79,22 +79,22 @@ public:
       return;
     }
 
     if (mIsShutdown) {
       NS_WARNING("EME decrypted sample arrived after shutdown");
       return;
     }
 
-    if (aDecrypted.mStatus == GMPNoKeyErr) {
+    if (aDecrypted.mStatus == NoKeyErr) {
       // Key became unusable after we sent the sample to CDM to decrypt.
       // Call Input() again, so that the sample is enqueued for decryption
       // if the key becomes usable again.
       Input(aDecrypted.mSample);
-    } else if (GMP_FAILED(aDecrypted.mStatus)) {
+    } else if (aDecrypted.mStatus != Ok) {
       if (mCallback) {
         mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
       }
     } else {
       MOZ_ASSERT(!mIsShutdown);
       // The Adobe GMP AAC decoder gets confused if we pass it non-encrypted
       // samples with valid crypto data. So clear the crypto data, since the
       // sample should be decrypted now anyway. If we don't do this and we're
--- a/dom/media/webaudio/AudioNode.cpp
+++ b/dom/media/webaudio/AudioNode.cpp
@@ -286,24 +286,19 @@ void
 AudioNode::SendChannelMixingParametersToStream()
 {
   if (mStream) {
     mStream->SetChannelMixingParameters(mChannelCount, mChannelCountMode,
                                         mChannelInterpretation);
   }
 }
 
-void
-AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
+bool
+AudioNode::DisconnectFromOutputIfConnected(AudioNode& aDestination, uint32_t aOutputIndex, uint32_t aInputIndex)
 {
-  if (aOutput >= NumberOfOutputs()) {
-    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
-    return;
-  }
-
   WEB_AUDIO_API_LOG("%f: %s %u Disconnect()", Context()->CurrentTime(),
                     NodeType(), Id());
 
   // An upstream node may be starting to play on the graph thread, and the
   // engine for a downstream node may be sending a PlayingRefChangeHandler
   // ADDREF message to this (main) thread.  Wait for a round trip before
   // releasing nodes, to give engines receiving sound now time to keep their
   // nodes alive.
@@ -317,60 +312,233 @@ AudioNode::Disconnect(uint32_t aOutput, 
     {
       mNode = nullptr;
       return NS_OK;
     }
   private:
     RefPtr<AudioNode> mNode;
   };
 
-  for (int32_t i = mOutputNodes.Length() - 1; i >= 0; --i) {
-    AudioNode* dest = mOutputNodes[i];
-    for (int32_t j = dest->mInputNodes.Length() - 1; j >= 0; --j) {
-      InputNode& input = dest->mInputNodes[j];
-      if (input.mInputNode == this && input.mOutputPort == aOutput) {
-        // Destroying the InputNode here sends a message to the graph thread
-        // to disconnect the streams, which should be sent before the
-        // RunAfterPendingUpdates() call below.
-        dest->mInputNodes.RemoveElementAt(j);
-        // Remove one instance of 'dest' from mOutputNodes. There could be
-        // others, and it's not correct to remove them all since some of them
-        // could be for different output ports.
-        RefPtr<AudioNode> output = mOutputNodes[i].forget();
-        mOutputNodes.RemoveElementAt(i);
-        output->NotifyInputsChanged();
-        if (mStream) {
-          RefPtr<nsIRunnable> runnable = new RunnableRelease(output.forget());
-          mStream->RunAfterPendingUpdates(runnable.forget());
-        }
-        break;
+  InputNode& input = aDestination.mInputNodes[aInputIndex];
+  if (input.mInputNode != this) {
+    return false;
+  }
+  // RunAfterPendingUpdates() call below.
+  aDestination.mInputNodes.RemoveElementAt(aInputIndex);
+  // Remove one instance of 'dest' from mOutputNodes. There could be
+  // others, and it's not correct to remove them all since some of them
+  // could be for different output ports.
+  RefPtr<AudioNode> output = mOutputNodes[aOutputIndex].forget();
+  mOutputNodes.RemoveElementAt(aOutputIndex);
+  if (mStream) {
+    nsCOMPtr<nsIRunnable> runnable = new RunnableRelease(output.forget());
+    mStream->RunAfterPendingUpdates(runnable.forget());
+  }
+  return true;
+}
+
+bool
+AudioNode::DisconnectFromParamIfConnected(AudioParam& aDestination, uint32_t aOutputIndex, uint32_t aInputIndex)
+{
+  MOZ_ASSERT(aOutputIndex < mOutputParams.Length());
+  MOZ_ASSERT(aInputIndex < aDestination.InputNodes().Length());
+
+  const InputNode& input = aDestination.InputNodes()[aInputIndex];
+  if (input.mInputNode != this) {
+    return false;
+  }
+  aDestination.RemoveInputNode(aInputIndex);
+  // Remove one instance of 'dest' from mOutputParams. There could be
+  // others, and it's not correct to remove them all since some of them
+  // could be for different output ports.
+  mOutputParams.RemoveElementAt(aOutputIndex);
+  return true;
+}
+
+void
+AudioNode::Disconnect(ErrorResult& aRv)
+{
+  for (int32_t outputIndex = mOutputNodes.Length() - 1; outputIndex >= 0; --outputIndex) {
+    AudioNode* dest = mOutputNodes[outputIndex];
+    for (int32_t inputIndex = dest->mInputNodes.Length() - 1; inputIndex >= 0; --inputIndex) {
+      DisconnectFromOutputIfConnected(*dest, outputIndex, inputIndex);
+    }
+  }
+
+  for (int32_t outputIndex = mOutputParams.Length() - 1; outputIndex >= 0; --outputIndex) {
+    AudioParam* dest = mOutputParams[outputIndex];
+    for (int32_t inputIndex = dest->InputNodes().Length() - 1; inputIndex >= 0; --inputIndex) {
+      DisconnectFromParamIfConnected(*dest, outputIndex, inputIndex);
+    }
+  }
+
+  // This disconnection may have disconnected a panner and a source.
+  Context()->UpdatePannerSource();
+}
+
+void
+AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv)
+{
+  if (aOutput >= NumberOfOutputs()) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  for (int32_t outputIndex = mOutputNodes.Length() - 1; outputIndex >= 0; --outputIndex) {
+    AudioNode* dest = mOutputNodes[outputIndex];
+    for (int32_t inputIndex = dest->mInputNodes.Length() - 1; inputIndex >= 0; --inputIndex) {
+      InputNode& input = dest->mInputNodes[inputIndex];
+      if (input.mOutputPort == aOutput) {
+        DisconnectFromOutputIfConnected(*dest, outputIndex, inputIndex);
       }
     }
   }
 
-  for (int32_t i = mOutputParams.Length() - 1; i >= 0; --i) {
-    AudioParam* dest = mOutputParams[i];
-    for (int32_t j = dest->InputNodes().Length() - 1; j >= 0; --j) {
-      const InputNode& input = dest->InputNodes()[j];
-      if (input.mInputNode == this && input.mOutputPort == aOutput) {
-        dest->RemoveInputNode(j);
-        // Remove one instance of 'dest' from mOutputParams. There could be
-        // others, and it's not correct to remove them all since some of them
-        // could be for different output ports.
-        mOutputParams.RemoveElementAt(i);
-        break;
+  for (int32_t outputIndex = mOutputParams.Length() - 1; outputIndex >= 0; --outputIndex) {
+    AudioParam* dest = mOutputParams[outputIndex];
+    for (int32_t inputIndex = dest->InputNodes().Length() - 1; inputIndex >= 0; --inputIndex) {
+      const InputNode& input = dest->InputNodes()[inputIndex];
+      if (input.mOutputPort == aOutput) {
+        DisconnectFromParamIfConnected(*dest, outputIndex, inputIndex);
       }
     }
   }
 
   // This disconnection may have disconnected a panner and a source.
   Context()->UpdatePannerSource();
 }
 
 void
+AudioNode::Disconnect(AudioNode& aDestination, ErrorResult& aRv)
+{
+  bool wasConnected = false;
+
+  size_t outputIndex = mOutputNodes.IndexOf(&aDestination);
+  if (outputIndex == nsTArray<InputNode>::NoIndex) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    return;
+  }
+  for (int32_t inputIndex = aDestination.mInputNodes.Length() - 1; inputIndex >= 0; --inputIndex) {
+    wasConnected |= DisconnectFromOutputIfConnected(aDestination, outputIndex, inputIndex);
+  }
+
+  if (!wasConnected) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    return;
+  }
+
+  // This disconnection may have disconnected a panner and a source.
+  Context()->UpdatePannerSource();
+}
+
+void
+AudioNode::Disconnect(AudioNode& aDestination, uint32_t aOutput, ErrorResult& aRv)