merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 12 Apr 2016 13:49:51 +0200
changeset 330652 49d7fb650c9dde7cf6e4b2c7aa578a4a11e83f83
parent 330518 7116915548728dd89dd80455daee47f1adff2e0c (current diff)
parent 330651 52fd577f808f21db74f8ad98c3cc82a20bae486e (diff)
child 330678 cb2b8b7bc47f92d51420a1c942042d27a9c9ae39
child 330740 c6e90a3857c019e418c113f31dec86ca43e60243
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone48.0a1
first release with
nightly linux32
49d7fb650c9d / 48.0a1 / 20160412050029 / files
nightly linux64
49d7fb650c9d / 48.0a1 / 20160412050029 / files
nightly mac
49d7fb650c9d / 48.0a1 / 20160412050029 / files
nightly win32
49d7fb650c9d / 48.0a1 / 20160412050029 / files
nightly win64
49d7fb650c9d / 48.0a1 / 20160412050029 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
accessible/base/AccCollector.cpp
accessible/base/AccCollector.h
dom/base/test/test_bug1011748.html
ipc/chromium/src/base/non_thread_safe.cc
ipc/chromium/src/base/non_thread_safe.h
netwerk/base/nsIPackagedAppChannelListener.idl
netwerk/test/mochitests/signed_web_packaged_app_random.sjs
netwerk/test/mochitests/test_about_blank_to_signed_web_packaged_app.html
netwerk/test/mochitests/test_origin_attributes_conversion.html
netwerk/test/mochitests/test_signed_to_signed_web_packaged_app.html
netwerk/test/mochitests/test_signed_web_packaged_app.html
netwerk/test/mochitests/test_signed_web_packaged_app_origin.html
testing/mozbase/manifestparser/tests/test_default_skipif.py
testing/web-platform/harness/wptrunner/browsers/webdriver.py
testing/web-platform/harness/wptrunner/executors/webdriver.py
testing/web-platform/meta/compat/webkit-text-fill-color-property-001a.html.ini
testing/web-platform/meta/compat/webkit-text-fill-color-property-001b.html.ini
testing/web-platform/meta/compat/webkit-text-fill-color-property-001c.html.ini
testing/web-platform/meta/compat/webkit-text-fill-color-property-001d.html.ini
testing/web-platform/meta/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/api-present.html.ini
testing/web-platform/meta/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/deny.html.ini
testing/web-platform/meta/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/empty-option-param.html.ini
testing/web-platform/meta/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/getusermedia-impossible-constraint.html.ini
testing/web-platform/meta/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/getusermedia-optional-constraint.html.ini
testing/web-platform/meta/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/getusermedia-trivial-constraint.html.ini
testing/web-platform/meta/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/unknownkey-option-param.html.ini
testing/web-platform/meta/mediacapture-streams/stream-api/introduction/disabled-audio-silence.html.ini
testing/web-platform/meta/mediacapture-streams/stream-api/introduction/disabled-video-black.html.ini
testing/web-platform/meta/mediacapture-streams/stream-api/mediastream/audio.html.ini
testing/web-platform/meta/mediacapture-streams/stream-api/mediastream/mediastream-addtrack.html.ini
testing/web-platform/meta/mediacapture-streams/stream-api/mediastream/mediastream-finished-add.html.ini
testing/web-platform/meta/mediacapture-streams/stream-api/mediastream/mediastream-gettrackid.html.ini
testing/web-platform/meta/mediacapture-streams/stream-api/mediastream/mediastream-idl.html.ini
testing/web-platform/meta/mediacapture-streams/stream-api/mediastream/mediastream-removetrack.html.ini
testing/web-platform/meta/mediacapture-streams/stream-api/mediastream/video.html.ini
testing/web-platform/meta/mediacapture-streams/stream-api/mediastreamtrack/mediastreamtrack-end.html.ini
testing/web-platform/meta/mediacapture-streams/stream-api/mediastreamtrack/mediastreamtrack-id.html.ini
testing/web-platform/meta/mediacapture-streams/stream-api/mediastreamtrack/mediastreamtrack-init.html.ini
testing/web-platform/meta/mediacapture-streams/stream-api/video-and-audio-tracks/audiostreamtrack.html.ini
testing/web-platform/meta/mediacapture-streams/stream-api/video-and-audio-tracks/videostreamtrack.html.ini
testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/extensions-to-event-interface/event-path-002.html.ini
testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/the-content-html-element/test-001.html.ini
testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/the-content-html-element/test-002.html.ini
testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/the-content-html-element/test-003.html.ini
testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/the-content-html-element/test-005.html.ini
testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/the-shadow-html-element/test-001.html.ini
testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/the-shadow-html-element/test-002.html.ini
testing/web-platform/meta/shadow-dom/untriaged/elements-and-dom-objects/the-shadow-html-element/test-004.html.ini
testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/content-pseudo-element/test-001.html.ini
testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/content-pseudo-element/test-002.html.ini
testing/web-platform/meta/shadow-dom/untriaged/shadow-trees/custom-pseudo-elements/test-001.html.ini
testing/web-platform/meta/shadow-dom/untriaged/styles/deep-combinator/deep-combinator-001.html.ini
testing/web-platform/meta/shadow-dom/untriaged/styles/shadow-pseudoelement/shadow-pseudoelement-001.html.ini
testing/web-platform/tests/mediacapture-streams/mediastreams-as-media-elements/video-assignment-manual.html
testing/web-platform/tests/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/api-present.html
testing/web-platform/tests/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/deny.html
testing/web-platform/tests/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/empty-option-param.html
testing/web-platform/tests/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/getusermedia-impossible-constraint.html
testing/web-platform/tests/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/getusermedia-optional-constraint.html
testing/web-platform/tests/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/getusermedia-trivial-constraint.html
testing/web-platform/tests/mediacapture-streams/obtaining-local-multimedia-content/navigatorusermedia/unknownkey-option-param.html
testing/web-platform/tests/mediacapture-streams/stream-api/introduction/disabled-audio-silence.html
testing/web-platform/tests/mediacapture-streams/stream-api/introduction/disabled-video-black.html
testing/web-platform/tests/mediacapture-streams/stream-api/mediastream/audio.html
testing/web-platform/tests/mediacapture-streams/stream-api/mediastream/mediastream-addtrack.html
testing/web-platform/tests/mediacapture-streams/stream-api/mediastream/mediastream-finished-add.html
testing/web-platform/tests/mediacapture-streams/stream-api/mediastream/mediastream-gettrackid.html
testing/web-platform/tests/mediacapture-streams/stream-api/mediastream/mediastream-id-manual.html
testing/web-platform/tests/mediacapture-streams/stream-api/mediastream/mediastream-idl.html
testing/web-platform/tests/mediacapture-streams/stream-api/mediastream/mediastream-removetrack.html
testing/web-platform/tests/mediacapture-streams/stream-api/mediastream/video.html
testing/web-platform/tests/mediacapture-streams/stream-api/mediastreamtrack/mediastreamtrack-end.html
testing/web-platform/tests/mediacapture-streams/stream-api/mediastreamtrack/mediastreamtrack-id.html
testing/web-platform/tests/mediacapture-streams/stream-api/mediastreamtrack/mediastreamtrack-init.html
testing/web-platform/tests/mediacapture-streams/stream-api/video-and-audio-tracks/audiostreamtrack.html
testing/web-platform/tests/mediacapture-streams/stream-api/video-and-audio-tracks/videostreamtrack.html
testing/web-platform/tests/service-workers/service-workers/resources/test-helpers.js
testing/web-platform/tests/shadow-dom/untriaged/elements-and-dom-objects/extensions-to-element-interface/attributes/test-001.html
testing/web-platform/tests/shadow-dom/untriaged/elements-and-dom-objects/extensions-to-element-interface/attributes/test-005.html
testing/web-platform/tests/shadow-dom/untriaged/elements-and-dom-objects/extensions-to-element-interface/methods/elements-001.html
testing/web-platform/tests/shadow-dom/untriaged/elements-and-dom-objects/extensions-to-element-interface/methods/non-element-nodes-001.html
testing/web-platform/tests/shadow-dom/untriaged/elements-and-dom-objects/extensions-to-event-interface/event-path-002.html
testing/web-platform/tests/shadow-dom/untriaged/elements-and-dom-objects/the-content-html-element/test-001.html
testing/web-platform/tests/shadow-dom/untriaged/elements-and-dom-objects/the-content-html-element/test-002.html
testing/web-platform/tests/shadow-dom/untriaged/elements-and-dom-objects/the-content-html-element/test-003.html
testing/web-platform/tests/shadow-dom/untriaged/elements-and-dom-objects/the-content-html-element/test-005.html
testing/web-platform/tests/shadow-dom/untriaged/elements-and-dom-objects/the-content-html-element/test-006.html
testing/web-platform/tests/shadow-dom/untriaged/elements-and-dom-objects/the-shadow-html-element/test-001.html
testing/web-platform/tests/shadow-dom/untriaged/elements-and-dom-objects/the-shadow-html-element/test-002.html
testing/web-platform/tests/shadow-dom/untriaged/elements-and-dom-objects/the-shadow-html-element/test-004.html
testing/web-platform/tests/shadow-dom/untriaged/shadow-trees/content-pseudo-element/test-001.html
testing/web-platform/tests/shadow-dom/untriaged/shadow-trees/content-pseudo-element/test-002.html
testing/web-platform/tests/shadow-dom/untriaged/shadow-trees/custom-pseudo-elements/test-001.html
testing/web-platform/tests/shadow-dom/untriaged/shadow-trees/satisfying-matching-criteria/test-001.html
testing/web-platform/tests/shadow-dom/untriaged/shadow-trees/satisfying-matching-criteria/test-002.html
testing/web-platform/tests/shadow-dom/untriaged/shadow-trees/satisfying-matching-criteria/test-003.html
testing/web-platform/tests/shadow-dom/untriaged/shadow-trees/satisfying-matching-criteria/test-004.html
testing/web-platform/tests/shadow-dom/untriaged/shadow-trees/satisfying-matching-criteria/test-005.html
testing/web-platform/tests/shadow-dom/untriaged/styles/deep-combinator/deep-combinator-001.html
testing/web-platform/tests/shadow-dom/untriaged/styles/shadow-pseudoelement/shadow-pseudoelement-001.html
testing/web-platform/tests/shadow-dom/untriaged/testcommon.js
testing/web-platform/tests/tools/webdriver/webdriver/alert.py
testing/web-platform/tests/tools/webdriver/webdriver/capabilities.py
testing/web-platform/tests/tools/webdriver/webdriver/command.py
testing/web-platform/tests/tools/webdriver/webdriver/driver.py
testing/web-platform/tests/tools/webdriver/webdriver/exceptions.py
testing/web-platform/tests/tools/webdriver/webdriver/keys.py
testing/web-platform/tests/tools/webdriver/webdriver/searchcontext.py
testing/web-platform/tests/tools/webdriver/webdriver/wait.py
testing/web-platform/tests/tools/webdriver/webdriver/webelement.py
testing/web-platform/tests/webdriver/base_test.py
testing/web-platform/tests/webdriver/command_contexts/__init__.py
testing/web-platform/tests/webdriver/command_contexts/open_and_close_window_test.py
testing/web-platform/tests/webdriver/command_contexts/res/first-page.html
testing/web-platform/tests/webdriver/command_contexts/res/other-page.html
testing/web-platform/tests/webdriver/command_contexts/window_handle_test.py
testing/web-platform/tests/webdriver/command_contexts/window_size_test.py
testing/web-platform/tests/webdriver/cookie/__init__.py
testing/web-platform/tests/webdriver/cookie/cookie_test.py
testing/web-platform/tests/webdriver/cookie/res/cookie_container.html
testing/web-platform/tests/webdriver/ecmascript/ecmascript_test.py
testing/web-platform/tests/webdriver/ecmascript/res/ecmascript_test.html
testing/web-platform/tests/webdriver/element_location/__init__.py
testing/web-platform/tests/webdriver/element_location/element_location_test.py
testing/web-platform/tests/webdriver/element_location/res/elements.html
testing/web-platform/tests/webdriver/element_state/__init__.py
testing/web-platform/tests/webdriver/element_state/method_test.py
testing/web-platform/tests/webdriver/element_state/properties.py
testing/web-platform/tests/webdriver/element_state/res/0x0-pixels.html
testing/web-platform/tests/webdriver/element_state/res/1x1-pixels.html
testing/web-platform/tests/webdriver/element_state/res/a-with-href-attribute.html
testing/web-platform/tests/webdriver/element_state/res/absolute-children-ancestor-hidden-overflow.html
testing/web-platform/tests/webdriver/element_state/res/body_empty.html
testing/web-platform/tests/webdriver/element_state/res/body_implicit.html
testing/web-platform/tests/webdriver/element_state/res/body_overflow_hidden.html
testing/web-platform/tests/webdriver/element_state/res/body_visibility_hidden.html
testing/web-platform/tests/webdriver/element_state/res/display-block.html
testing/web-platform/tests/webdriver/element_state/res/display-none-child-link.html
testing/web-platform/tests/webdriver/element_state/res/display-none-child-paragraph.html
testing/web-platform/tests/webdriver/element_state/res/display-none-child.html
testing/web-platform/tests/webdriver/element_state/res/display-none-dynamic.html
testing/web-platform/tests/webdriver/element_state/res/display-none-parent-presedence-visibility.html
testing/web-platform/tests/webdriver/element_state/res/display-none-parent-presedence.html
testing/web-platform/tests/webdriver/element_state/res/display-none.html
testing/web-platform/tests/webdriver/element_state/res/element-dynamically-moved-outside-viewport.html
testing/web-platform/tests/webdriver/element_state/res/element-hidden-by-other-element.html
testing/web-platform/tests/webdriver/element_state/res/element-hidden-by-z-index.html
testing/web-platform/tests/webdriver/element_state/res/element-moved-behind-other-element-by-transform.html
testing/web-platform/tests/webdriver/element_state/res/element-moved-outside-viewport-by-transform.html
testing/web-platform/tests/webdriver/element_state/res/element-outside-viewport.html
testing/web-platform/tests/webdriver/element_state/res/element-partially-hidden-by-other-element.html
testing/web-platform/tests/webdriver/element_state/res/element-selected.html
testing/web-platform/tests/webdriver/element_state/res/element-with-color-style-attribute.html
testing/web-platform/tests/webdriver/element_state/res/element-with-custom-attribute.html
testing/web-platform/tests/webdriver/element_state/res/element-with-id-attribute.html
testing/web-platform/tests/webdriver/element_state/res/element-with-same-color-as-background.html
testing/web-platform/tests/webdriver/element_state/res/element-with-same-color-as-parent-background.html
testing/web-platform/tests/webdriver/element_state/res/element-with-style-attribute.html
testing/web-platform/tests/webdriver/element_state/res/element-without-attribute.html
testing/web-platform/tests/webdriver/element_state/res/elements_text.html
testing/web-platform/tests/webdriver/element_state/res/get-element-attribute-extended.html
testing/web-platform/tests/webdriver/element_state/res/hidden-input-type-checkbox-untogglable.html
testing/web-platform/tests/webdriver/element_state/res/hidden-input-type-text-writing.html
testing/web-platform/tests/webdriver/element_state/res/hidden.html
testing/web-platform/tests/webdriver/element_state/res/img-with-src-attribute.html
testing/web-platform/tests/webdriver/element_state/res/input-morphs-into-hidden.html
testing/web-platform/tests/webdriver/element_state/res/input-type-hidden-unclickable.html
testing/web-platform/tests/webdriver/element_state/res/input-type-hidden.html
testing/web-platform/tests/webdriver/element_state/res/input-with-checked-attribute.html
testing/web-platform/tests/webdriver/element_state/res/input-without-checked-attribute.html
testing/web-platform/tests/webdriver/element_state/res/option-with-value-attribute.html
testing/web-platform/tests/webdriver/element_state/res/option-without-value-attribute.html
testing/web-platform/tests/webdriver/element_state/res/text-with-matching-color-and-background.html
testing/web-platform/tests/webdriver/element_state/res/text-with-same-color-as-background.html
testing/web-platform/tests/webdriver/element_state/res/text-with-same-color-as-parent-background.html
testing/web-platform/tests/webdriver/element_state/res/visibility-child-link.html
testing/web-platform/tests/webdriver/element_state/res/visibility-child-paragraph.html
testing/web-platform/tests/webdriver/element_state/res/visibility-child-presedence.html
testing/web-platform/tests/webdriver/element_state/res/visibility-child.html
testing/web-platform/tests/webdriver/element_state/res/visibility-hidden.html
testing/web-platform/tests/webdriver/element_state/res/visibility-visible.html
testing/web-platform/tests/webdriver/element_state/res/x-auto-y-hidden.html
testing/web-platform/tests/webdriver/element_state/res/x-hidden-y-auto.html
testing/web-platform/tests/webdriver/element_state/res/x-hidden-y-hidden.html
testing/web-platform/tests/webdriver/element_state/res/x-hidden-y-scroll.html
testing/web-platform/tests/webdriver/element_state/res/x-scroll-y-hidden.html
testing/web-platform/tests/webdriver/element_state/res/zero-sized-element-with-sizable-decendant.html
testing/web-platform/tests/webdriver/element_state/selected_test.py
testing/web-platform/tests/webdriver/element_state/visibility_test.py
testing/web-platform/tests/webdriver/javascript/__init__.py
testing/web-platform/tests/webdriver/javascript/execute_script_test.py
testing/web-platform/tests/webdriver/javascript/res/execute_script_test.html
testing/web-platform/tests/webdriver/javascript/res/return_array_of_dom_elements.html
testing/web-platform/tests/webdriver/javascript/res/return_document_body.html
testing/web-platform/tests/webdriver/javascript/res/return_node_list.html
testing/web-platform/tests/webdriver/modal/__init__.py
testing/web-platform/tests/webdriver/modal/alerts_quit_test.py
testing/web-platform/tests/webdriver/modal/alerts_test.py
testing/web-platform/tests/webdriver/modal/res/alerts.html
testing/web-platform/tests/webdriver/navigation/__init__.py
testing/web-platform/tests/webdriver/navigation/auth_tests.py
testing/web-platform/tests/webdriver/navigation/forward.py
testing/web-platform/tests/webdriver/navigation/forwardToNothing.py
testing/web-platform/tests/webdriver/navigation/get_from_http_test.py
testing/web-platform/tests/webdriver/navigation/invalid_cert_test.py
testing/web-platform/tests/webdriver/navigation/refresh-page.py
testing/web-platform/tests/webdriver/navigation/refresh_page.py
testing/web-platform/tests/webdriver/navigation/res/1s-meta-redirect.html
testing/web-platform/tests/webdriver/navigation/res/60s-meta-redirect.html
testing/web-platform/tests/webdriver/navigation/res/authenticated.html
testing/web-platform/tests/webdriver/navigation/res/empty.html
testing/web-platform/tests/webdriver/navigation/res/forwardNext.html
testing/web-platform/tests/webdriver/navigation/res/forwardStart.html
testing/web-platform/tests/webdriver/navigation/res/fragment.html
testing/web-platform/tests/webdriver/navigation/res/instant-meta-redirect.html
testing/web-platform/tests/webdriver/navigation/res/refreshPageDynamic.html
testing/web-platform/tests/webdriver/navigation/res/refreshPageStatic.html
testing/web-platform/tests/webdriver/navigation/res/self-signed.key
testing/web-platform/tests/webdriver/network.py
testing/web-platform/tests/webdriver/runtests.py
testing/web-platform/tests/webdriver/runtests_p0.py
testing/web-platform/tests/webdriver/screenshot/__init__.py
testing/web-platform/tests/webdriver/screenshot/res/screenshot.html
testing/web-platform/tests/webdriver/screenshot/take_screenshot.py
testing/web-platform/tests/webdriver/timeouts/__init__.py
testing/web-platform/tests/webdriver/timeouts/implicit_waits_tests.py
testing/web-platform/tests/webdriver/timeouts/page_load_timeouts_tests.py
testing/web-platform/tests/webdriver/timeouts/res/implicit_waits_tests.html
testing/web-platform/tests/webdriver/timeouts/res/page_load_timeouts_tests.html
testing/web-platform/tests/webdriver/user_input/__init__.py
testing/web-platform/tests/webdriver/user_input/clear_test.py
testing/web-platform/tests/webdriver/user_input/click_test.py
testing/web-platform/tests/webdriver/user_input/res/click.html
testing/web-platform/tests/webdriver/user_input/res/element_clear_contenteditable_page.html
testing/web-platform/tests/webdriver/user_input/res/element_clear_disabled_input_page.html
testing/web-platform/tests/webdriver/user_input/res/element_clear_disabled_textarea_page.html
testing/web-platform/tests/webdriver/user_input/res/element_clear_readonly_input_page.html
testing/web-platform/tests/webdriver/user_input/res/element_clear_readonly_textarea_page.html
testing/web-platform/tests/webdriver/user_input/res/element_clear_writable_input_page.html
testing/web-platform/tests/webdriver/user_input/res/element_clear_writable_textarea_page.html
testing/web-platform/tests/webdriver/user_input/res/text-form-landing.html
testing/web-platform/tests/webdriver/user_input/res/text-form.html
testing/web-platform/tests/webdriver/user_input/sendkeys_test.py
testing/web-platform/tests/webdriver/webdriver.cfg
testing/web-platform/tests/webdriver/windows/__init__.py
testing/web-platform/tests/webdriver/windows/res/win1.html
testing/web-platform/tests/webdriver/windows/res/win2.html
testing/web-platform/tests/webdriver/windows/res/win3.html
testing/web-platform/tests/webdriver/windows/res/win4.html
testing/web-platform/tests/webdriver/windows/res/win5.html
testing/web-platform/tests/webdriver/windows/tabbing.py
testing/web-platform/tests/webdriver/windows/window_manipulation.py
xpcom/threads/nsICancelableRunnable.idl
deleted file mode 100644
--- a/accessible/base/AccCollector.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "AccCollector.h"
-
-#include "Accessible.h"
-
-using namespace mozilla::a11y;
-
-////////////////////////////////////////////////////////////////////////////////
-// nsAccCollector
-////////////////////////////////////////////////////////////////////////////////
-
-AccCollector::
-  AccCollector(Accessible* aRoot, filters::FilterFuncPtr aFilterFunc) :
-  mFilterFunc(aFilterFunc), mRoot(aRoot), mRootChildIdx(0)
-{
-}
-
-AccCollector::~AccCollector()
-{
-}
-
-uint32_t
-AccCollector::Count()
-{
-  EnsureNGetIndex(nullptr);
-  return mObjects.Length();
-}
-
-Accessible*
-AccCollector::GetAccessibleAt(uint32_t aIndex)
-{
-  Accessible* accessible = mObjects.SafeElementAt(aIndex, nullptr);
-  if (accessible)
-    return accessible;
-
-  return EnsureNGetObject(aIndex);
-}
-
-int32_t
-AccCollector::GetIndexAt(Accessible* aAccessible)
-{
-  int32_t index = mObjects.IndexOf(aAccessible);
-  if (index != -1)
-    return index;
-
-  return EnsureNGetIndex(aAccessible);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// nsAccCollector protected
-
-Accessible*
-AccCollector::EnsureNGetObject(uint32_t aIndex)
-{
-  uint32_t childCount = mRoot->ChildCount();
-  while (mRootChildIdx < childCount) {
-    Accessible* child = mRoot->GetChildAt(mRootChildIdx++);
-    if (!(mFilterFunc(child) & filters::eMatch))
-      continue;
-
-    AppendObject(child);
-    if (mObjects.Length() - 1 == aIndex)
-      return mObjects[aIndex];
-  }
-
-  return nullptr;
-}
-
-int32_t
-AccCollector::EnsureNGetIndex(Accessible* aAccessible)
-{
-  uint32_t childCount = mRoot->ChildCount();
-  while (mRootChildIdx < childCount) {
-    Accessible* child = mRoot->GetChildAt(mRootChildIdx++);
-    if (!(mFilterFunc(child) & filters::eMatch))
-      continue;
-
-    AppendObject(child);
-    if (child == aAccessible)
-      return mObjects.Length() - 1;
-  }
-
-  return -1;
-}
-
-void
-AccCollector::AppendObject(Accessible* aAccessible)
-{
-  mObjects.AppendElement(aAccessible);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// EmbeddedObjCollector
-////////////////////////////////////////////////////////////////////////////////
-
-int32_t
-EmbeddedObjCollector::GetIndexAt(Accessible* aAccessible)
-{
-  if (aAccessible->mParent != mRoot)
-    return -1;
-
-  MOZ_ASSERT(!aAccessible->IsProxy());
-  if (aAccessible->mInt.mIndexOfEmbeddedChild != -1)
-    return aAccessible->mInt.mIndexOfEmbeddedChild;
-
-  return mFilterFunc(aAccessible) & filters::eMatch ?
-    EnsureNGetIndex(aAccessible) : -1;
-}
-
-void
-EmbeddedObjCollector::AppendObject(Accessible* aAccessible)
-{
-  MOZ_ASSERT(!aAccessible->IsProxy());
-  aAccessible->mInt.mIndexOfEmbeddedChild = mObjects.Length();
-  mObjects.AppendElement(aAccessible);
-}
deleted file mode 100644
--- a/accessible/base/AccCollector.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_a11y_AccCollector_h__
-#define mozilla_a11y_AccCollector_h__
-
-#include "Filters.h"
-
-#include "nsTArray.h"
-
-namespace mozilla {
-namespace a11y {
-
-class Accessible;
-
-/**
- * Collect accessible children complying with filter function. Provides quick
- * access to accessible by index.
- */
-class AccCollector
-{
-public:
-  AccCollector(Accessible* aRoot, filters::FilterFuncPtr aFilterFunc);
-  virtual ~AccCollector();
-
-  /**
-   * Return accessible count within the collection.
-   */
-  uint32_t Count();
-
-  /**
-   * Return an accessible from the collection at the given index.
-   */
-  Accessible* GetAccessibleAt(uint32_t aIndex);
-
-  /**
-   * Return index of the given accessible within the collection.
-   */
-  virtual int32_t GetIndexAt(Accessible* aAccessible);
-
-protected:
-  /**
-   * Ensure accessible at the given index is stored and return it.
-   */
-  Accessible* EnsureNGetObject(uint32_t aIndex);
-
-  /**
-   * Ensure index for the given accessible is stored and return it.
-   */
-  int32_t EnsureNGetIndex(Accessible* aAccessible);
-
-  /**
-   * Append the object to collection.
-   */
-  virtual void AppendObject(Accessible* aAccessible);
-
-  filters::FilterFuncPtr mFilterFunc;
-  Accessible* mRoot;
-  uint32_t mRootChildIdx;
-
-  nsTArray<Accessible*> mObjects;
-
-private:
-  AccCollector();
-  AccCollector(const AccCollector&);
-  AccCollector& operator =(const AccCollector&);
-};
-
-/**
- * Collect embedded objects. Provide quick access to accessible by index and
- * vice versa.
- */
-class EmbeddedObjCollector final : public AccCollector
-{
-public:
-  virtual ~EmbeddedObjCollector() { }
-
-public:
-  virtual int32_t GetIndexAt(Accessible* aAccessible) override;
-
-protected:
-  // Make sure it's used by Accessible class only.
-  explicit EmbeddedObjCollector(Accessible* aRoot) :
-    AccCollector(aRoot, filters::GetEmbeddedObject) { }
-
-  virtual void AppendObject(Accessible* aAccessible) override;
-
-  friend class Accessible;
-};
-
-} // namespace a11y
-} // namespace mozilla
-
-#endif
new file mode 100644
--- /dev/null
+++ b/accessible/base/EmbeddedObjCollector.cpp
@@ -0,0 +1,81 @@
+/* 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 "EmbeddedObjCollector.h"
+
+#include "Accessible.h"
+
+using namespace mozilla::a11y;
+
+uint32_t
+EmbeddedObjCollector::Count()
+{
+  EnsureNGetIndex(nullptr);
+  return mObjects.Length();
+}
+
+Accessible*
+EmbeddedObjCollector::GetAccessibleAt(uint32_t aIndex)
+{
+  Accessible* accessible = mObjects.SafeElementAt(aIndex, nullptr);
+  if (accessible)
+    return accessible;
+
+  return EnsureNGetObject(aIndex);
+}
+
+Accessible*
+EmbeddedObjCollector::EnsureNGetObject(uint32_t aIndex)
+{
+  uint32_t childCount = mRoot->ChildCount();
+  while (mRootChildIdx < childCount) {
+    Accessible* child = mRoot->GetChildAt(mRootChildIdx++);
+    if (child->IsText())
+      continue;
+
+    AppendObject(child);
+    if (mObjects.Length() - 1 == aIndex)
+      return mObjects[aIndex];
+  }
+
+  return nullptr;
+}
+
+int32_t
+EmbeddedObjCollector::EnsureNGetIndex(Accessible* aAccessible)
+{
+  uint32_t childCount = mRoot->ChildCount();
+  while (mRootChildIdx < childCount) {
+    Accessible* child = mRoot->GetChildAt(mRootChildIdx++);
+    if (child->IsText())
+      continue;
+
+    AppendObject(child);
+    if (child == aAccessible)
+      return mObjects.Length() - 1;
+  }
+
+  return -1;
+}
+
+int32_t
+EmbeddedObjCollector::GetIndexAt(Accessible* aAccessible)
+{
+  if (aAccessible->mParent != mRoot)
+    return -1;
+
+  MOZ_ASSERT(!aAccessible->IsProxy());
+  if (aAccessible->mInt.mIndexOfEmbeddedChild != -1)
+    return aAccessible->mInt.mIndexOfEmbeddedChild;
+
+  return !aAccessible->IsText() ?  EnsureNGetIndex(aAccessible) : -1;
+}
+
+void
+EmbeddedObjCollector::AppendObject(Accessible* aAccessible)
+{
+  MOZ_ASSERT(!aAccessible->IsProxy());
+  aAccessible->mInt.mIndexOfEmbeddedChild = mObjects.Length();
+  mObjects.AppendElement(aAccessible);
+}
new file mode 100644
--- /dev/null
+++ b/accessible/base/EmbeddedObjCollector.h
@@ -0,0 +1,69 @@
+/* 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 mozilla_a11y_EmbeddedObjCollector_h__
+#define mozilla_a11y_EmbeddedObjCollector_h__
+
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace a11y {
+
+class Accessible;
+
+/**
+ * Collect embedded objects. Provide quick access to accessible by index and
+ * vice versa.
+ */
+class EmbeddedObjCollector final
+{
+public:
+  ~EmbeddedObjCollector() { }
+
+  /**
+   * Return index of the given accessible within the collection.
+   */
+  int32_t GetIndexAt(Accessible* aAccessible);
+
+  /**
+   * Return accessible count within the collection.
+   */
+  uint32_t Count();
+
+  /**
+   * Return an accessible from the collection at the given index.
+   */
+  Accessible* GetAccessibleAt(uint32_t aIndex);
+
+protected:
+  /**
+   * Ensure accessible at the given index is stored and return it.
+   */
+  Accessible* EnsureNGetObject(uint32_t aIndex);
+
+  /**
+   * Ensure index for the given accessible is stored and return it.
+   */
+  int32_t EnsureNGetIndex(Accessible* aAccessible);
+
+  // Make sure it's used by Accessible class only.
+  explicit EmbeddedObjCollector(Accessible* aRoot) :
+    mRoot(aRoot), mRootChildIdx(0) {}
+
+  /**
+   * Append the object to collection.
+   */
+  void AppendObject(Accessible* aAccessible);
+
+  friend class Accessible;
+
+  Accessible* mRoot;
+  uint32_t mRootChildIdx;
+  nsTArray<Accessible*> mObjects;
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
--- a/accessible/base/Filters.cpp
+++ b/accessible/base/Filters.cpp
@@ -44,14 +44,8 @@ filters::GetRow(Accessible* aAccessible)
   return eSkipSubtree;
 }
 
 uint32_t
 filters::GetCell(Accessible* aAccessible)
 {
   return aAccessible->IsTableCell() ? eMatch : eSkipSubtree;
 }
-
-uint32_t
-filters::GetEmbeddedObject(Accessible* aAccessible)
-{
-  return aAccessible->IsText() ? eSkipSubtree : eMatch | eSkipSubtree;
-}
--- a/accessible/base/Filters.h
+++ b/accessible/base/Filters.h
@@ -38,19 +38,13 @@ uint32_t GetSelectable(Accessible* aAcce
  * Matches row accessibles in subtree.
  */
 uint32_t GetRow(Accessible* aAccessible);
 
 /**
  * Matches cell accessibles in children.
  */
 uint32_t GetCell(Accessible* aAccessible);
-
-/**
- * Matches embedded objects in children.
- */
-uint32_t GetEmbeddedObject(Accessible* aAccessible);
-
 } // namespace filters
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/base/TextAttrs.cpp
+++ b/accessible/base/TextAttrs.cpp
@@ -823,17 +823,17 @@ TextAttrsMgr::TextPosTextAttr::
       break;
   }
 }
 
 TextAttrsMgr::TextPosValue
 TextAttrsMgr::TextPosTextAttr::
   GetTextPosValue(nsIFrame* aFrame) const
 {
-  const nsStyleCoord& styleCoord = aFrame->StyleTextReset()->mVerticalAlign;
+  const nsStyleCoord& styleCoord = aFrame->StyleDisplay()->mVerticalAlign;
   switch (styleCoord.GetUnit()) {
     case eStyleUnit_Enumerated:
       switch (styleCoord.GetIntValue()) {
         case NS_STYLE_VERTICAL_ALIGN_BASELINE:
           return eTextPosBaseline;
         case NS_STYLE_VERTICAL_ALIGN_SUB:
           return eTextPosSub;
         case NS_STYLE_VERTICAL_ALIGN_SUPER:
--- a/accessible/base/moz.build
+++ b/accessible/base/moz.build
@@ -21,24 +21,24 @@ EXPORTS.mozilla.a11y += [
 ]
 
 if CONFIG['MOZ_DEBUG']:
     EXPORTS.mozilla.a11y += [
         'Logging.h',
     ]
 
 UNIFIED_SOURCES += [
-    'AccCollector.cpp',
     'AccEvent.cpp',
     'AccGroupInfo.cpp',
     'AccIterator.cpp',
     'ARIAMap.cpp',
     'ARIAStateMap.cpp',
     'Asserts.cpp',
     'DocManager.cpp',
+    'EmbeddedObjCollector.cpp',
     'EventQueue.cpp',
     'EventTree.cpp',
     'Filters.cpp',
     'FocusManager.cpp',
     'NotificationController.cpp',
     'nsAccessibilityService.cpp',
     'nsAccessiblePivot.cpp',
     'nsAccUtils.cpp',
--- a/accessible/base/nsAccessiblePivot.cpp
+++ b/accessible/base/nsAccessiblePivot.cpp
@@ -896,17 +896,17 @@ RuleCache::ApplyFilter(Accessible* aAcce
         *aResult |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
         return NS_OK;
       }
     }
 
     if ((nsIAccessibleTraversalRule::PREFILTER_TRANSPARENT & mPreFilter) &&
         !(state & states::OPAQUE1)) {
       nsIFrame* frame = aAccessible->GetFrame();
-      if (frame->StyleDisplay()->mOpacity == 0.0f) {
+      if (frame->StyleEffects()->mOpacity == 0.0f) {
         *aResult |= nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
         return NS_OK;
       }
     }
   }
 
   if (mAcceptRolesLength > 0) {
     uint32_t accessibleRole = aAccessible->Role();
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -2,17 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Accessible-inl.h"
 
 #include "nsIXBLAccessible.h"
 
-#include "AccCollector.h"
+#include "EmbeddedObjCollector.h"
 #include "AccGroupInfo.h"
 #include "AccIterator.h"
 #include "nsAccUtils.h"
 #include "nsAccessibilityService.h"
 #include "ApplicationAccessible.h"
 #include "NotificationController.h"
 #include "nsEventShell.h"
 #include "nsTextEquivUtils.h"
@@ -1200,18 +1200,17 @@ Accessible::State()
     state |= states::EXPANDABLE;
 
   // For some reasons DOM node may have not a frame. We tract such accessibles
   // as invisible.
   nsIFrame *frame = GetFrame();
   if (!frame)
     return state;
 
-  const nsStyleDisplay* display = frame->StyleDisplay();
-  if (display && display->mOpacity == 1.0f &&
+  if (frame->StyleEffects()->mOpacity == 1.0f &&
       !(state & states::INVISIBLE)) {
     state |= states::OPAQUE1;
   }
 
   return state;
 }
 
 void
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1054,16 +1054,19 @@ pref("layout.accessiblecaret.enabled", t
 // by the spec in bug 921965.
 pref("layout.accessiblecaret.bar.enabled", true);
 
 // APZ on real devices supports long tap events.
 #ifdef MOZ_WIDGET_GONK
 pref("layout.accessiblecaret.use_long_tap_injector", false);
 #endif
 
+// The active caret is disallow to be dragged across the other (inactive) caret.
+pref("layout.accessiblecaret.allow_dragging_across_other_caret", false);
+
 // Enable sync and mozId with Firefox Accounts.
 pref("services.sync.fxaccounts.enabled", true);
 pref("identity.fxaccounts.enabled", true);
 
 // Mobile Identity API.
 pref("services.mobileid.server.uri", "https://msisdn.services.mozilla.com");
 
 pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1544,21 +1544,28 @@ pref("media.gmp.decoder.enabled", true);
 // installed.
 pref("media.gmp.decoder.aac", 2);
 pref("media.gmp.decoder.h264", 2);
 
 // Whether we should run a test-pattern through EME GMPs before assuming they'll
 // decode H.264.
 pref("media.gmp.trial-create.enabled", true);
 
-#ifdef MOZ_ADOBE_EME
+#if defined(MOZ_ADOBE_EME) || defined(MOZ_WIDEVINE_EME)
 pref("browser.eme.ui.enabled", true);
+#endif
+
+#ifdef MOZ_ADOBE_EME
 pref("media.gmp-eme-adobe.enabled", true);
 #endif
 
+#ifdef MOZ_WIDEVINE_EME
+pref("media.gmp-widevinecdm.enabled", true);
+#endif
+
 // Play with different values of the decay time and get telemetry,
 // 0 means to randomize (and persist) the experiment value in users' profiles,
 // -1 means no experiment is run and we use the preferred value for frecency (6h)
 pref("browser.cache.frecency_experiment", 0);
 
 pref("browser.translation.detectLanguage", false);
 pref("browser.translation.neverForLanguages", "");
 // Show the translation UI bits, like the info bar, notification icon and preferences.
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2306,38 +2306,16 @@
         <body>
           <![CDATA[
             if (aTab.closing ||
                 this._windowIsClosing)
               return false;
 
             var browser = this.getBrowserForTab(aTab);
 
-            var closeWindow = false;
-            var newTab = false;
-            if (this.tabs.length - this._removingTabs.length == 1) {
-              closeWindow = aCloseWindowWithLastTab != null ? aCloseWindowWithLastTab :
-                            !window.toolbar.visible ||
-                              Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab");
-
-              // Closing the tab and replacing it with a blank one is notably slower
-              // than closing the window right away. If the caller opts in, take
-              // the fast path.
-              if (closeWindow &&
-                  aCloseWindowFastpath &&
-                  this._removingTabs.length == 0) {
-                // This call actually closes the window, unless the user
-                // cancels the operation.  We are finished here in both cases.
-                this._windowIsClosing = window.closeWindow(true, window.warnAboutClosingWindow);
-                return null;
-              }
-
-              newTab = true;
-            }
-
             if (!aTab._pendingPermitUnload && !aAdoptedByTab && !aSkipPermitUnload) {
               // We need to block while calling permitUnload() because it
               // processes the event queue and may lead to another removeTab()
               // call before permitUnload() returns.
               aTab._pendingPermitUnload = true;
               let {permitUnload} = browser.permitUnload();
               delete aTab._pendingPermitUnload;
               // If we were closed during onbeforeunload, we return false now
@@ -2345,16 +2323,45 @@
               // also stop if the unload was cancelled by the user:
               if (aTab.closing || !permitUnload) {
                 // NB: deliberately keep the _closedDuringPermitUnload set to
                 // true so we keep exiting early in case of multiple calls.
                 return false;
               }
             }
 
+
+            var closeWindow = false;
+            var newTab = false;
+            if (this.tabs.length - this._removingTabs.length == 1) {
+              closeWindow = aCloseWindowWithLastTab != null ? aCloseWindowWithLastTab :
+                            !window.toolbar.visible ||
+                              Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab");
+
+              if (closeWindow) {
+                // We've already called beforeunload on all the relevant tabs if we get here,
+                // so avoid calling it again:
+                window.skipNextCanClose = true;
+              }
+
+              // Closing the tab and replacing it with a blank one is notably slower
+              // than closing the window right away. If the caller opts in, take
+              // the fast path.
+              if (closeWindow &&
+                  aCloseWindowFastpath &&
+                  this._removingTabs.length == 0) {
+                // This call actually closes the window, unless the user
+                // cancels the operation.  We are finished here in both cases.
+                this._windowIsClosing = window.closeWindow(true, window.warnAboutClosingWindow);
+                return null;
+              }
+
+              newTab = true;
+            }
+
             aTab.closing = true;
             this._removingTabs.push(aTab);
             this._visibleTabs = null; // invalidate cache
 
             // Invalidate hovered tab state tracking for this closing tab.
             if (this.tabContainer._hoveredTab == aTab)
               aTab._mouseleave();
 
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -433,16 +433,17 @@ support-files =
   contentSearchUI.js
 [browser_selectpopup.js]
 run-if = e10s
 [browser_selectTabAtIndex.js]
 [browser_ssl_error_reports.js]
 [browser_star_hsts.js]
 [browser_subframe_favicons_not_used.js]
 [browser_syncui.js]
+[browser_tab_close_dependent_window.js]
 [browser_tabDrop.js]
 skip-if = buildapp == 'mulet'
 [browser_tabReorder.js]
 skip-if = buildapp == 'mulet'
 [browser_tabMatchesInAwesomebar.js]
 [browser_tabMatchesInAwesomebar_perwindowpb.js]
 skip-if = os == 'linux' # Bug 1104755
 [browser_tab_detach_restore.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_tab_close_dependent_window.js
@@ -0,0 +1,24 @@
+"use strict";
+
+add_task(function* closing_tab_with_dependents_should_close_window() {
+  info("Opening window");
+  let win = yield BrowserTestUtils.openNewBrowserWindow();
+
+  info("Opening tab with data URI");
+  let tab = yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, `data:text/html,<html%20onclick="W=window.open()"><body%20onbeforeunload="W.close()">`);
+  info("Closing original tab in this window.");
+  yield BrowserTestUtils.removeTab(win.gBrowser.tabs[0]);
+  info("Clicking into the window");
+  let depTabOpened = BrowserTestUtils.waitForEvent(win.gBrowser.tabContainer, "TabOpen");
+  yield BrowserTestUtils.synthesizeMouse("html", 0, 0, {}, tab.linkedBrowser);
+
+  let openedTab = (yield depTabOpened).target;
+  info("Got opened tab");
+
+  let windowClosedPromise = BrowserTestUtils.windowClosed(win);
+  yield BrowserTestUtils.removeTab(tab);
+  is(openedTab.linkedBrowser, null, "Opened tab should also have closed");
+  info("If we timeout now, the window failed to close - that shouldn't happen!");
+  yield windowClosedPromise;
+});
+
--- a/browser/config/mozconfigs/macosx-universal/common-opt
+++ b/browser/config/mozconfigs/macosx-universal/common-opt
@@ -14,10 +14,15 @@ ac_add_options --with-mozilla-api-keyfil
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors (modulo ALLOW_COMPILER_WARNINGS).
 ac_add_options --enable-warnings-as-errors
 
+# Enable Widevine CDMs on MacOSX in Mozilla builds.
+# Enabled here on the assumption that downstream vendors will not be using
+# these build configs.
+ac_add_options --enable-eme=widevine
+
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/win32/common-opt
+++ b/browser/config/mozconfigs/win32/common-opt
@@ -27,15 +27,15 @@ export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 . $topsrcdir/build/win32/mozconfig.vs2015-win64
 
 # Treat warnings as errors (modulo ALLOW_COMPILER_WARNINGS).
 ac_add_options --enable-warnings-as-errors
 
-# Enable Adobe Primetime CDM on 32-bit Windows in Mozilla builds.
+# Enable Adobe Primetime and Widevine CDMs on 32-bit Windows in Mozilla builds.
 # Enabled here on the assumption that downstream vendors will not be using
 # these build configs.
-ac_add_options --enable-eme=adobe
+ac_add_options --enable-eme=adobe,widevine
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
--- a/browser/config/mozconfigs/win64/common-opt
+++ b/browser/config/mozconfigs/win64/common-opt
@@ -25,15 +25,15 @@ export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 # Treat warnings as errors (modulo ALLOW_COMPILER_WARNINGS).
 ac_add_options --enable-warnings-as-errors
 
 . $topsrcdir/build/win64/mozconfig.vs2015
 
-# Enable Adobe Primetime CDM on 64-bit Windows in Mozilla builds.
+# Enable Adobe Primetime and Widevine CDMs on 64-bit Windows in Mozilla builds.
 # Enabled here on the assumption that downstream vendors will not be using
 # these build configs.
-ac_add_options --enable-eme=adobe
+ac_add_options --enable-eme=adobe,widevine
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -505,16 +505,20 @@ a {
   text-decoration: none;
 }
 
 .cm-s-mozilla a[class]:hover,
 .cm-s-mozilla a[class]:focus {
   text-decoration: underline;
 }
 
+a.learn-more-link.webconsole-learn-more-link {
+    font-style: normal;
+}
+
 /* Open DOMNode in inspector button */
 .open-inspector {
   background: url("chrome://devtools/skin/images/vview-open-inspector.png") no-repeat 0 0;
   padding-left: 16px;
   margin-left: 5px;
   cursor: pointer;
 }
 
--- a/devtools/client/webconsole/console-output.js
+++ b/devtools/client/webconsole/console-output.js
@@ -1,10 +1,10 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft= javascript ts=2 et sw=2 tw=80: */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 
@@ -1336,18 +1336,20 @@ Messages.Extended.prototype = Heritage.e
  * The JavaScriptEvalOutput message.
  *
  * @constructor
  * @extends Messages.Extended
  * @param object evalResponse
  *        The evaluation response packet received from the server.
  * @param string [errorMessage]
  *        Optional error message to display.
+ * @param string [errorDocLink]
+ * Optional error doc URL to link to.
  */
-Messages.JavaScriptEvalOutput = function(evalResponse, errorMessage)
+Messages.JavaScriptEvalOutput = function(evalResponse, errorMessage, errorDocLink)
 {
   let severity = "log", msg, quoteStrings = true;
 
   // Store also the response packet from the back end. It might
   // be useful to extensions customizing the console output.
   this.response = evalResponse;
 
   if (typeof(errorMessage) !== "undefined") {
@@ -1360,17 +1362,23 @@ Messages.JavaScriptEvalOutput = function
 
   let options = {
     className: "cm-s-mozilla",
     timestamp: evalResponse.timestamp,
     category: "output",
     severity: severity,
     quoteStrings: quoteStrings,
   };
-  Messages.Extended.call(this, [msg], options);
+
+  let messages = [msg];
+  if (errorDocLink) {
+    messages.push(errorDocLink);
+  }
+
+  Messages.Extended.call(this, messages, options);
 };
 
 Messages.JavaScriptEvalOutput.prototype = Messages.Extended.prototype;
 
 /**
  * The ConsoleGeneric message is used for console API calls.
  *
  * @constructor
--- a/devtools/client/webconsole/jsterm.js
+++ b/devtools/client/webconsole/jsterm.js
@@ -1,10 +1,10 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft= javascript ts=2 et sw=2 tw=80: */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {Cc, Ci, Cu} = require("chrome");
 
@@ -303,16 +303,32 @@ JSTerm.prototype = {
       return;
     }
     if (response.error) {
       Cu.reportError("Evaluation error " + response.error + ": " +
                      response.message);
       return;
     }
     let errorMessage = response.exceptionMessage;
+    let errorDocURL = response.exceptionDocURL;
+
+    let errorDocLink;
+    if (errorDocURL) {
+      errorMessage += " ";
+      errorDocLink = this.hud.document.createElementNS(XHTML_NS, "a");
+      errorDocLink.className = "learn-more-link webconsole-learn-more-link";
+      errorDocLink.textContent = "[" + l10n.getStr("webConsoleMoreInfoLabel") + "]";
+      errorDocLink.title = errorDocURL;
+      errorDocLink.href = "#";
+      errorDocLink.draggable = false;
+      errorDocLink.addEventListener("click", () => {
+        this.hud.owner.openLink(errorDocURL);
+      });
+    }
+
     // Wrap thrown strings in Error objects, so `throw "foo"` outputs
     // "Error: foo"
     if (typeof(response.exception) === "string") {
       errorMessage = new Error(errorMessage).toString();
     }
     let result = response.result;
     let helperResult = response.helperResult;
     let helperHasRawOutput = !!(helperResult || {}).rawOutput;
@@ -351,17 +367,17 @@ JSTerm.prototype = {
     // Hide undefined results coming from JSTerm helper functions.
     if (!errorMessage && result && typeof result == "object" &&
         result.type == "undefined" &&
         helperResult && !helperHasRawOutput) {
       callback && callback();
       return;
     }
 
-    let msg = new Messages.JavaScriptEvalOutput(response, errorMessage);
+    let msg = new Messages.JavaScriptEvalOutput(response, errorMessage, errorDocLink);
     this.hud.output.addMessage(msg);
 
     if (callback) {
       let oldFlushCallback = this.hud._flushCallback;
       this.hud._flushCallback = () => {
         callback(msg.element);
         if (oldFlushCallback) {
           oldFlushCallback();
--- a/devtools/client/webconsole/test/browser_webconsole_jsterm.js
+++ b/devtools/client/webconsole/test/browser_webconsole_jsterm.js
@@ -161,9 +161,31 @@ function* testJSTerm(hud) {
   }, "thrown non-empty string generates error message");
 
   jsterm.clearOutput();
   yield jsterm.execute("throw { foo: 'bar' };");
   yield checkResult((node) => {
     return node.parentNode.getAttribute("severity") === "error" &&
       node.textContent === Object.prototype.toString();
   }, "thrown object generates error message");
+
+  // check that errors with entires in errordocs.js display links
+  // alongside their messages.
+  const ErrorDocs = require("devtools/server/actors/errordocs");
+
+  const ErrorDocStatements = {
+    "JSMSG_BAD_RADIX": "(42).toString(0);",
+    "JSMSG_BAD_ARRAY_LENGTH": "([]).length = -1",
+    "JSMSG_NEGATIVE_REPETITION_COUNT": "'abc'.repeat(-1);",
+    "JSMSG_BAD_FORMAL": "var f = Function('x y', 'return x + y;');",
+    "JSMSG_PRECISION_RANGE": "77.1234.toExponential(-1);",
+  };
+
+  for (let errorMessageName of Object.keys(ErrorDocStatements)) {
+    let url = ErrorDocs.GetURL(errorMessageName);
+
+    jsterm.clearOutput();
+    yield jsterm.execute(ErrorDocStatements[errorMessageName]);
+    yield checkResult((node) => {
+      return node.parentNode.getElementsByTagName("a")[0].title == url;
+    }, `error links to ${url}`);
+  }
 }
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -12,16 +12,17 @@ const {Utils: WebConsoleUtils, CONSOLE_W
   require("devtools/shared/webconsole/utils");
 const { getSourceNames } = require("devtools/client/shared/source-utils");
 const BrowserLoaderModule = {};
 Cu.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule);
 
 const promise = require("promise");
 const Services = require("Services");
 const ErrorDocs = require("devtools/server/actors/errordocs");
+const Telemetry = require("devtools/client/shared/telemetry")
 
 loader.lazyServiceGetter(this, "clipboardHelper",
                          "@mozilla.org/widget/clipboardhelper;1",
                          "nsIClipboardHelper");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 loader.lazyRequireGetter(this, "AutocompletePopup", "devtools/client/shared/autocomplete-popup", true);
 loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/framework/sidebar", true);
 loader.lazyRequireGetter(this, "ConsoleOutput", "devtools/client/webconsole/console-output", true);
@@ -237,16 +238,18 @@ function WebConsoleFrame(webConsoleOwner
     window: this.window,
     useOnlyShared: true
   }).require;
 
   this.React = require("devtools/client/shared/vendor/react");
   this.ReactDOM = require("devtools/client/shared/vendor/react-dom");
   this.FrameView = this.React.createFactory(require("devtools/client/shared/components/frame"));
 
+  this._telemetry = new Telemetry();
+
   EventEmitter.decorate(this);
 }
 exports.WebConsoleFrame = WebConsoleFrame;
 
 WebConsoleFrame.prototype = {
   /**
    * The WebConsole instance that owns this frame.
    * @see hudservice.js::WebConsole
@@ -1489,16 +1492,21 @@ WebConsoleFrame.prototype = {
     let node = msg.init(this.output).render().element;
 
     // Select the body of the message node that is displayed in the console
     let msgBody = node.getElementsByClassName("message-body")[0];
 
     // Add the more info link node to messages that belong to certain categories
     this.addMoreInfoLink(msgBody, scriptError);
 
+    // Collect telemetry data regarding JavaScript errors
+    this._telemetry.logKeyed("DEVTOOLS_JAVASCRIPT_ERROR_DISPLAYED",
+                             scriptError.errorMessageName,
+                             true);
+
     if (objectActors.size > 0) {
       node._objectActors = objectActors;
     }
 
     return node;
   },
 
   /**
--- a/devtools/server/actors/webconsole.js
+++ b/devtools/server/actors/webconsole.js
@@ -8,16 +8,17 @@
 
 const Services = require("Services");
 const { Cc, Ci, Cu } = require("chrome");
 const { DebuggerServer, ActorPool } = require("devtools/server/main");
 const { EnvironmentActor } = require("devtools/server/actors/environment");
 const { ThreadActor } = require("devtools/server/actors/script");
 const { ObjectActor, LongStringActor, createValueGrip, stringIsLong } = require("devtools/server/actors/object");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const ErrorDocs = require("devtools/server/actors/errordocs");
 
 loader.lazyRequireGetter(this, "NetworkMonitor", "devtools/shared/webconsole/network-monitor", true);
 loader.lazyRequireGetter(this, "NetworkMonitorChild", "devtools/shared/webconsole/network-monitor", true);
 loader.lazyRequireGetter(this, "ConsoleProgressListener", "devtools/shared/webconsole/network-monitor", true);
 loader.lazyRequireGetter(this, "events", "sdk/event/core");
 loader.lazyRequireGetter(this, "ServerLoggingListener", "devtools/shared/webconsole/server-logger", true);
 loader.lazyRequireGetter(this, "JSPropertyProvider", "devtools/shared/webconsole/js-property-provider", true);
 loader.lazyRequireGetter(this, "Parser", "resource://devtools/shared/Parser.jsm", true);
@@ -868,32 +869,38 @@ WebConsoleActor.prototype =
       selectedNodeActor: aRequest.selectedNodeActor,
       selectedObjectActor: aRequest.selectedObjectActor,
     };
 
     let evalInfo = this.evalWithDebugger(input, evalOptions);
     let evalResult = evalInfo.result;
     let helperResult = evalInfo.helperResult;
 
-    let result, errorMessage, errorGrip = null;
+    let result, errorDocURL, errorMessage, errorGrip = null;
     if (evalResult) {
       if ("return" in evalResult) {
         result = evalResult.return;
       } else if ("yield" in evalResult) {
         result = evalResult.yield;
       } else if ("throw" in evalResult) {
         let error = evalResult.throw;
         errorGrip = this.createValueGrip(error);
         // XXXworkers: Calling unsafeDereference() returns an object with no
         // toString method in workers. See Bug 1215120.
         let unsafeDereference = error && (typeof error === "object") &&
                                 error.unsafeDereference();
         errorMessage = unsafeDereference && unsafeDereference.toString
           ? unsafeDereference.toString()
           : String(error);
+
+          // It is possible that we won't have permission to unwrap an
+          // object and retrieve its errorMessageName.
+          try {
+            errorDocURL = ErrorDocs.GetURL(error && error.errorMessageName);
+          } catch (ex) {}
       }
     }
 
     // If a value is encountered that the debugger server doesn't support yet,
     // the console should remain functional.
     let resultGrip;
     try {
       resultGrip = this.createValueGrip(result);
@@ -905,16 +912,17 @@ WebConsoleActor.prototype =
 
     return {
       from: this.actorID,
       input: input,
       result: resultGrip,
       timestamp: timestamp,
       exception: errorGrip,
       exceptionMessage: this._createStringGrip(errorMessage),
+      exceptionDocURL: errorDocURL,
       helperResult: helperResult,
     };
   },
 
   /**
    * The Autocomplete request handler.
    *
    * @param object aRequest
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -402,24 +402,33 @@ private:
       ConsoleReleaseRunnable(WorkerPrivate* aWorkerPrivate,
                              ConsoleRunnable* aRunnable)
         : MainThreadWorkerControlRunnable(aWorkerPrivate)
         , mRunnable(aRunnable)
       {
         MOZ_ASSERT(aRunnable);
       }
 
+      // If something goes wrong, we still need to release the ConsoleCallData
+      // object. For this reason we have a custom Cancel method.
+      NS_IMETHOD
+      Cancel() override
+      {
+        mRunnable->ReleaseData();
+        mRunnable->mConsole = nullptr;
+        return NS_OK;
+      }
+
       virtual bool
       WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override
       {
         MOZ_ASSERT(aWorkerPrivate);
         aWorkerPrivate->AssertIsOnWorkerThread();
 
-        mRunnable->ReleaseData();
-        mRunnable->mConsole = nullptr;
+        Cancel();
 
         aWorkerPrivate->RemoveFeature(mRunnable);
         return true;
       }
 
     private:
       ~ConsoleReleaseRunnable()
       {}
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -1013,23 +1013,33 @@ NS_IMPL_ISUPPORTS_INHERITED0(EmptyBlobIm
 
 already_AddRefed<BlobImpl>
 EmptyBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
                            const nsAString& aContentType,
                            ErrorResult& aRv)
 {
   MOZ_ASSERT(!aStart && !aLength);
   RefPtr<BlobImpl> impl = new EmptyBlobImpl(aContentType);
+
+  DebugOnly<bool> isMutable;
+  MOZ_ASSERT(NS_SUCCEEDED(impl->GetMutable(&isMutable)));
+  MOZ_ASSERT(!isMutable);
+
   return impl.forget();
 }
 
 void
 EmptyBlobImpl::GetInternalStream(nsIInputStream** aStream,
                                  ErrorResult& aRv)
 {
+  if (NS_WARN_IF(!aStream)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
   nsresult rv = NS_NewCStringInputStream(aStream, EmptyCString());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(rv);
     return;
   }
 }
 
 ////////////////////////////////////////////////////////////////////////////
--- a/dom/base/File.h
+++ b/dom/base/File.h
@@ -749,17 +749,27 @@ private:
 
 class EmptyBlobImpl final : public BlobImplBase
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   explicit EmptyBlobImpl(const nsAString& aContentType)
     : BlobImplBase(aContentType, 0 /* aLength */)
-  {}
+  {
+    mImmutable = true;
+  }
+
+  EmptyBlobImpl(const nsAString& aName,
+                const nsAString& aContentType,
+                int64_t aLastModifiedDate)
+    : BlobImplBase(aName, aContentType, 0, aLastModifiedDate)
+  {
+    mImmutable = true;
+  }
 
   virtual void GetInternalStream(nsIInputStream** aStream,
                                  ErrorResult& aRv) override;
 
   virtual already_AddRefed<BlobImpl>
   CreateSlice(uint64_t aStart, uint64_t aLength,
               const nsAString& aContentType, ErrorResult& aRv) override;
 
--- a/dom/base/MultipartBlobImpl.h
+++ b/dom/base/MultipartBlobImpl.h
@@ -11,18 +11,18 @@
 #include "mozilla/CheckedInt.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/FileBinding.h"
 #include <algorithm>
 #include "nsPIDOMWindow.h"
 
-using namespace mozilla;
-using namespace mozilla::dom;
+namespace mozilla {
+namespace dom {
 
 class MultipartBlobImpl final : public BlobImplBase
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   // Create as a file
   static already_AddRefed<MultipartBlobImpl>
@@ -99,21 +99,16 @@ public:
   virtual nsresult
   SetMutable(bool aMutable) override;
 
   void SetName(const nsAString& aName)
   {
     mName = aName;
   }
 
-  void SetFromNsIFile(bool aValue)
-  {
-    mIsFromNsIFile = aValue;
-  }
-
   virtual bool MayBeClonedToOtherThreads() const override;
 
 protected:
   MultipartBlobImpl(const nsTArray<RefPtr<BlobImpl>>& aBlobImpls,
                     const nsAString& aName,
                     const nsAString& aContentType)
     : BlobImplBase(aName, aContentType, UINT64_MAX),
       mBlobImpls(aBlobImpls),
@@ -132,9 +127,12 @@ protected:
   virtual ~MultipartBlobImpl() {}
 
   void SetLengthAndModifiedDate(ErrorResult& aRv);
 
   nsTArray<RefPtr<BlobImpl>> mBlobImpls;
   bool mIsFromNsIFile;
 };
 
+} // dom namespace
+} // mozilla namespace
+
 #endif // mozilla_dom_MultipartBlobImpl_h
--- a/dom/base/ResponsiveImageSelector.cpp
+++ b/dom/base/ResponsiveImageSelector.cpp
@@ -449,17 +449,16 @@ ResponsiveImageSelector::ComputeFinalWid
     nsCSSValue defaultWidth(100.0f, eCSSUnit_ViewportWidth);
     effectiveWidth = nsRuleNode::CalcLengthWithInitialFont(pctx,
                                                            defaultWidth);
   } else {
     effectiveWidth = nsRuleNode::CalcLengthWithInitialFont(pctx,
                                                            mSizeValues[i]);
   }
 
-  MOZ_ASSERT(effectiveWidth >= 0);
   *aWidth = nsPresContext::AppUnitsToDoubleCSSPixels(std::max(effectiveWidth, 0));
   return true;
 }
 
 ResponsiveImageCandidate::ResponsiveImageCandidate()
 {
   mType = eCandidateType_Invalid;
   mValue.mDensity = 1.0;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -12561,59 +12561,47 @@ nsDocument::ShouldLockPointer(Element* a
   }
 
   return true;
 }
 
 bool
 nsDocument::SetPointerLock(Element* aElement, int aCursorStyle)
 {
-  // NOTE: aElement will be nullptr when unlocking.
-  nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
-  if (!window) {
-    NS_WARNING("SetPointerLock(): No Window");
-    return false;
-  }
-
-  nsIDocShell *docShell = window->GetDocShell();
-  if (!docShell) {
-    NS_WARNING("SetPointerLock(): No DocShell (window already closed?)");
-    return false;
-  }
-
-  RefPtr<nsPresContext> presContext;
-  docShell->GetPresContext(getter_AddRefs(presContext));
-  if (!presContext) {
-    NS_WARNING("SetPointerLock(): Unable to get presContext in \
-                domWindow->GetDocShell()->GetPresContext()");
+  MOZ_ASSERT(!aElement || aElement->OwnerDoc() == this,
+             "We should be either unlocking pointer (aElement is nullptr), "
+             "or locking pointer to an element in this document");
+#ifdef DEBUG
+  if (!aElement) {
+    nsCOMPtr<nsIDocument> pointerLockedDoc =
+      do_QueryReferent(EventStateManager::sPointerLockedDoc);
+    MOZ_ASSERT(pointerLockedDoc == this);
+  }
+#endif
+
+  nsIPresShell* shell = GetShell();
+  if (!shell) {
+    NS_WARNING("SetPointerLock(): No PresShell");
     return false;
   }
-
-  nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
-  if (!shell) {
-    NS_WARNING("SetPointerLock(): Unable to find presContext->PresShell()");
-    return false;
-  }
-
-  nsIFrame* rootFrame = shell->GetRootFrame();
-  if (!rootFrame) {
-    NS_WARNING("SetPointerLock(): Unable to get root frame");
+  nsPresContext* presContext = shell->GetPresContext();
+  if (!presContext) {
+    NS_WARNING("SetPointerLock(): Unable to get PresContext");
     return false;
   }
 
-  nsCOMPtr<nsIWidget> widget = rootFrame->GetNearestWidget();
-  if (!widget) {
-    NS_WARNING("SetPointerLock(): Unable to find widget in \
-                shell->GetRootFrame()->GetNearestWidget();");
-    return false;
-  }
-
-  if (aElement && (aElement->OwnerDoc() != this)) {
-    NS_WARNING("SetPointerLock(): Element not in this document.");
-    return false;
+  nsCOMPtr<nsIWidget> widget;
+  nsIFrame* rootFrame = shell->GetRootFrame();
+  if (!NS_WARN_IF(!rootFrame)) {
+    widget = rootFrame->GetNearestWidget();
+    NS_WARN_IF_FALSE(widget, "SetPointerLock(): Unable to find widget "
+                     "in shell->GetRootFrame()->GetNearestWidget();");
+    if (aElement && !widget) {
+      return false;
+    }
   }
 
   // Hide the cursor and set pointer lock for future mouse events
   RefPtr<EventStateManager> esm = presContext->EventStateManager();
   esm->SetCursor(aCursorStyle, nullptr, false,
                  0.0f, 0.0f, widget, true);
   esm->SetPointerLock(widget, aElement);
 
--- a/dom/base/test/browser.ini
+++ b/dom/base/test/browser.ini
@@ -1,10 +1,12 @@
 [DEFAULT]
 support-files =
+  file_bug1011748_redirect.sjs
+  file_bug1011748_OK.sjs
   file_messagemanager_unload.html
   file_use_counter_outer.html
   file_use_counter_svg_getElementById.svg
   file_use_counter_svg_currentScale.svg
   file_use_counter_svg_background.html
   file_use_counter_svg_list_style_image.html
   file_use_counter_svg_fill_pattern_definition.svg
   file_use_counter_svg_fill_pattern.svg
@@ -21,8 +23,9 @@ skip-if = e10s # this tests non-e10s beh
 [browser_messagemanager_unload.js]
 [browser_state_notifications.js]
 # skip-if = e10s # Bug ?????? - content-document-* notifications come while document's URI is still about:blank, but test expects real URL.
 skip-if = true # Intermittent failures - bug 987493. Restore the skip-if above once fixed
 [browser_bug1058164.js]
 [browser_use_counters.js]
 [browser_bug1238440.js]
 skip-if = e10s
+[browser_bug1011748.js]
rename from dom/base/test/test_bug1011748.html
rename to dom/base/test/browser_bug1011748.js
--- a/dom/base/test/test_bug1011748.html
+++ b/dom/base/test/browser_bug1011748.js
@@ -1,57 +1,31 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1011748
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 1011748</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <script type="application/javascript">
+const gHttpTestRoot = "http://example.com/browser/dom/base/test/";
 
-  /** Test for Bug 1011748 **/
-  "use strict";
-  
+add_task(function* () {
+  var statusTexts = [];
+  var xhr = new XMLHttpRequest();
   var observer = {
     observe: function (aSubject, aTopic, aData) {
       try {
-        var channel = aSubject.QueryInterface(SpecialPowers.Ci.nsIHttpChannel);
+        var channel = aSubject.QueryInterface(Ci.nsIHttpChannel);
         channel.getResponseHeader("Location");
       } catch (e) {
         return;
       }
       statusTexts.push(xhr.statusText);
     }
   };
   
-  var statusTexts = [];
-  SpecialPowers.addObserver(observer, "http-on-examine-response", false);
-  SimpleTest.waitForExplicitFinish();
-  var xhr = new XMLHttpRequest();
-  xhr.addEventListener("load", function() {
-    statusTexts.push(this.statusText);
-    SpecialPowers.removeObserver(observer, "http-on-examine-response");
-    is(statusTexts[0], "", "Empty statusText value for HTTP 302");
-    is(statusTexts[1], "OK", "OK statusText value for the redirect."); 
-    SimpleTest.finish();
+  Services.obs.addObserver(observer, "http-on-examine-response", false);
+  yield new Promise((resolve) => {
+    xhr.addEventListener("load", function() {
+      statusTexts.push(this.statusText);
+      is(statusTexts[0], "", "Empty statusText value for HTTP 302");
+      is(statusTexts[1], "OK", "OK statusText value for the redirect.");
+      resolve();
+    });
+    xhr.open("GET", gHttpTestRoot+ "file_bug1011748_redirect.sjs", true);
+    xhr.send();
   });
-  xhr.open("GET", "file_bug1011748_redirect.sjs", true);
-  xhr.send();
-  
 
-
-
-
-  </script>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1011748">Mozilla Bug 1011748</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-</pre>
-</body>
-</html>
+  Services.obs.removeObserver(observer, "http-on-examine-response");
+});
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -668,27 +668,22 @@ skip-if = buildapp == 'b2g'
 [test_bug693875.html]
 [test_bug694754.xhtml]
 [test_bug696301-1.html]
 [test_bug696301-2.html]
 [test_bug698381.html]
 [test_bug698384.html]
 [test_bug704063.html]
 [test_bug704320_http_http.html]
-support-files = referrerHelper.js
 [test_bug704320_http_https.html]
-support-files = referrerHelper.js
 [test_bug704320_https_http.html]
-support-files = referrerHelper.js
 skip-if = buildapp == 'b2g'  # b2g (https://example.com not working bug 1162353)
 [test_bug704320_https_https.html]
-support-files = referrerHelper.js
 skip-if = buildapp == 'b2g'  # b2g (https://example.com not working bug 1162353)
 [test_bug704320_policyset.html]
-support-files = referrerHelper.js
 [test_bug704320_policyset2.html]
 skip-if = os == "mac" # fails intermittently - bug 1101288
 [test_bug704320_preload.html]
 [test_bug707142.html]
 [test_bug708620.html]
 [test_bug711047.html]
 [test_bug711180.html]
 [test_bug719533.html]
@@ -734,19 +729,17 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_bug927196.html]
 [test_bug982153.html]
 [test_bug1057176.html]
 [test_bug1070015.html]
 [test_bug1075702.html]
 [test_bug1101364.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android'
 [test_bug1163743.html]
-support-files = referrerHelper.js
 [test_bug1165501.html]
-support-files = referrerHelper.js
 [test_img_referrer.html]
 [test_anchor_area_referrer.html]
 [test_anchor_area_referrer_changing.html]
 [test_anchor_area_referrer_invalid.html]
 [test_anchor_area_referrer_rel.html]
 [test_iframe_referrer.html]
 [test_iframe_referrer_changing.html]
 [test_iframe_referrer_invalid.html]
@@ -852,19 +845,16 @@ skip-if = toolkit == 'android'
 [test_file_from_blob.html]
 [test_warning_for_blocked_cross_site_request.html]
 [test_bug444546.html]
 disabled = Disabled for now. Mochitest isn't reliable enough for these.
 support-files = bug444546.sjs
 [test_bug503473.html]
 disabled = Disabled due to making the harness time out
 support-files = file_bug503473-frame.sjs
-[test_bug1011748.html]
-skip-if = buildapp == 'b2g' || e10s
-support-files = file_bug1011748_redirect.sjs file_bug1011748_OK.sjs
 [test_bug1025933.html]
 [test_bug1037687.html]
 [test_element.matches.html]
 [test_user_select.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
 [test_bug1081686.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android'
 [test_window_define_nonconfigurable.html]
@@ -895,9 +885,9 @@ skip-if = buildapp == 'b2g' #no ssl supp
 [test_bug1198095.html]
 [test_bug1187157.html]
 [test_bug769117.html]
 [test_bug1250148.html]
 [test_bug1240471.html]
 [test_mozbrowser_apis_allowed.html]
 [test_mozbrowser_apis_blocked.html]
 [test_document_register.html]
-[test_bug962251.html]
\ No newline at end of file
+[test_bug962251.html]
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -78,17 +78,16 @@ support-files =
   browserElement_TargetTop.js
   browserElement_Titlechange.js
   browserElement_TopBarrier.js
   browserElement_VisibilityChange.js
   browserElement_XFrameOptions.js
   browserElement_XFrameOptionsAllowFrom.js
   browserElement_XFrameOptionsDeny.js
   browserElement_XFrameOptionsSameOrigin.js
-  browserElement_XFrameOptionsSameOrigin.js
   browserElement_GetContentDimensions.js
   browserElement_AudioChannel.js
   browserElement_AudioChannel_nested.js
   file_browserElement_ActiveStateChangeOnChangingMutedOrVolume.html
   file_browserElement_AlertInFrame.html
   file_browserElement_AlertInFrame_Inner.html
   file_browserElement_AllowEmbedAppsInNestedOOIframe.html
   file_browserElement_AppFramePermission.html
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -2433,17 +2433,17 @@ CanvasRenderingContext2D::ParseFilter(co
 
   RefPtr<nsStyleContext> sc =
     ResolveStyleForFilter(aString, presShell, parentContext, aError);
 
   if (!sc) {
     return false;
   }
 
-  aFilterChain = sc->StyleSVGReset()->mFilters;
+  aFilterChain = sc->StyleEffects()->mFilters;
   return true;
 }
 
 void
 CanvasRenderingContext2D::SetFilter(const nsAString& aFilter, ErrorResult& aError)
 {
   nsTArray<nsStyleFilter> filterChain;
   if (ParseFilter(aFilter, filterChain, aError)) {
--- a/dom/canvas/TexUnpackBlob.cpp
+++ b/dom/canvas/TexUnpackBlob.cpp
@@ -751,17 +751,23 @@ TexUnpackSurface::TexOrSubImage(bool isS
 
     WebGLContext* webgl = tex->mContext;
 
     // MakeCurrent is a big mess in here, because mapping (and presumably unmapping) on
     // OSX can lose our MakeCurrent. Therefore it's easiest to MakeCurrent just before we
     // call into GL, instead of trying to keep MakeCurrent-ed.
 
     RefPtr<gfx::DataSourceSurface> dataSurf = mSurf->GetDataSurface();
-    MOZ_ASSERT(dataSurf);
+
+    if (!dataSurf) {
+        // Since GetDataSurface didn't return error code, assume system
+        // is out of memory
+        *out_glError = LOCAL_GL_OUT_OF_MEMORY;
+        return;
+    }
 
     GLenum error;
     if (UploadDataSurface(isSubImage, webgl, target, level, dui, xOffset, yOffset,
                           zOffset, mWidth, mHeight, dataSurf, mIsAlphaPremult, &error))
     {
         return;
     }
     if (error == LOCAL_GL_OUT_OF_MEMORY) {
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -1904,26 +1904,34 @@ ContentEventHandler::GetStartFrameAndOff
 }
 
 nsresult
 ContentEventHandler::ConvertToRootRelativeOffset(nsIFrame* aFrame,
                                                  nsRect& aRect)
 {
   NS_ASSERTION(aFrame, "aFrame must not be null");
 
-  nsPresContext* rootPresContext = aFrame->PresContext()->GetRootPresContext();
-  if (NS_WARN_IF(!rootPresContext)) {
+  nsPresContext* thisPC = aFrame->PresContext();
+  nsPresContext* rootPC = thisPC->GetRootPresContext();
+  if (NS_WARN_IF(!rootPC)) {
     return NS_ERROR_FAILURE;
   }
-  nsIFrame* rootFrame = rootPresContext->PresShell()->GetRootFrame();
+  nsIFrame* rootFrame = rootPC->PresShell()->GetRootFrame();
   if (NS_WARN_IF(!rootFrame)) {
     return NS_ERROR_FAILURE;
   }
 
   aRect = nsLayoutUtils::TransformFrameRectToAncestor(aFrame, aRect, rootFrame);
+
+  // TransformFrameRectToAncestor returned the rect in the ancestor's appUnits,
+  // but we want it in aFrame's units (in case of different full-zoom factors),
+  // so convert back.
+  aRect = aRect.ScaleToOtherAppUnitsRoundOut(rootPC->AppUnitsPerDevPixel(),
+                                             thisPC->AppUnitsPerDevPixel());
+
   return NS_OK;
 }
 
 static void AdjustRangeForSelection(nsIContent* aRoot,
                                     nsINode** aNode,
                                     int32_t* aNodeOffset)
 {
   nsINode* node = *aNode;
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -259,18 +259,18 @@ protected:
                                       uint32_t* aNewOffset = nullptr);
   // If the aRange isn't in text node but next to a text node, this method
   // modifies it in the text node.  Otherwise, not modified.
   nsresult AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aCollapsedRange);
   // Find the first frame for the range and get the start offset in it.
   nsresult GetStartFrameAndOffset(const nsRange* aRange,
                                   nsIFrame*& aFrame,
                                   int32_t& aOffsetInFrame);
-  // Convert the frame relative offset to the root frame of the root presContext
-  // relative offset.
+  // Convert the frame relative offset to be relative to the root frame of the
+  // root presContext (but still measured in appUnits of aFrame's presContext).
   nsresult ConvertToRootRelativeOffset(nsIFrame* aFrame,
                                        nsRect& aRect);
   // Expand aXPOffset to the nearest offset in cluster boundary. aForward is
   // true, it is expanded to forward.
   nsresult ExpandToClusterBoundary(nsIContent* aContent, bool aForward,
                                    uint32_t* aXPOffset);
 
   typedef nsTArray<mozilla::FontRange> FontRangeArray;
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -4334,53 +4334,53 @@ EventStateManager::GetWrapperByEventID(W
 
 void
 EventStateManager::SetPointerLock(nsIWidget* aWidget,
                                   nsIContent* aElement)
 {
   // NOTE: aElement will be nullptr when unlocking.
   sIsPointerLocked = !!aElement;
 
-  if (!aWidget) {
-    return;
-  }
-
   // Reset mouse wheel transaction
   WheelTransaction::EndTransaction();
 
   // Deal with DnD events
   nsCOMPtr<nsIDragService> dragService =
     do_GetService("@mozilla.org/widget/dragservice;1");
 
   if (sIsPointerLocked) {
+    MOZ_ASSERT(aWidget, "Locking pointer requires a widget");
+
     // Store the last known ref point so we can reposition the pointer after unlock.
     mPreLockPoint = sLastRefPoint;
 
     // Fire a synthetic mouse move to ensure event state is updated. We first
     // set the mouse to the center of the window, so that the mouse event
     // doesn't report any movement.
     sLastRefPoint = GetWindowClientRectCenter(aWidget);
-    aWidget->SynthesizeNativeMouseMove(sLastRefPoint + aWidget->WidgetToScreenOffset(),
-                                       nullptr);
+    aWidget->SynthesizeNativeMouseMove(
+      sLastRefPoint + aWidget->WidgetToScreenOffset(), nullptr);
 
     // Retarget all events to this element via capture.
     nsIPresShell::SetCapturingContent(aElement, CAPTURE_POINTERLOCK);
 
     // Suppress DnD
     if (dragService) {
       dragService->Suppress();
     }
   } else {
     // Unlocking, so return pointer to the original position by firing a
     // synthetic mouse event. We first reset sLastRefPoint to its
     // pre-pointerlock position, so that the synthetic mouse event reports
     // no movement.
     sLastRefPoint = mPreLockPoint;
-    aWidget->SynthesizeNativeMouseMove(mPreLockPoint + aWidget->WidgetToScreenOffset(),
-                                       nullptr);
+    if (aWidget) {
+      aWidget->SynthesizeNativeMouseMove(
+        mPreLockPoint + aWidget->WidgetToScreenOffset(), nullptr);
+    }
 
     // Don't retarget events to this element any more.
     nsIPresShell::SetCapturingContent(nullptr, CAPTURE_POINTERLOCK);
 
     // Unsuppress DnD
     if (dragService) {
       dragService->Unsuppress();
     }
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -289,16 +289,25 @@ public:
   // dom::Event::GetClientCoords() to make mouse events' client coords appear
   // frozen at the last mouse position while the pointer is locked.
   static CSSIntPoint sLastClientPoint;
 
   static bool sIsPointerLocked;
   static nsWeakPtr sPointerLockedElement;
   static nsWeakPtr sPointerLockedDoc;
 
+  /**
+   * If the absolute values of mMultiplierX and/or mMultiplierY are equal or
+   * larger than this value, the computed scroll amount isn't rounded down to
+   * the page width or height.
+   */
+  enum {
+    MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL = 1000
+  };
+
 protected:
   /**
    * Prefs class capsules preference management.
    */
   class Prefs
   {
   public:
     static bool KeyCausesActivation() { return sKeyCausesActivation; }
@@ -555,25 +564,16 @@ protected:
      *                      "mousewheel.default.".
      */
     void GetBasePrefName(Index aIndex, nsACString& aBasePrefName);
 
     void Init(Index aIndex);
 
     void Reset();
 
-    /**
-     * If the abosolute values of mMultiplierX and/or mMultiplierY are equals or
-     * larger than this value, the computed scroll amount isn't rounded down to
-     * the page width or height.
-     */
-    enum {
-      MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL = 1000
-    };
-
     bool mInit[COUNT_OF_MULTIPLIERS];
     double mMultiplierX[COUNT_OF_MULTIPLIERS];
     double mMultiplierY[COUNT_OF_MULTIPLIERS];
     double mMultiplierZ[COUNT_OF_MULTIPLIERS];
     Action mActions[COUNT_OF_MULTIPLIERS];
     /**
      * action values overridden by .override_x pref.
      * If an .override_x value is -1, same as the
--- a/dom/events/test/window_wheel_default_action.html
+++ b/dom/events/test/window_wheel_default_action.html
@@ -1347,18 +1347,18 @@ function doTestZoomedScroll(aCallback)
   //     is computed by complex logic.
 
   prepareTestZoomedPixelScroll();
 }
 
 function doTestWholeScroll(aCallback)
 {
   SpecialPowers.pushPrefEnv({"set": [
-    ["mousewheel.default.delta_multiplier_x", 99999999],
-    ["mousewheel.default.delta_multiplier_y", 99999999]]},
+    ["mousewheel.default.delta_multiplier_x", 999999],
+    ["mousewheel.default.delta_multiplier_y", 999999]]},
     function() { doTestWholeScroll2(aCallback); });
 }
 
 function doTestWholeScroll2(aCallback)
 {
   const kTests = [
     { description: "try whole-scroll to top (line)",
       prepare: function () {
--- a/dom/html/HTMLTableCellElement.cpp
+++ b/dom/html/HTMLTableCellElement.cpp
@@ -488,17 +488,17 @@ HTMLTableCellElement::MapAttributesIntoR
         if (!value || value->Type() != nsAttrValue::eInteger ||
             value->GetIntegerValue() == 0 ||
             eCompatibility_NavQuirks != mode) {
           whiteSpace->SetIntValue(NS_STYLE_WHITESPACE_NOWRAP, eCSSUnit_Enumerated);
         }
       }
     }
   }
-  if (aData->mSIDs & NS_STYLE_INHERIT_BIT(TextReset)) {
+  if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Display)) {
     nsCSSValue* verticalAlign = aData->ValueForVerticalAlign();
     if (verticalAlign->GetUnit() == eCSSUnit_Null) {
       // valign: enum
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::valign);
       if (value && value->Type() == nsAttrValue::eEnum)
         verticalAlign->SetIntValue(value->GetEnumValue(), eCSSUnit_Enumerated);
     }
   }
--- a/dom/html/HTMLTableColElement.cpp
+++ b/dom/html/HTMLTableColElement.cpp
@@ -106,17 +106,17 @@ HTMLTableColElement::MapAttributesIntoRu
     nsCSSValue* textAlign = aData->ValueForTextAlign();
     if (textAlign->GetUnit() == eCSSUnit_Null) {
       // align: enum
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
       if (value && value->Type() == nsAttrValue::eEnum)
         textAlign->SetIntValue(value->GetEnumValue(), eCSSUnit_Enumerated);
     }
   }
-  if (aData->mSIDs & NS_STYLE_INHERIT_BIT(TextReset)) {
+  if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Display)) {
     nsCSSValue* verticalAlign = aData->ValueForVerticalAlign();
     if (verticalAlign->GetUnit() == eCSSUnit_Null) {
       // valign: enum
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::valign);
       if (value && value->Type() == nsAttrValue::eEnum)
         verticalAlign->SetIntValue(value->GetEnumValue(), eCSSUnit_Enumerated);
     }
   }
--- a/dom/html/HTMLTableRowElement.cpp
+++ b/dom/html/HTMLTableRowElement.cpp
@@ -281,17 +281,17 @@ HTMLTableRowElement::MapAttributesIntoRu
     nsCSSValue* textAlign = aData->ValueForTextAlign();
     if (textAlign->GetUnit() == eCSSUnit_Null) {
       // align: enum
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
       if (value && value->Type() == nsAttrValue::eEnum)
         textAlign->SetIntValue(value->GetEnumValue(), eCSSUnit_Enumerated);
     }
   }
-  if (aData->mSIDs & NS_STYLE_INHERIT_BIT(TextReset)) {
+  if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Display)) {
     nsCSSValue* verticalAlign = aData->ValueForVerticalAlign();
     if (verticalAlign->GetUnit() == eCSSUnit_Null) {
       // valign: enum
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::valign);
       if (value && value->Type() == nsAttrValue::eEnum)
         verticalAlign->SetIntValue(value->GetEnumValue(), eCSSUnit_Enumerated);
     }
   }
--- a/dom/html/HTMLTableSectionElement.cpp
+++ b/dom/html/HTMLTableSectionElement.cpp
@@ -181,17 +181,17 @@ HTMLTableSectionElement::MapAttributesIn
     nsCSSValue* textAlign = aData->ValueForTextAlign();
     if (textAlign->GetUnit() == eCSSUnit_Null) {
       // align: enum
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
       if (value && value->Type() == nsAttrValue::eEnum)
         textAlign->SetIntValue(value->GetEnumValue(), eCSSUnit_Enumerated);
     }
   }
-  if (aData->mSIDs & NS_STYLE_INHERIT_BIT(TextReset)) {
+  if (aData->mSIDs & NS_STYLE_INHERIT_BIT(Display)) {
     nsCSSValue* verticalAlign = aData->ValueForVerticalAlign();
     if (verticalAlign->GetUnit() == eCSSUnit_Null) {
       // valign: enum
       const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::valign);
       if (value && value->Type() == nsAttrValue::eEnum)
         verticalAlign->SetIntValue(value->GetEnumValue(), eCSSUnit_Enumerated);
     }
   }
new file mode 100644
--- /dev/null
+++ b/dom/html/crashtests/1237633.html
@@ -0,0 +1,1 @@
+<img srcset="data:,a 2400w" sizes="(min-width: 1px) calc(300px - 100vw)">
--- a/dom/html/crashtests/crashtests.list
+++ b/dom/html/crashtests/crashtests.list
@@ -71,9 +71,9 @@ load 865147.html
 load 877910.html
 load 903106.html
 load 916322-1.html
 load 916322-2.html
 load 1032654.html
 pref(dom.image.srcset.enabled,true) load 1141260.html
 load 1228876.html
 load 1230110.html
-
+load 1237633.html
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -1426,42 +1426,37 @@ nsGenericHTMLElement::sBackgroundColorAt
   { &nsGkAtoms::bgcolor },
   { nullptr }
 };
 
 void
 nsGenericHTMLElement::MapImageAlignAttributeInto(const nsMappedAttributes* aAttributes,
                                                  nsRuleData* aRuleData)
 {
-  if (aRuleData->mSIDs & (NS_STYLE_INHERIT_BIT(Display) |
-                          NS_STYLE_INHERIT_BIT(TextReset))) {
+  if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Display)) {
     const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
     if (value && value->Type() == nsAttrValue::eEnum) {
       int32_t align = value->GetEnumValue();
-      if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(Display)) {
-        nsCSSValue* cssFloat = aRuleData->ValueForFloat();
-        if (cssFloat->GetUnit() == eCSSUnit_Null) {
-          if (align == NS_STYLE_TEXT_ALIGN_LEFT) {
-            cssFloat->SetIntValue(NS_STYLE_FLOAT_LEFT, eCSSUnit_Enumerated);
-          } else if (align == NS_STYLE_TEXT_ALIGN_RIGHT) {
-            cssFloat->SetIntValue(NS_STYLE_FLOAT_RIGHT, eCSSUnit_Enumerated);
-          }
+      nsCSSValue* cssFloat = aRuleData->ValueForFloat();
+      if (cssFloat->GetUnit() == eCSSUnit_Null) {
+        if (align == NS_STYLE_TEXT_ALIGN_LEFT) {
+          cssFloat->SetIntValue(NS_STYLE_FLOAT_LEFT, eCSSUnit_Enumerated);
+        } else if (align == NS_STYLE_TEXT_ALIGN_RIGHT) {
+          cssFloat->SetIntValue(NS_STYLE_FLOAT_RIGHT, eCSSUnit_Enumerated);
         }
       }
-      if (aRuleData->mSIDs & NS_STYLE_INHERIT_BIT(TextReset)) {
-        nsCSSValue* verticalAlign = aRuleData->ValueForVerticalAlign();
-        if (verticalAlign->GetUnit() == eCSSUnit_Null) {
-          switch (align) {
-          case NS_STYLE_TEXT_ALIGN_LEFT:
-          case NS_STYLE_TEXT_ALIGN_RIGHT:
-            break;
-          default:
-            verticalAlign->SetIntValue(align, eCSSUnit_Enumerated);
-            break;
-          }
+      nsCSSValue* verticalAlign = aRuleData->ValueForVerticalAlign();
+      if (verticalAlign->GetUnit() == eCSSUnit_Null) {
+        switch (align) {
+        case NS_STYLE_TEXT_ALIGN_LEFT:
+        case NS_STYLE_TEXT_ALIGN_RIGHT:
+          break;
+        default:
+          verticalAlign->SetIntValue(align, eCSSUnit_Enumerated);
+          break;
         }
       }
     }
   }
 }
 
 void
 nsGenericHTMLElement::MapDivAlignAttributeInto(const nsMappedAttributes* aAttributes,
--- a/dom/html/test/file_fullscreen-backdrop.html
+++ b/dom/html/test/file_fullscreen-backdrop.html
@@ -3,19 +3,28 @@
 <head>
   <meta charset="UTF-8">
   <title>Test for Bug 1064843</title>
   <style id="style"></style>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
   <script type="text/javascript" src="file_fullscreen-utils.js"></script>
+  <style>
+    html {
+      overflow: hidden;
+    }
+    #placeholder {
+      height: 1000vh;
+    }
+  </style>
 </head>
 <body>
 <div id="fullscreen"></div>
+<div id="placeholder"></div>
 <script>
 
 const gStyle = document.getElementById("style");
 const gFullscreen = document.getElementById("fullscreen");
 
 function is(a, b, msg) {
   opener.is(a, b, "[backdrop] " + msg);
 }
@@ -72,16 +81,25 @@ function enterFullscreen() {
   setBackdropStyle("");
   info("Content should return to black because we restore the backdrop");
   assertWindowPureColor(window, "black");
 
   gFullscreen.style.display = "none";
   info("The backdrop should disappear with the fullscreen element");
   assertWindowPureColor(window, "white");
 
+  gFullscreen.style.display = "";
+  setBackdropStyle("position: absolute");
+  info("Changing position shouldn't immediately affect the view");
+  assertWindowPureColor(window, "black");
+
+  window.scroll(0, screen.height);
+  info("Scrolled up the absolutely-positioned element");
+  assertWindowPureColor(window, "white");
+
   addFullscreenChangeContinuation("exit", exitFullscreen);
   document.exitFullscreen();
 }
 
 function exitFullscreen() {
   opener.nextTest();
 }
 </script>
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -10448,17 +10448,17 @@ DatabaseConnection::GetFreelistCount(Cac
 }
 
 void
 DatabaseConnection::Close()
 {
   AssertIsOnConnectionThread();
   MOZ_ASSERT(mStorageConnection);
   MOZ_ASSERT(!mDEBUGSavepointCount);
-  MOZ_RELEASE_ASSERT(!mInWriteTransaction);
+  MOZ_ASSERT(!mInWriteTransaction);
 
   PROFILER_LABEL("IndexedDB",
                  "DatabaseConnection::Close",
                  js::ProfileEntry::Category::STORAGE);
 
   if (mUpdateRefcountFunction) {
     MOZ_ALWAYS_SUCCEEDS(
       mStorageConnection->RemoveFunction(
@@ -12134,17 +12134,17 @@ ConnectionPool::ScheduleQueuedTransactio
   AdjustIdleTimer();
 }
 
 void
 ConnectionPool::NoteIdleDatabase(DatabaseInfo* aDatabaseInfo)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aDatabaseInfo);
-  MOZ_RELEASE_ASSERT(!aDatabaseInfo->TotalTransactionCount());
+  MOZ_ASSERT(!aDatabaseInfo->TotalTransactionCount());
   MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mThread);
   MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
   MOZ_ASSERT(!mIdleDatabases.Contains(aDatabaseInfo));
 
   PROFILER_LABEL("IndexedDB",
                  "ConnectionPool::NoteIdleDatabase",
                  js::ProfileEntry::Category::STORAGE);
 
@@ -12308,17 +12308,17 @@ ConnectionPool::MaybeFireCallback(Databa
   return true;
 }
 
 void
 ConnectionPool::PerformIdleDatabaseMaintenance(DatabaseInfo* aDatabaseInfo)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aDatabaseInfo);
-  MOZ_RELEASE_ASSERT(!aDatabaseInfo->TotalTransactionCount());
+  MOZ_ASSERT(!aDatabaseInfo->TotalTransactionCount());
   MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mThread);
   MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
   MOZ_ASSERT(aDatabaseInfo->mIdle);
   MOZ_ASSERT(!aDatabaseInfo->mCloseOnIdle);
   MOZ_ASSERT(!aDatabaseInfo->mClosing);
   MOZ_ASSERT(mIdleDatabases.Contains(aDatabaseInfo));
   MOZ_ASSERT(!mDatabasesPerformingIdleMaintenance.Contains(aDatabaseInfo));
 
@@ -12335,17 +12335,17 @@ ConnectionPool::PerformIdleDatabaseMaint
                                                  NS_DISPATCH_NORMAL));
 }
 
 void
 ConnectionPool::CloseDatabase(DatabaseInfo* aDatabaseInfo)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aDatabaseInfo);
-  MOZ_RELEASE_ASSERT(!aDatabaseInfo->TotalTransactionCount());
+  MOZ_ASSERT(!aDatabaseInfo->TotalTransactionCount());
   MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mThread);
   MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable);
   MOZ_ASSERT(!aDatabaseInfo->mClosing);
 
   aDatabaseInfo->mIdle = false;
   aDatabaseInfo->mNeedsCheckpoint = false;
   aDatabaseInfo->mClosing = true;
 
@@ -13960,17 +13960,17 @@ Database::RecvPBackgroundIDBTransactionC
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
   MOZ_ASSERT(!aObjectStoreNames.IsEmpty());
   MOZ_ASSERT(aMode == IDBTransaction::READ_ONLY ||
              aMode == IDBTransaction::READ_WRITE ||
              aMode == IDBTransaction::READ_WRITE_FLUSH ||
              aMode == IDBTransaction::CLEANUP);
-  MOZ_RELEASE_ASSERT(!mClosed);
+  MOZ_ASSERT(!mClosed);
 
   if (IsInvalidated()) {
     // This is an expected race. We don't want the child to die here, just don't
     // actually do any work.
     return true;
   }
 
   if (!gConnectionPool) {
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -652,68 +652,16 @@ private:
   // This method is only called by the IPDL message machinery.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) override
   {
     // Nothing needs to be done here.
   }
 };
 
-class EmptyBlobImpl final
-  : public BlobImplBase
-{
-public:
-  explicit EmptyBlobImpl(const nsAString& aContentType)
-    : BlobImplBase(aContentType, 0)
-  {
-    mImmutable = true;
-  }
-
-  EmptyBlobImpl(const nsAString& aName,
-                const nsAString& aContentType,
-                int64_t aLastModifiedDate)
-    : BlobImplBase(aName, aContentType, 0, aLastModifiedDate)
-  {
-    mImmutable = true;
-  }
-
-private:
-  virtual already_AddRefed<BlobImpl>
-  CreateSlice(uint64_t /* aStart */,
-              uint64_t aLength,
-              const nsAString& aContentType,
-              ErrorResult& /* aRv */) override
-  {
-    MOZ_ASSERT(!aLength);
-
-    RefPtr<BlobImpl> sliceImpl = new EmptyBlobImpl(aContentType);
-
-    DebugOnly<bool> isMutable;
-    MOZ_ASSERT(NS_SUCCEEDED(sliceImpl->GetMutable(&isMutable)));
-    MOZ_ASSERT(!isMutable);
-
-    return sliceImpl.forget();
-  }
-
-  virtual void
-  GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) override
-  {
-    if (NS_WARN_IF(!aStream)) {
-      aRv.Throw(NS_ERROR_FAILURE);
-      return;
-    }
-
-    nsString emptyString;
-    aRv = NS_NewStringInputStream(aStream, emptyString);
-    if (NS_WARN_IF(aRv.Failed())) {
-      return;
-    }
-  }
-};
-
 struct MOZ_STACK_CLASS CreateBlobImplMetadata final
 {
   nsString mContentType;
   nsString mName;
   uint64_t mLength;
   int64_t mLastModifiedDate;
   bool mHasRecursed;
   const bool mIsSameProcessActor;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -101,22 +101,16 @@
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::services;
 using namespace mozilla::widget;
 using namespace mozilla::jsipc;
 
-#if DEUBG
-  #define LOG(args...) printf_stderr(args)
-#else
-  #define LOG(...)
-#endif
-
 // The flags passed by the webProgress notifications are 16 bits shifted
 // from the ones registered by webProgressListeners.
 #define NOTIFY_FLAG_SHIFT 16
 
 class OpenFileAndSendFDRunnable : public nsRunnable
 {
     const nsString mPath;
     RefPtr<TabParent> mTabParent;
@@ -455,131 +449,16 @@ TabParent::IsVisible() const
     return false;
   }
 
   bool visible = false;
   frameLoader->GetVisible(&visible);
   return visible;
 }
 
-static void LogChannelRelevantInfo(nsIURI* aURI,
-                                   nsIPrincipal* aLoadingPrincipal,
-                                   nsIPrincipal* aChannelResultPrincipal,
-                                   nsContentPolicyType aContentPolicyType) {
-  nsCString loadingOrigin;
-  aLoadingPrincipal->GetOrigin(loadingOrigin);
-
-  nsCString uriString;
-  aURI->GetAsciiSpec(uriString);
-  LOG("Loading %s from origin %s (type: %d)\n", uriString.get(),
-                                                loadingOrigin.get(),
-                                                aContentPolicyType);
-
-  nsCString resultPrincipalOrigin;
-  aChannelResultPrincipal->GetOrigin(resultPrincipalOrigin);
-  LOG("Result principal origin: %s\n", resultPrincipalOrigin.get());
-}
-
-// This is similar to nsIScriptSecurityManager.getChannelResultPrincipal
-// but taking signedPkg into account. The reason we can't rely on channel
-// loadContext/loadInfo is it's dangerous to mutate them on parent process.
-static already_AddRefed<nsIPrincipal>
-GetChannelPrincipalWithSingedPkg(nsIChannel* aChannel, const nsACString& aSignedPkg)
-{
-  NeckoOriginAttributes neckoAttrs;
-  NS_GetOriginAttributes(aChannel, neckoAttrs);
-
-  PrincipalOriginAttributes attrs;
-  attrs.InheritFromNecko(neckoAttrs);
-  attrs.mSignedPkg = NS_ConvertUTF8toUTF16(aSignedPkg);
-
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
-  NS_ENSURE_SUCCESS(rv, nullptr);
-
-  nsCOMPtr<nsIPrincipal> principal =
-    BasePrincipal::CreateCodebasePrincipal(uri, attrs);
-
-  return principal.forget();
-}
-
-bool
-TabParent::ShouldSwitchProcess(nsIChannel* aChannel, const nsACString& aSignedPkg)
-{
-  // If we lack of any information which is required to decide the need of
-  // process switch, consider that we should switch process.
-
-  // Prepare the channel loading principal.
-  nsCOMPtr<nsILoadInfo> loadInfo;
-  aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
-  NS_ENSURE_TRUE(loadInfo, true);
-  nsCOMPtr<nsIPrincipal> loadingPrincipal;
-  loadInfo->GetLoadingPrincipal(getter_AddRefs(loadingPrincipal));
-  NS_ENSURE_TRUE(loadingPrincipal, true);
-
-  // Prepare the channel result principal.
-  nsCOMPtr<nsIPrincipal> channelPrincipal =
-    GetChannelPrincipalWithSingedPkg(aChannel, aSignedPkg);
-
-  // Log the debug info which is used to decide the need of proces switch.
-  nsCOMPtr<nsIURI> uri;
-  aChannel->GetURI(getter_AddRefs(uri));
-  LogChannelRelevantInfo(uri, loadingPrincipal, channelPrincipal,
-                         loadInfo->InternalContentPolicyType());
-
-  // Check if the signed package is loaded from the same origin.
-  bool sameOrigin = false;
-  loadingPrincipal->Equals(channelPrincipal, &sameOrigin);
-  if (sameOrigin) {
-    LOG("Loading singed package from the same origin. Don't switch process.\n");
-    return false;
-  }
-
-  // If this is not a top level document, there's no need to switch process.
-  if (nsIContentPolicy::TYPE_DOCUMENT != loadInfo->InternalContentPolicyType()) {
-    LOG("Subresource of a document. No need to switch process.\n");
-    return false;
-  }
-
-  DocShellOriginAttributes attrs = OriginAttributesRef();
-  if (attrs.mSignedPkg == NS_ConvertUTF8toUTF16(aSignedPkg)) {
-    // This tab is made for the incoming signed content.
-    return false;
-  }
-
-  return true;
-}
-
-void
-TabParent::OnStartSignedPackageRequest(nsIChannel* aChannel,
-                                       const nsACString& aPackageId)
-{
-  if (!ShouldSwitchProcess(aChannel, aPackageId)) {
-    return;
-  }
-
-  nsCOMPtr<nsIURI> uri;
-  aChannel->GetURI(getter_AddRefs(uri));
-
-  aChannel->Cancel(NS_BINDING_FAILED);
-
-  nsCString uriString;
-  uri->GetAsciiSpec(uriString);
-  LOG("We decide to switch process. Call nsFrameLoader::SwitchProcessAndLoadURIs: %s\n",
-       uriString.get());
-
-  RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
-  NS_ENSURE_TRUE_VOID(frameLoader);
-
-  nsresult rv = frameLoader->SwitchProcessAndLoadURI(uri, aPackageId);
-  if (NS_FAILED(rv)) {
-    NS_WARNING("Failed to switch process.");
-  }
-}
-
 void
 TabParent::DestroyInternal()
 {
   IMEStateManager::OnTabParentDestroying(this);
 
   RemoveWindowListeners();
 
   // If this fails, it's most likely due to a content-process crash,
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -539,21 +539,16 @@ public:
 
   void AddInitialDnDDataTo(DataTransfer* aDataTransfer);
 
   void TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface,
                              int32_t& aDragAreaX, int32_t& aDragAreaY);
 
   layout::RenderFrameParent* GetRenderFrame();
 
-  // Called by HttpChannelParent. The function may use a new process to
-  // reload the URI associated with the given channel.
-  void OnStartSignedPackageRequest(nsIChannel* aChannel,
-                                   const nsACString& aPackageId);
-
   void AudioChannelChangeNotification(nsPIDOMWindowOuter* aWindow,
                                       AudioChannel aAudioChannel,
                                       float aVolume,
                                       bool aMuted);
   bool SetRenderFrame(PRenderFrameParent* aRFParent);
   bool GetRenderFrameInfo(TextureFactoryIdentifier* aTextureFactoryIdentifier,
                           uint64_t* aLayersId);
 
@@ -587,20 +582,16 @@ protected:
                                  const int32_t& aCx, const int32_t& aCy) override;
 
   virtual bool RecvAudioChannelActivityNotification(const uint32_t& aAudioChannel,
                                                     const bool& aActive) override;
 
   bool InitBrowserConfiguration(const nsCString& aURI,
                                 BrowserConfiguration& aConfiguration);
 
-  // Decide whether we have to use a new process to reload the URI associated
-  // with the given channel.
-  bool ShouldSwitchProcess(nsIChannel* aChannel, const nsACString& aSignedPkg);
-
   ContentCacheInParent mContentCache;
 
   nsIntRect mRect;
   ScreenIntSize mDimensions;
   ScreenOrientationInternal mOrientation;
   float mDPI;
   CSSToLayoutDeviceScale mDefaultScale;
   bool mUpdatedDimensions;
--- a/dom/locales/en-US/chrome/plugins.properties
+++ b/dom/locales/en-US/chrome/plugins.properties
@@ -24,8 +24,11 @@ learn_more_label=Learn More
 # GMP Plugins
 gmp_license_info=License information
 
 openH264_name=OpenH264 Video Codec provided by Cisco Systems, Inc.
 openH264_description2=This plugin is automatically installed by Mozilla to comply with the WebRTC specification and to enable WebRTC calls with devices that require the H.264 video codec. Visit http://www.openh264.org/ to view the codec source code and learn more about the implementation.
 
 eme-adobe_name=Primetime Content Decryption Module provided by Adobe Systems, Incorporated
 eme-adobe_description=Play back protected web video.
+
+widevine_name=WidevineCdm
+widevine_description=Widevine Content Decryption Module provided by Google Inc.
--- a/dom/media/AudioCompactor.h
+++ b/dom/media/AudioCompactor.h
@@ -12,17 +12,25 @@
 
 namespace mozilla {
 
 class AudioCompactor
 {
 public:
   explicit AudioCompactor(MediaQueue<AudioData>& aQueue)
     : mQueue(aQueue)
-  { }
+  {
+    // Determine padding size used by AlignedBuffer.
+    size_t paddedSize = AlignedAudioBuffer::AlignmentPaddingSize();
+    mSamplesPadding = paddedSize / sizeof(AudioDataValue);
+    if (mSamplesPadding * sizeof(AudioDataValue) < paddedSize) {
+      // Round up.
+      mSamplesPadding++;
+    }
+  }
 
   // Push audio data into the underlying queue with minimal heap allocation
   // slop.  This method is responsible for allocating AudioDataValue[] buffers.
   // The caller must provide a functor to copy the data into the buffers.  The
   // functor must provide the following signature:
   //
   //   uint32_t operator()(AudioDataValue *aBuffer, uint32_t aSamples);
   //
@@ -36,17 +44,23 @@ public:
             uint32_t aFrames, uint32_t aChannels, CopyFunc aCopyFunc)
   {
     // If we are losing more than a reasonable amount to padding, try to chunk
     // the data.
     size_t maxSlop = AudioDataSize(aFrames, aChannels) / MAX_SLOP_DIVISOR;
 
     while (aFrames > 0) {
       uint32_t samples = GetChunkSamples(aFrames, aChannels, maxSlop);
-      auto buffer = MakeUnique<AudioDataValue[]>(samples);
+      if (aFrames * aChannels > mSamplesPadding) {
+        samples -= mSamplesPadding;
+      }
+      AlignedAudioBuffer buffer(samples);
+      if (!buffer) {
+        return false;
+      }
 
       // Copy audio data to buffer using caller-provided functor.
       uint32_t framesCopied = aCopyFunc(buffer.get(), samples);
 
       NS_ASSERTION(framesCopied <= aFrames, "functor copied too many frames");
 
       CheckedInt64 duration = FramesToUsecs(framesCopied, aSampleRate);
       if (!duration.isValid()) {
@@ -110,13 +124,14 @@ private:
   }
 
   static size_t AudioDataSize(uint32_t aFrames, uint32_t aChannels)
   {
     return aFrames * BytesPerFrame(aChannels);
   }
 
   MediaQueue<AudioData> &mQueue;
+  size_t mSamplesPadding;
 };
 
 } // namespace mozilla
 
 #endif // AudioCompactor_h
new file mode 100644
--- /dev/null
+++ b/dom/media/AudioConverter.cpp
@@ -0,0 +1,229 @@
+/* -*- 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 "AudioConverter.h"
+#include <string.h>
+
+/*
+ *  Parts derived from MythTV AudioConvert Class
+ *  Created by Jean-Yves Avenard.
+ *
+ *  Copyright (C) Bubblestuff Pty Ltd 2013
+ *  Copyright (C) foobum@gmail.com 2010
+ */
+
+namespace mozilla {
+
+AudioConverter::AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut)
+  : mIn(aIn)
+  , mOut(aOut)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aIn.Rate() == aOut.Rate() &&
+                        aIn.Format() == aOut.Format() &&
+                        aIn.Interleaved() == aOut.Interleaved(),
+                        "No format or rate conversion is supported at this stage");
+  MOZ_DIAGNOSTIC_ASSERT((aIn.Channels() > aOut.Channels() && aOut.Channels() <= 2) ||
+                        aIn.Channels() == aOut.Channels(),
+                        "Only downmixing to mono or stereo is supported at this stage");
+  MOZ_DIAGNOSTIC_ASSERT(aOut.Interleaved(), "planar audio format not supported");
+  mIn.Layout().MappingTable(mOut.Layout(), mChannelOrderMap);
+}
+
+bool
+AudioConverter::CanWorkInPlace() const
+{
+  return mIn.Channels() * mIn.Rate() * AudioConfig::SampleSize(mIn.Format()) >=
+    mOut.Channels() * mOut.Rate() * AudioConfig::SampleSize(mOut.Format());
+}
+
+size_t
+AudioConverter::Process(void* aOut, const void* aIn, size_t aBytes)
+{
+  if (!CanWorkInPlace()) {
+    return 0;
+  }
+  if (mIn.Channels() > mOut.Channels()) {
+    return DownmixAudio(aOut, aIn, aBytes);
+  } else if (mIn.Layout() != mOut.Layout() &&
+      CanReorderAudio()) {
+    ReOrderInterleavedChannels(aOut, aIn, aBytes);
+  }
+  return aBytes;
+}
+
+// Reorder interleaved channels.
+// Can work in place (e.g aOut == aIn).
+template <class AudioDataType>
+void
+_ReOrderInterleavedChannels(AudioDataType* aOut, const AudioDataType* aIn,
+                            uint32_t aFrames, uint32_t aChannels,
+                            const uint8_t* aChannelOrderMap)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aChannels <= MAX_AUDIO_CHANNELS);
+  AudioDataType val[MAX_AUDIO_CHANNELS];
+  for (uint32_t i = 0; i < aFrames; i++) {
+    for (uint32_t j = 0; j < aChannels; j++) {
+      val[j] = aIn[aChannelOrderMap[j]];
+    }
+    for (uint32_t j = 0; j < aChannels; j++) {
+      aOut[j] = val[j];
+    }
+    aOut += aChannels;
+    aIn += aChannels;
+  }
+}
+
+void
+AudioConverter::ReOrderInterleavedChannels(void* aOut, const void* aIn,
+                                           size_t aDataSize) const
+{
+  MOZ_DIAGNOSTIC_ASSERT(mIn.Channels() == mOut.Channels());
+
+  if (mOut.Layout() == mIn.Layout()) {
+    return;
+  }
+  if (mOut.Channels() == 1) {
+    // If channel count is 1, planar and non-planar formats are the same and
+    // there's nothing to reorder.
+    if (aOut != aIn) {
+      memmove(aOut, aIn, aDataSize);
+    }
+    return;
+  }
+
+  uint32_t bits = AudioConfig::FormatToBits(mOut.Format());
+  switch (bits) {
+    case 8:
+      _ReOrderInterleavedChannels((uint8_t*)aOut, (const uint8_t*)aIn,
+                                  aDataSize/sizeof(uint8_t)/mIn.Channels(),
+                                  mIn.Channels(), mChannelOrderMap);
+      break;
+    case 16:
+      _ReOrderInterleavedChannels((int16_t*)aOut,(const int16_t*)aIn,
+                                  aDataSize/sizeof(int16_t)/mIn.Channels(),
+                                  mIn.Channels(), mChannelOrderMap);
+      break;
+    default:
+      MOZ_DIAGNOSTIC_ASSERT(AudioConfig::SampleSize(mOut.Format()) == 4);
+      _ReOrderInterleavedChannels((int32_t*)aOut,(const int32_t*)aIn,
+                                  aDataSize/sizeof(int32_t)/mIn.Channels(),
+                                  mIn.Channels(), mChannelOrderMap);
+      break;
+  }
+}
+
+static inline int16_t clipTo15(int32_t aX)
+{
+  return aX < -32768 ? -32768 : aX <= 32767 ? aX : 32767;
+}
+
+size_t
+AudioConverter::DownmixAudio(void* aOut, const void* aIn, size_t aDataSize) const
+{
+  MOZ_ASSERT(mIn.Format() == AudioConfig::FORMAT_S16 ||
+             mIn.Format() == AudioConfig::FORMAT_FLT);
+  MOZ_ASSERT(mIn.Channels() >= mOut.Channels());
+  MOZ_ASSERT(mIn.Layout() == AudioConfig::ChannelLayout(mIn.Channels()),
+             "Can only downmix input data in SMPTE layout");
+  MOZ_ASSERT(mOut.Layout() == AudioConfig::ChannelLayout(2) ||
+             mOut.Layout() == AudioConfig::ChannelLayout(1));
+
+  uint32_t channels = mIn.Channels();
+  uint32_t frames =
+    aDataSize / AudioConfig::SampleSize(mOut.Format()) / channels;
+
+  if (channels == 1 && mOut.Channels() == 1) {
+    if (aOut != aIn) {
+      memmove(aOut, aIn, aDataSize);
+    }
+    return aDataSize;
+  }
+
+  if (channels > 2) {
+    if (mIn.Format() == AudioConfig::FORMAT_FLT) {
+      // Downmix matrix. Per-row normalization 1 for rows 3,4 and 2 for rows 5-8.
+      static const float dmatrix[6][8][2]= {
+          /*3*/{{0.5858f,0},{0,0.5858f},{0.4142f,0.4142f}},
+          /*4*/{{0.4226f,0},{0,0.4226f},{0.366f, 0.2114f},{0.2114f,0.366f}},
+          /*5*/{{0.6510f,0},{0,0.6510f},{0.4600f,0.4600f},{0.5636f,0.3254f},{0.3254f,0.5636f}},
+          /*6*/{{0.5290f,0},{0,0.5290f},{0.3741f,0.3741f},{0.3741f,0.3741f},{0.4582f,0.2645f},{0.2645f,0.4582f}},
+          /*7*/{{0.4553f,0},{0,0.4553f},{0.3220f,0.3220f},{0.3220f,0.3220f},{0.2788f,0.2788f},{0.3943f,0.2277f},{0.2277f,0.3943f}},
+          /*8*/{{0.3886f,0},{0,0.3886f},{0.2748f,0.2748f},{0.2748f,0.2748f},{0.3366f,0.1943f},{0.1943f,0.3366f},{0.3366f,0.1943f},{0.1943f,0.3366f}},
+      };
+      // Re-write the buffer with downmixed data
+      const float* in = static_cast<const float*>(aIn);
+      float* out = static_cast<float*>(aOut);
+      for (uint32_t i = 0; i < frames; i++) {
+        float sampL = 0.0;
+        float sampR = 0.0;
+        for (uint32_t j = 0; j < channels; j++) {
+          sampL += in[i*mIn.Channels()+j]*dmatrix[mIn.Channels()-3][j][0];
+          sampR += in[i*mIn.Channels()+j]*dmatrix[mIn.Channels()-3][j][1];
+        }
+        *out++ = sampL;
+        *out++ = sampR;
+      }
+    } else if (mIn.Format() == AudioConfig::FORMAT_S16) {
+      // Downmix matrix. Per-row normalization 1 for rows 3,4 and 2 for rows 5-8.
+      // Coefficients in Q14.
+      static const int16_t dmatrix[6][8][2]= {
+          /*3*/{{9598, 0},{0,   9598},{6786,6786}},
+          /*4*/{{6925, 0},{0,   6925},{5997,3462},{3462,5997}},
+          /*5*/{{10663,0},{0,  10663},{7540,7540},{9234,5331},{5331,9234}},
+          /*6*/{{8668, 0},{0,   8668},{6129,6129},{6129,6129},{7507,4335},{4335,7507}},
+          /*7*/{{7459, 0},{0,   7459},{5275,5275},{5275,5275},{4568,4568},{6460,3731},{3731,6460}},
+          /*8*/{{6368, 0},{0,   6368},{4502,4502},{4502,4502},{5514,3184},{3184,5514},{5514,3184},{3184,5514}}
+      };
+      // Re-write the buffer with downmixed data
+      const int16_t* in = static_cast<const int16_t*>(aIn);
+      int16_t* out = static_cast<int16_t*>(aOut);
+      for (uint32_t i = 0; i < frames; i++) {
+        int32_t sampL = 0;
+        int32_t sampR = 0;
+        for (uint32_t j = 0; j < channels; j++) {
+          sampL+=in[i*channels+j]*dmatrix[channels-3][j][0];
+          sampR+=in[i*channels+j]*dmatrix[channels-3][j][1];
+        }
+        *out++ = clipTo15((sampL + 8192)>>14);
+        *out++ = clipTo15((sampR + 8192)>>14);
+      }
+    } else {
+      MOZ_DIAGNOSTIC_ASSERT(false, "Unsupported data type");
+    }
+
+    // If we are to continue downmixing to mono, start working on the output
+    // buffer.
+    aIn = aOut;
+    channels = 2;
+  }
+
+  if (mOut.Channels() == 1) {
+    if (mIn.Format() == AudioConfig::FORMAT_FLT) {
+      const float* in = static_cast<const float*>(aIn);
+      float* out = static_cast<float*>(aOut);
+      for (uint32_t fIdx = 0; fIdx < frames; ++fIdx) {
+        float sample = 0.0;
+        // The sample of the buffer would be interleaved.
+        sample = (in[fIdx*channels] + in[fIdx*channels + 1]) * 0.5;
+        *out++ = sample;
+      }
+    } else if (mIn.Format() == AudioConfig::FORMAT_S16) {
+      const int16_t* in = static_cast<const int16_t*>(aIn);
+      int16_t* out = static_cast<int16_t*>(aOut);
+      for (uint32_t fIdx = 0; fIdx < frames; ++fIdx) {
+        int32_t sample = 0.0;
+        // The sample of the buffer would be interleaved.
+        sample = (in[fIdx*channels] + in[fIdx*channels + 1]) * 0.5;
+        *out++ = sample;
+      }
+    } else {
+      MOZ_DIAGNOSTIC_ASSERT(false, "Unsupported data type");
+    }
+  }
+  return frames * AudioConfig::SampleSize(mOut.Format()) * mOut.Channels();
+}
+
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/media/AudioConverter.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 2; 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/. */
+
+#if !defined(AudioConverter_h)
+#define AudioConverter_h
+
+#include "MediaInfo.h"
+
+namespace mozilla {
+
+template <AudioConfig::SampleFormat T> struct AudioDataBufferTypeChooser;
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_U8>
+{ typedef uint8_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S16>
+{ typedef int16_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24LSB>
+{ typedef int32_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24>
+{ typedef int32_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S32>
+{ typedef int32_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_FLT>
+{ typedef float Type; };
+
+// 'Value' is the type used externally to deal with stored value.
+// AudioDataBuffer can perform conversion between different SampleFormat content.
+template <AudioConfig::SampleFormat Format, typename Value = typename AudioDataBufferTypeChooser<Format>::Type>
+class AudioDataBuffer
+{
+public:
+  AudioDataBuffer() {}
+  AudioDataBuffer(Value* aBuffer, size_t aLength)
+    : mBuffer(aBuffer, aLength)
+  {}
+  explicit AudioDataBuffer(const AudioDataBuffer& aOther)
+    : mBuffer(aOther.mBuffer)
+  {}
+  AudioDataBuffer(AudioDataBuffer&& aOther)
+    : mBuffer(Move(aOther.mBuffer))
+  {}
+  template <AudioConfig::SampleFormat OtherFormat, typename OtherValue>
+  explicit AudioDataBuffer(const AudioDataBuffer<OtherFormat, OtherValue>& other)
+  {
+    // TODO: Convert from different type, may use asm routines.
+    MOZ_CRASH("Conversion not implemented yet");
+  }
+
+  // A u8, s16 and float aligned buffer can only be treated as
+  // FORMAT_U8, FORMAT_S16 and FORMAT_FLT respectively.
+  // So allow them as copy and move constructors.
+  explicit AudioDataBuffer(const AlignedByteBuffer& aBuffer)
+    : mBuffer(aBuffer)
+  {
+    static_assert(Format == AudioConfig::FORMAT_U8,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(const AlignedShortBuffer& aBuffer)
+    : mBuffer(aBuffer)
+  {
+    static_assert(Format == AudioConfig::FORMAT_S16,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(const AlignedFloatBuffer& aBuffer)
+    : mBuffer(aBuffer)
+  {
+    static_assert(Format == AudioConfig::FORMAT_FLT,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(AlignedByteBuffer&& aBuffer)
+    : mBuffer(Move(aBuffer))
+  {
+    static_assert(Format == AudioConfig::FORMAT_U8,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(AlignedShortBuffer&& aBuffer)
+    : mBuffer(Move(aBuffer))
+  {
+    static_assert(Format == AudioConfig::FORMAT_S16,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(const AlignedFloatBuffer&& aBuffer)
+    : mBuffer(Move(aBuffer))
+  {
+    static_assert(Format == AudioConfig::FORMAT_FLT,
+                  "Conversion not implemented yet");
+  }
+
+  Value* Data() const { return mBuffer.Data(); }
+  size_t Length() const { return mBuffer.Length(); }
+  size_t Size() const { return mBuffer.Size(); }
+  AlignedBuffer<Value> Forget()
+  {
+    // Correct type -> Just give values as-is.
+    return Move(mBuffer);
+  }
+private:
+  AlignedBuffer<Value> mBuffer;
+};
+
+typedef AudioDataBuffer<AudioConfig::FORMAT_DEFAULT> AudioSampleBuffer;
+
+class AudioConverter {
+public:
+  AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut);
+
+  // Attempt to convert the AudioDataBuffer in place.
+  // Will return 0 if the conversion wasn't possible.
+  // Process may allocate memory internally should intermediary steps be
+  // required.
+  template <AudioConfig::SampleFormat Type, typename Value>
+  size_t Process(AudioDataBuffer<Type, Value>& aBuffer)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format() && mIn.Format() == Type);
+    return Process(aBuffer.Data(), aBuffer.Data(), aBuffer.Size());
+  }
+  template <typename Value>
+  size_t Process(Value* aBuffer, size_t aSamples)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format());
+    return Process(aBuffer, aBuffer, aSamples * AudioConfig::SampleSize(mIn.Format()));
+  }
+  bool CanWorkInPlace() const;
+  bool CanReorderAudio() const
+  {
+    return mIn.Layout().MappingTable(mOut.Layout());
+  }
+
+private:
+  const AudioConfig mIn;
+  const AudioConfig mOut;
+  uint8_t mChannelOrderMap[MAX_AUDIO_CHANNELS];
+  /**
+   * Process
+   * Parameters:
+   * aOut  : destination buffer where converted samples will be copied
+   * aIn   : source buffer
+   * aBytes: size in bytes of source buffer
+   *
+   * Return Value: size in bytes of samples converted or 0 if error
+   */
+  size_t Process(void* aOut, const void* aIn, size_t aBytes);
+  void ReOrderInterleavedChannels(void* aOut, const void* aIn, size_t aDataSize) const;
+  size_t DownmixAudio(void* aOut, const void* aIn, size_t aDataSize) const;
+};
+
+} // namespace mozilla
+
+#endif /* AudioConverter_h */
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/Monitor.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Snprintf.h"
 #include <algorithm>
 #include "mozilla/Telemetry.h"
 #include "CubebUtils.h"
 #include "nsPrintfCString.h"
 #include "gfxPrefs.h"
+#include "AudioConverter.h"
 
 namespace mozilla {
 
 #undef LOG
 #undef LOGW
 
 LazyLogModule gAudioStreamLog("AudioStream");
 // For simple logs
@@ -347,16 +348,19 @@ AudioStream::Init(uint32_t aNumChannels,
   if (params.stream_type == CUBEB_STREAM_TYPE_MAX) {
     return NS_ERROR_INVALID_ARG;
   }
 #endif
 
   params.format = ToCubebFormat<AUDIO_OUTPUT_FORMAT>::value;
   mAudioClock.Init();
 
+  AudioConfig inConfig(mChannels, mInRate);
+  AudioConfig outConfig(mOutChannels, mOutRate);
+  mAudioConverter = MakeUnique<AudioConverter>(inConfig, outConfig);
   return OpenCubeb(params);
 }
 
 // This code used to live inside AudioStream::Init(), but on Mac (others?)
 // it has been known to take 300-800 (or even 8500) ms to execute(!)
 nsresult
 AudioStream::OpenCubeb(cubeb_stream_params &aParams)
 {
@@ -552,20 +556,20 @@ AudioStream::Downmix(Chunk* aChunk)
     LOGW("mismatched sample %u, mInRate=%u", aChunk->Rate(), mInRate);
     return false;
   }
 
   if (aChunk->Channels() > 8) {
     return false;
   }
 
-  if (aChunk->Channels() > 2 && aChunk->Channels() <= 8) {
-    DownmixAudioToStereo(aChunk->GetWritable(),
-                         aChunk->Channels(),
-                         aChunk->Frames());
+  if (aChunk->Channels() > 2) {
+    MOZ_ASSERT(mAudioConverter);
+    mAudioConverter->Process(aChunk->GetWritable(),
+                             aChunk->Channels() * aChunk->Frames());
   }
 
   if (aChunk->Channels() >= 2 && mIsMonoAudioEnabled) {
     DownmixStereoToMono(aChunk->GetWritable(), aChunk->Frames());
   }
 
   return true;
 }
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -24,16 +24,18 @@ struct CubebDestroyPolicy
 {
   void operator()(cubeb_stream* aStream) const {
     cubeb_stream_destroy(aStream);
   }
 };
 
 class AudioStream;
 class FrameHistory;
+class AudioConfig;
+class AudioConverter;
 
 class AudioClock
 {
 public:
   explicit AudioClock(AudioStream* aStream);
   // Initialize the clock with the current AudioStream. Need to be called
   // before querying the clock. Called on the audio thread.
   void Init();
@@ -362,17 +364,19 @@ private:
     STOPPED,     // Stopped by a call to Pause().
     DRAINED,     // StateCallback has indicated that the drain is complete.
     ERRORED,     // Stream disabled due to an internal error.
     SHUTDOWN     // Shutdown has been called
   };
 
   StreamState mState;
   bool mIsFirst;
-  // Get this value from the preferece, if true, we would downmix the stereo.
+  // Get this value from the preference, if true, we would downmix the stereo.
   bool mIsMonoAudioEnabled;
 
   DataSource& mDataSource;
+
+  UniquePtr<AudioConverter> mAudioConverter;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/MP3Demuxer.cpp
+++ b/dom/media/MP3Demuxer.cpp
@@ -342,17 +342,17 @@ MP3TrackDemuxer::StreamLength() const {
 TimeUnit
 MP3TrackDemuxer::Duration() const {
   if (!mNumParsedFrames) {
     return TimeUnit::FromMicroseconds(-1);
   }
 
   int64_t numFrames = 0;
   const auto numAudioFrames = mParser.VBRInfo().NumAudioFrames();
-  if (numAudioFrames) {
+  if (mParser.VBRInfo().IsValid()) {
     // VBR headers don't include the VBR header frame.
     numFrames = numAudioFrames.value() + 1;
   } else {
     const int64_t streamLen = StreamLength();
     if (streamLen < 0) {
       // Unknown length, we can't estimate duration.
       return TimeUnit::FromMicroseconds(-1);
     }
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -7,17 +7,16 @@
 #include "MediaData.h"
 #include "MediaInfo.h"
 #ifdef MOZ_OMX_DECODER
 #include "GrallocImages.h"
 #include "mozilla/layers/TextureClient.h"
 #endif
 #include "VideoUtils.h"
 #include "ImageContainer.h"
-#include "mozilla/UniquePtrExtensions.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include <cutils/properties.h>
 #endif
 #include <stdint.h>
 
 namespace mozilla {
 
@@ -42,17 +41,18 @@ AudioData::EnsureAudioBuffer()
       data[j*mFrames + i] = mAudioData[i*mChannels + j];
     }
   }
 }
 
 size_t
 AudioData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
-  size_t size = aMallocSizeOf(this) + aMallocSizeOf(mAudioData.get());
+  size_t size =
+    aMallocSizeOf(this) + mAudioData.SizeOfExcludingThis(aMallocSizeOf);
   if (mAudioBuffer) {
     size += mAudioBuffer->SizeOfIncludingThis(aMallocSizeOf);
   }
   return size;
 }
 
 bool
 AudioData::IsAudible() const
@@ -482,186 +482,99 @@ VideoData::Create(const VideoInfo& aInfo
   RefPtr<layers::GrallocImage> image = new layers::GrallocImage();
   image->SetData(aBuffer, aPicture.Size());
   v->mImage = image;
 
   return v.forget();
 }
 #endif  // MOZ_OMX_DECODER
 
-// Alignment value - 1. 0 means that data isn't aligned.
-// For 32-bytes aligned, use 31U.
-#define RAW_DATA_ALIGNMENT 31U
-
 MediaRawData::MediaRawData()
   : MediaData(RAW_DATA, 0)
   , mCrypto(mCryptoInternal)
-  , mData(nullptr)
-  , mSize(0)
-  , mBuffer(nullptr)
-  , mCapacity(0)
 {
 }
 
 MediaRawData::MediaRawData(const uint8_t* aData, size_t aSize)
   : MediaData(RAW_DATA, 0)
   , mCrypto(mCryptoInternal)
-  , mData(nullptr)
-  , mSize(0)
-  , mBuffer(nullptr)
-  , mCapacity(0)
+  , mBuffer(aData, aSize)
 {
-  if (!EnsureCapacity(aSize)) {
-    return;
-  }
-
-  // We ensure sufficient capacity above so this shouldn't fail.
-  memcpy(mData, aData, aSize);
-  mSize = aSize;
 }
 
 already_AddRefed<MediaRawData>
 MediaRawData::Clone() const
 {
   RefPtr<MediaRawData> s = new MediaRawData;
   s->mTimecode = mTimecode;
   s->mTime = mTime;
   s->mDuration = mDuration;
   s->mOffset = mOffset;
   s->mKeyframe = mKeyframe;
   s->mExtraData = mExtraData;
   s->mCryptoInternal = mCryptoInternal;
   s->mTrackInfo = mTrackInfo;
-  if (mSize) {
-    if (!s->EnsureCapacity(mSize)) {
-      return nullptr;
-    }
-
-    memcpy(s->mData, mData, mSize);
-    s->mSize = mSize;
+  if (!s->mBuffer.Append(mBuffer.Data(), mBuffer.Length())) {
+    return nullptr;
   }
   return s.forget();
 }
 
-// EnsureCapacity ensures that the buffer is big enough to hold
-// aSize. It doesn't set the mSize. It's up to the caller to adjust it.
-bool
-MediaRawData::EnsureCapacity(size_t aSize)
-{
-  const size_t sizeNeeded = aSize + RAW_DATA_ALIGNMENT * 2;
-
-  if (mData && mCapacity >= sizeNeeded) {
-    return true;
-  }
-  auto newBuffer = MakeUniqueFallible<uint8_t[]>(sizeNeeded);
-  if (!newBuffer) {
-    return false;
-  }
-
-  // Find alignment address.
-  const uintptr_t alignmask = RAW_DATA_ALIGNMENT;
-  uint8_t* newData = reinterpret_cast<uint8_t*>(
-    (reinterpret_cast<uintptr_t>(newBuffer.get()) + alignmask) & ~alignmask);
-  MOZ_ASSERT(uintptr_t(newData) % (RAW_DATA_ALIGNMENT+1) == 0);
-  memcpy(newData, mData, mSize);
-
-  mBuffer = Move(newBuffer);
-  mCapacity = sizeNeeded;
-  mData = newData;
-
-  return true;
-}
-
 MediaRawData::~MediaRawData()
 {
 }
 
 size_t
 MediaRawData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t size = aMallocSizeOf(this);
-  size += aMallocSizeOf(mBuffer.get());
+  size += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
   return size;
 }
 
 MediaRawDataWriter*
 MediaRawData::CreateWriter()
 {
   return new MediaRawDataWriter(this);
 }
 
 MediaRawDataWriter::MediaRawDataWriter(MediaRawData* aMediaRawData)
   : mCrypto(aMediaRawData->mCryptoInternal)
   , mTarget(aMediaRawData)
 {
 }
 
 bool
-MediaRawDataWriter::EnsureSize(size_t aSize)
-{
-  if (aSize <= mTarget->mSize) {
-    return true;
-  }
-  if (!mTarget->EnsureCapacity(aSize)) {
-    return false;
-  }
-  return true;
-}
-
-bool
 MediaRawDataWriter::SetSize(size_t aSize)
 {
-  if (aSize > mTarget->mSize && !EnsureSize(aSize)) {
-    return false;
-  }
-
-  mTarget->mSize = aSize;
-  return true;
+  return mTarget->mBuffer.SetLength(aSize);
 }
 
 bool
 MediaRawDataWriter::Prepend(const uint8_t* aData, size_t aSize)
 {
-  if (!EnsureSize(aSize + mTarget->mSize)) {
-    return false;
-  }
-
-  // Shift the data to the right by aSize to leave room for the new data.
-  memmove(mTarget->mData + aSize, mTarget->mData, mTarget->mSize);
-  memcpy(mTarget->mData, aData, aSize);
-
-  mTarget->mSize += aSize;
-  return true;
+  return mTarget->mBuffer.Prepend(aData, aSize);
 }
 
 bool
 MediaRawDataWriter::Replace(const uint8_t* aData, size_t aSize)
 {
-  // If aSize is smaller than our current size, we leave the buffer as is,
-  // only adjusting the reported size.
-  if (!EnsureSize(aSize)) {
-    return false;
-  }
-
-  memcpy(mTarget->mData, aData, aSize);
-  mTarget->mSize = aSize;
-  return true;
+  return mTarget->mBuffer.Replace(aData, aSize);
 }
 
 void
 MediaRawDataWriter::Clear()
 {
-  mTarget->mSize = 0;
-  mTarget->mData = nullptr;
+  mTarget->mBuffer.Clear();
 }
 
 uint8_t*
 MediaRawDataWriter::Data()
 {
-  return mTarget->mData;
+  return mTarget->mBuffer.Data();
 }
 
 size_t
 MediaRawDataWriter::Size()
 {
   return mTarget->Size();
 }
 
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -9,28 +9,262 @@
 #include "nsSize.h"
 #include "mozilla/gfx/Rect.h"
 #include "nsRect.h"
 #include "AudioSampleFormat.h"
 #include "nsIMemoryReporter.h"
 #include "SharedBuffer.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
 #include "nsTArray.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/PodOperations.h"
 
 namespace mozilla {
 
 namespace layers {
 class Image;
 class ImageContainer;
 } // namespace layers
 
 class MediaByteBuffer;
 class SharedTrackInfo;
 
+// AlignedBuffer:
+// Memory allocations are fallibles. Methods return a boolean indicating if
+// memory allocations were successful. Return values should always be checked.
+// AlignedBuffer::mData will be nullptr if no memory has been allocated or if
+// an error occurred during construction.
+// Existing data is only ever modified if new memory allocation has succeeded
+// and preserved if not.
+//
+// The memory referenced by mData will always be Alignment bytes aligned and the
+// underlying buffer will always have a size such that Alignment bytes blocks
+// can be used to read the content, regardless of the mSize value. Buffer is
+// zeroed on creation, elements are not individually constructed.
+// An Alignment value of 0 means that the data isn't aligned.
+//
+// Type must be trivially copyable.
+//
+// AlignedBuffer can typically be used in place of UniquePtr<Type[]> however
+// care must be taken as all memory allocations are fallible.
+// Example:
+// auto buffer = MakeUniqueFallible<float[]>(samples)
+// becomes: AlignedFloatBuffer buffer(samples)
+//
+// auto buffer = MakeUnique<float[]>(samples)
+// becomes:
+// AlignedFloatBuffer buffer(samples);
+// if (!buffer) { return NS_ERROR_OUT_OF_MEMORY; }
+
+template <typename Type, int Alignment = 32>
+class AlignedBuffer
+{
+public:
+  AlignedBuffer()
+    : mData(nullptr)
+    , mLength(0)
+    , mBuffer(nullptr)
+    , mCapacity(0)
+  {}
+
+  explicit AlignedBuffer(size_t aLength)
+    : mData(nullptr)
+    , mLength(0)
+    , mBuffer(nullptr)
+    , mCapacity(0)
+  {
+    if (EnsureCapacity(aLength)) {
+      mLength = aLength;
+    }
+  }
+
+  AlignedBuffer(const Type* aData, size_t aLength)
+    : AlignedBuffer(aLength)
+  {
+    if (!mData) {
+      return;
+    }
+    PodCopy(mData, aData, aLength);
+  }
+
+  AlignedBuffer(const AlignedBuffer& aOther)
+    : AlignedBuffer(aOther.Data(), aOther.Length())
+  {}
+
+  AlignedBuffer(AlignedBuffer&& aOther)
+    : mData(aOther.mData)
+    , mLength(aOther.mLength)
+    , mBuffer(Move(aOther.mBuffer))
+    , mCapacity(aOther.mCapacity)
+  {
+    aOther.mData = nullptr;
+    aOther.mLength = 0;
+    aOther.mCapacity = 0;
+  }
+
+  AlignedBuffer& operator=(AlignedBuffer&& aOther)
+  {
+    this->~AlignedBuffer();
+    new (this) AlignedBuffer(Move(aOther));
+    return *this;
+  }
+
+  Type* Data() const { return mData; }
+  size_t Length() const { return mLength; }
+  size_t Size() const { return mLength * sizeof(Type); }
+  Type& operator[](size_t aIndex)
+  {
+    MOZ_ASSERT(aIndex < mLength);
+    return mData[aIndex];
+  }
+  const Type& operator[](size_t aIndex) const
+  {
+    MOZ_ASSERT(aIndex < mLength);
+    return mData[aIndex];
+  }
+  // Set length of buffer, allocating memory as required.
+  // If length is increased, new buffer area is filled with 0.
+  bool SetLength(size_t aLength)
+  {
+    if (aLength > mLength && !EnsureCapacity(aLength)) {
+      return false;
+    }
+    mLength = aLength;
+    return true;
+  }
+  // Add aData at the beginning of buffer.
+  bool Prepend(const Type* aData, size_t aLength)
+  {
+    if (!EnsureCapacity(aLength + mLength)) {
+      return false;
+    }
+
+    // Shift the data to the right by aLength to leave room for the new data.
+    PodMove(mData + aLength, mData, mLength);
+    PodCopy(mData, aData, aLength);
+
+    mLength += aLength;
+    return true;
+  }
+  // Add aData at the end of buffer.
+  bool Append(const Type* aData, size_t aLength)
+  {
+    if (!EnsureCapacity(aLength + mLength)) {
+      return false;
+    }
+
+    PodCopy(mData + mLength, aData, aLength);
+
+    mLength += aLength;
+    return true;
+  }
+  // Replace current content with aData.
+  bool Replace(const Type* aData, size_t aLength)
+  {
+    // If aLength is smaller than our current length, we leave the buffer as is,
+    // only adjusting the reported length.
+    if (!EnsureCapacity(aLength)) {
+      return false;
+    }
+
+    PodCopy(mData, aData, aLength);
+    mLength = aLength;
+    return true;
+  }
+  // Clear the memory buffer. Will set target mData and mLength to 0.
+  void Clear()
+  {
+    mLength = 0;
+    mData = nullptr;
+  }
+
+  // Methods for reporting memory.
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t size = aMallocSizeOf(this);
+    size += aMallocSizeOf(mBuffer.get());
+    return size;
+  }
+  // AlignedBuffer is typically allocated on the stack. As such, you likely
+  // want to use SizeOfExcludingThis
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(mBuffer.get());
+  }
+  size_t ComputedSizeOfExcludingThis() const
+  {
+    return mCapacity;
+  }
+
+  // For backward compatibility with UniquePtr<Type[]>
+  Type* get() const { return mData; }
+  explicit operator bool() const { return mData != nullptr; }
+
+  // Size in bytes of extra space allocated for padding.
+  static size_t AlignmentPaddingSize()
+  {
+    return AlignmentOffset() * 2;
+  }
+
+private:
+  static size_t AlignmentOffset()
+  {
+    return Alignment ? Alignment - 1 : 0;
+  }
+
+  // Ensure that the backend buffer can hold aLength data. Will update mData.
+  // Will enforce that the start of allocated data is always Alignment bytes
+  // aligned and that it has sufficient end padding to allow for Alignment bytes
+  // block read as required by some data decoders.
+  // Returns false if memory couldn't be allocated.
+  bool EnsureCapacity(size_t aLength)
+  {
+    const CheckedInt<size_t> sizeNeeded =
+      CheckedInt<size_t>(aLength) * sizeof(Type) + AlignmentPaddingSize();
+
+    if (!sizeNeeded.isValid()) {
+      // overflow.
+      return false;
+    }
+    if (mData && mCapacity >= sizeNeeded.value()) {
+      return true;
+    }
+    auto newBuffer = MakeUniqueFallible<uint8_t[]>(sizeNeeded.value());
+    if (!newBuffer) {
+      return false;
+    }
+
+    // Find alignment address.
+    const uintptr_t alignmask = AlignmentOffset();
+    Type* newData = reinterpret_cast<Type*>(
+      (reinterpret_cast<uintptr_t>(newBuffer.get()) + alignmask) & ~alignmask);
+    MOZ_ASSERT(uintptr_t(newData) % (AlignmentOffset()+1) == 0);
+
+    PodZero(newData + mLength, aLength - mLength);
+    PodCopy(newData, mData, mLength);
+
+    mBuffer = Move(newBuffer);
+    mCapacity = sizeNeeded.value();
+    mData = newData;
+
+    return true;
+  }
+  Type* mData;
+  size_t mLength;
+  UniquePtr<uint8_t[]> mBuffer;
+  size_t mCapacity;
+};
+
+typedef AlignedBuffer<uint8_t> AlignedByteBuffer;
+typedef AlignedBuffer<float> AlignedFloatBuffer;
+typedef AlignedBuffer<int16_t> AlignedShortBuffer;
+typedef AlignedBuffer<AudioDataValue> AlignedAudioBuffer;
+
 // Container that holds media samples.
 class MediaData {
 public:
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaData)
 
   enum Type {
     AUDIO_DATA = 0,
@@ -119,17 +353,17 @@ protected:
 // Holds chunk a decoded audio frames.
 class AudioData : public MediaData {
 public:
 
   AudioData(int64_t aOffset,
             int64_t aTime,
             int64_t aDuration,
             uint32_t aFrames,
-            UniquePtr<AudioDataValue[]> aData,
+            AlignedAudioBuffer&& aData,
             uint32_t aChannels,
             uint32_t aRate)
     : MediaData(sType, aOffset, aTime, aDuration, aFrames)
     , mChannels(aChannels)
     , mRate(aRate)
     , mAudioData(Move(aData)) {}
 
   static const Type sType = AUDIO_DATA;
@@ -154,17 +388,17 @@ public:
   bool IsAudible() const;
 
   const uint32_t mChannels;
   const uint32_t mRate;
   // At least one of mAudioBuffer/mAudioData must be non-null.
   // mChannels channels, each with mFrames frames
   RefPtr<SharedBuffer> mAudioBuffer;
   // mFrames frames, each with mChannels values
-  UniquePtr<AudioDataValue[]> mAudioData;
+  AlignedAudioBuffer mAudioData;
 
 protected:
   ~AudioData() {}
 };
 
 namespace layers {
 class TextureClient;
 class PlanarYCbCrImage;
@@ -333,22 +567,21 @@ class CryptoSample : public CryptoTrack
 {
 public:
   nsTArray<uint16_t> mPlainSizes;
   nsTArray<uint32_t> mEncryptedSizes;
   nsTArray<uint8_t> mIV;
   nsTArray<nsCString> mSessionIds;
 };
 
-
 // MediaRawData is a MediaData container used to store demuxed, still compressed
 // samples.
 // Use MediaRawData::CreateWriter() to obtain a MediaRawDataWriter object that
 // provides methods to modify and manipulate the data.
-// Memory allocations are fallibles. Methods return a boolean indicating if
+// Memory allocations are fallible. Methods return a boolean indicating if
 // memory allocations were successful. Return values should always be checked.
 // MediaRawData::mData will be nullptr if no memory has been allocated or if
 // an error occurred during construction.
 // Existing data is only ever modified if new memory allocation has succeeded
 // and preserved if not.
 //
 // The memory referenced by mData will always be 32 bytes aligned and the
 // underlying buffer will always have a size such that 32 bytes blocks can be
@@ -391,22 +624,22 @@ private:
 };
 
 class MediaRawData : public MediaData {
 public:
   MediaRawData();
   MediaRawData(const uint8_t* aData, size_t mSize);
 
   // Pointer to data or null if not-yet allocated
-  const uint8_t* Data() const { return mData; }
+  const uint8_t* Data() const { return mBuffer.Data(); }
   // Size of buffer.
-  size_t Size() const { return mSize; }
+  size_t Size() const { return mBuffer.Length(); }
   size_t ComputedSizeOfIncludingThis() const
   {
-    return sizeof(*this) + mCapacity;
+    return sizeof(*this) + mBuffer.ComputedSizeOfExcludingThis();
   }
 
   const CryptoSample& mCrypto;
   RefPtr<MediaByteBuffer> mExtraData;
 
   RefPtr<SharedTrackInfo> mTrackInfo;
 
   // Return a deep copy or nullptr if out of memory.
@@ -416,26 +649,17 @@ public:
   virtual MediaRawDataWriter* CreateWriter();
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
 
 protected:
   ~MediaRawData();
 
 private:
   friend class MediaRawDataWriter;
-  // Ensure that the backend buffer can hold aSize data. Will update mData.
-  // Will enforce that the start of allocated data is always 32 bytes
-  // aligned and that it has sufficient end padding to allow for 32 bytes block
-  // read as required by some data decoders.
-  // Returns false if memory couldn't be allocated.
-  bool EnsureCapacity(size_t aSize);
-  uint8_t* mData;
-  size_t mSize;
-  UniquePtr<uint8_t[]> mBuffer;
-  uint32_t mCapacity;
+  AlignedByteBuffer mBuffer;
   CryptoSample mCryptoInternal;
   MediaRawData(const MediaRawData&); // Not implemented
 };
 
   // MediaByteBuffer is a ref counted infallible TArray.
 class MediaByteBuffer : public nsTArray<uint8_t> {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaByteBuffer);
   MediaByteBuffer() = default;
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -731,17 +731,17 @@ MediaDecoder::Load(nsIStreamListener** a
 }
 
 nsresult
 MediaDecoder::InitializeStateMachine()
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ASSERTION(mDecoderStateMachine, "Cannot initialize null state machine!");
 
-  nsresult rv = mDecoderStateMachine->Init();
+  nsresult rv = mDecoderStateMachine->Init(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // If some parameters got set before the state machine got created,
   // set them now
   SetStateMachineParameters();
 
   return NS_OK;
 }
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -290,50 +290,29 @@ MediaDecoderStateMachine::MediaDecoderSt
   mPlaybackOffset(mTaskQueue, 0,
                   "MediaDecoderStateMachine::mPlaybackOffset (Canonical)"),
   mIsAudioDataAudible(mTaskQueue, false,
                      "MediaDecoderStateMachine::mIsAudioDataAudible (Canonical)")
 {
   MOZ_COUNT_CTOR(MediaDecoderStateMachine);
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
-  // Dispatch initialization that needs to happen on that task queue.
-  nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<RefPtr<MediaDecoder>>(
-    this, &MediaDecoderStateMachine::InitializationTask, aDecoder);
-  mTaskQueue->Dispatch(r.forget());
-
   InitVideoQueuePrefs();
 
   mBufferingWait = IsRealTime() ? 0 : 15;
   mLowDataThresholdUsecs = IsRealTime() ? 0 : detail::LOW_DATA_THRESHOLD_USECS;
 
 #ifdef XP_WIN
   // Ensure high precision timers are enabled on Windows, otherwise the state
   // machine isn't woken up at reliable intervals to set the next frame,
   // and we drop frames while painting. Note that multiple calls to this
   // function per-process is OK, provided each call is matched by a corresponding
   // timeEndPeriod() call.
   timeBeginPeriod(1);
 #endif
-
-  mAudioQueueListener = AudioQueue().PopEvent().Connect(
-    mTaskQueue, this, &MediaDecoderStateMachine::OnAudioPopped);
-  mVideoQueueListener = VideoQueue().PopEvent().Connect(
-    mTaskQueue, this, &MediaDecoderStateMachine::OnVideoPopped);
-
-  mMetadataManager.Connect(mReader->TimedMetadataEvent(), OwnerThread());
-
-  mMediaSink = CreateMediaSink(mAudioCaptured);
-
-#ifdef MOZ_EME
-  mCDMProxyPromise.Begin(aDecoder->RequestCDMProxy()->Then(
-    OwnerThread(), __func__, this,
-    &MediaDecoderStateMachine::OnCDMProxyReady,
-    &MediaDecoderStateMachine::OnCDMProxyNotReady));
-#endif
 }
 
 MediaDecoderStateMachine::~MediaDecoderStateMachine()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   MOZ_COUNT_DTOR(MediaDecoderStateMachine);
 
 #ifdef XP_WIN
@@ -1067,24 +1046,45 @@ MediaDecoderStateMachine::CheckIfDecodeC
 }
 
 bool MediaDecoderStateMachine::IsPlaying() const
 {
   MOZ_ASSERT(OnTaskQueue());
   return mMediaSink->IsPlaying();
 }
 
-nsresult MediaDecoderStateMachine::Init()
+nsresult MediaDecoderStateMachine::Init(MediaDecoder* aDecoder)
 {
   MOZ_ASSERT(NS_IsMainThread());
+
+  // Dispatch initialization that needs to happen on that task queue.
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<RefPtr<MediaDecoder>>(
+    this, &MediaDecoderStateMachine::InitializationTask, aDecoder);
+  mTaskQueue->Dispatch(r.forget());
+
+  mAudioQueueListener = AudioQueue().PopEvent().Connect(
+    mTaskQueue, this, &MediaDecoderStateMachine::OnAudioPopped);
+  mVideoQueueListener = VideoQueue().PopEvent().Connect(
+    mTaskQueue, this, &MediaDecoderStateMachine::OnVideoPopped);
+
+  mMetadataManager.Connect(mReader->TimedMetadataEvent(), OwnerThread());
+
+  mMediaSink = CreateMediaSink(mAudioCaptured);
+
+#ifdef MOZ_EME
+  mCDMProxyPromise.Begin(aDecoder->RequestCDMProxy()->Then(
+    OwnerThread(), __func__, this,
+    &MediaDecoderStateMachine::OnCDMProxyReady,
+    &MediaDecoderStateMachine::OnCDMProxyNotReady));
+#endif
+
   nsresult rv = mReader->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
-    this, &MediaDecoderStateMachine::ReadMetadata);
+  r = NS_NewRunnableMethod(this, &MediaDecoderStateMachine::ReadMetadata);
   OwnerThread()->Dispatch(r.forget());
 
   return NS_OK;
 }
 
 void MediaDecoderStateMachine::StopPlayback()
 {
   MOZ_ASSERT(OnTaskQueue());
@@ -2506,17 +2506,20 @@ MediaDecoderStateMachine::DropAudioUpToS
   if (framesToPrune.value() > audio->mFrames) {
     // We've messed up somehow. Don't try to trim frames, the |frames|
     // variable below will overflow.
     DECODER_WARN("Can't prune more frames that we have!");
     return NS_ERROR_FAILURE;
   }
   uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune.value());
   uint32_t channels = audio->mChannels;
-  auto audioData = MakeUnique<AudioDataValue[]>(frames * channels);
+  AlignedAudioBuffer audioData(frames * channels);
+  if (!audioData) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
   memcpy(audioData.get(),
          audio->mAudioData.get() + (framesToPrune.value() * channels),
          frames * channels * sizeof(AudioDataValue));
   CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate);
   if (!duration.isValid()) {
     return NS_ERROR_FAILURE;
   }
   RefPtr<AudioData> data(new AudioData(audio->mOffset,
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -137,17 +137,17 @@ public:
   typedef MediaDecoderReader::AudioDataPromise AudioDataPromise;
   typedef MediaDecoderReader::VideoDataPromise VideoDataPromise;
   typedef MediaDecoderOwner::NextFrameStatus NextFrameStatus;
   typedef mozilla::layers::ImageContainer::FrameID FrameID;
   MediaDecoderStateMachine(MediaDecoder* aDecoder,
                            MediaDecoderReader* aReader,
                            bool aRealTime = false);
 
-  nsresult Init();
+  nsresult Init(MediaDecoder* aDecoder);
 
   // Enumeration for the valid decoding states
   enum State {
     DECODER_STATE_DECODING_METADATA,
     DECODER_STATE_WAIT_FOR_CDM,
     DECODER_STATE_DORMANT,
     DECODER_STATE_DECODING,
     DECODER_STATE_SEEKING,
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaInfo.cpp
@@ -0,0 +1,190 @@
+/* -*- 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 "MediaInfo.h"
+
+namespace mozilla {
+
+typedef AudioConfig::ChannelLayout ChannelLayout;
+
+/**
+ * AudioConfig::ChannelLayout
+ */
+
+/*
+ SMPTE channel layout (also known as wave order)
+ DUAL-MONO      L   R
+ DUAL-MONO-LFE  L   R   LFE
+ MONO           M
+ MONO-LFE       M   LFE
+ STEREO         L   R
+ STEREO-LFE     L   R   LFE
+ 3F             L   R   C
+ 3F-LFE         L   R   C    LFE
+ 2F1            L   R   S
+ 2F1-LFE        L   R   LFE  S
+ 3F1            L   R   C    S
+ 3F1-LFE        L   R   C    LFE S
+ 2F2            L   R   LS   RS
+ 2F2-LFE        L   R   LFE  LS   RS
+ 3F2            L   R   C    LS   RS
+ 3F2-LFE        L   R   C    LFE  LS   RS
+ 3F3R-LFE       L   R   C    LFE  BC   LS   RS
+ 3F4-LFE        L   R   C    LFE  Rls  Rrs  LS   RS
+*/
+
+void
+AudioConfig::ChannelLayout::UpdateChannelMap()
+{
+  mChannelMap = 0;
+  mValid = mChannels.Length() <= MAX_AUDIO_CHANNELS;
+  for (size_t i = 0; i < mChannels.Length() && i <= MAX_AUDIO_CHANNELS; i++) {
+    uint32_t mask = 1 << mChannels[i];
+    if (mChannels[i] == CHANNEL_INVALID || (mChannelMap & mask)) {
+      mValid = false;
+    }
+    mChannelMap |= mask;
+  }
+}
+
+/* static */ const AudioConfig::Channel*
+AudioConfig::ChannelLayout::SMPTEDefault(uint32_t aChannels) const
+{
+  switch (aChannels) {
+    case 1: // MONO
+    {
+      static const Channel config[] = { CHANNEL_MONO };
+      return config;
+    }
+    case 2: // STEREO
+    {
+      static const Channel config[] = { CHANNEL_LEFT, CHANNEL_RIGHT };
+      return config;
+    }
+    case 3: // 3F
+    {
+      static const Channel config[] = { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER };
+      return config;
+    }
+    case 4: // 2F2
+    {
+      static const Channel config[] = { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LS, CHANNEL_RS };
+      return config;
+    }
+    case 5: // 3F2
+    {
+      static const Channel config[] = { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LS, CHANNEL_RS };
+      return config;
+    }
+    case 6: // 3F2-LFE
+    {
+      static const Channel config[] = { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE, CHANNEL_LS, CHANNEL_RS };
+      return config;
+    }
+    case 7: // 3F3R-LFE
+    {
+      static const Channel config[] = { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE, CHANNEL_RCENTER, CHANNEL_LS, CHANNEL_RS };
+      return config;
+    }
+    case 8: // 3F4-LFE
+    {
+      static const Channel config[] = { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE, CHANNEL_RLS, CHANNEL_RRS, CHANNEL_LS, CHANNEL_RS };
+      return config;
+    }
+    default:
+      return nullptr;
+  }
+}
+
+bool
+AudioConfig::ChannelLayout::MappingTable(const ChannelLayout& aOther,
+                                         uint8_t* aMap) const
+{
+  if (!IsValid() || !aOther.IsValid() ||
+      Map() != aOther.Map()) {
+    return false;
+  }
+  if (!aMap) {
+    return true;
+  }
+  for (uint32_t i = 0; i < Count(); i++) {
+    for (uint32_t j = 0; j < Count(); j++) {
+      if (aOther[j] == mChannels[i]) {
+        aMap[j] = i;
+        break;
+      }
+    }
+  }
+  return true;
+}
+
+/**
+ * AudioConfig::ChannelConfig
+ */
+
+/* static */ const char*
+AudioConfig::FormatToString(AudioConfig::SampleFormat aFormat)
+{
+  switch (aFormat) {
+    case FORMAT_U8:     return "unsigned 8 bit";
+    case FORMAT_S16:    return "signed 16 bit";
+    case FORMAT_S24:    return "signed 24 bit MSB";
+    case FORMAT_S24LSB: return "signed 24 bit LSB";
+    case FORMAT_S32:    return "signed 32 bit";
+    case FORMAT_FLT:    return "32 bit floating point";
+    case FORMAT_NONE:   return "none";
+    default:            return "unknown";
+  }
+}
+/* static */ uint32_t
+AudioConfig::SampleSize(AudioConfig::SampleFormat aFormat)
+{
+  switch (aFormat) {
+    case FORMAT_U8:     return 1;
+    case FORMAT_S16:    return 2;
+    case FORMAT_S24:    MOZ_FALLTHROUGH;
+    case FORMAT_S24LSB: MOZ_FALLTHROUGH;
+    case FORMAT_S32:    MOZ_FALLTHROUGH;
+    case FORMAT_FLT:    return 4;
+    case FORMAT_NONE:
+    default:            return 0;
+  }
+}
+
+/* static */ uint32_t
+AudioConfig::FormatToBits(AudioConfig::SampleFormat aFormat)
+{
+  switch (aFormat) {
+    case FORMAT_U8:     return 8;
+    case FORMAT_S16:    return 16;
+    case FORMAT_S24LSB: MOZ_FALLTHROUGH;
+    case FORMAT_S24:    return 24;
+    case FORMAT_S32:    MOZ_FALLTHROUGH;
+    case FORMAT_FLT:    return 32;
+    case FORMAT_NONE:   MOZ_FALLTHROUGH;
+    default:            return 0;
+  }
+}
+
+AudioConfig::AudioConfig(const ChannelLayout& aChannelLayout, uint32_t aRate,
+                         AudioConfig::SampleFormat aFormat, bool aInterleaved)
+  : mChannelLayout(aChannelLayout)
+  , mChannels(aChannelLayout.Count())
+  , mRate(aRate)
+  , mFormat(aFormat)
+  , mInterleaved(aInterleaved)
+{}
+
+AudioConfig::AudioConfig(uint32_t aChannels, uint32_t aRate,
+                         AudioConfig::SampleFormat aFormat, bool aInterleaved)
+  : mChannelLayout(aChannels)
+  , mChannels(aChannels)
+  , mRate(aRate)
+  , mFormat(aFormat)
+  , mInterleaved(aInterleaved)
+{}
+
+} // namespace mozilla
--- a/dom/media/MediaInfo.h
+++ b/dom/media/MediaInfo.h
@@ -469,11 +469,158 @@ private:
   UniquePtr<TrackInfo> mInfo;
   // A unique ID, guaranteed to change when changing streams.
   uint32_t mStreamSourceID;
 
 public:
   const nsCString& mMimeType;
 };
 
+// Maximum channel number we can currently handle (7.1)
+#define MAX_AUDIO_CHANNELS 8
+
+class AudioConfig {
+public:
+  enum Channel {
+    CHANNEL_INVALID = -1,
+    CHANNEL_MONO = 0,
+    CHANNEL_LEFT,
+    CHANNEL_RIGHT,
+    CHANNEL_CENTER,
+    CHANNEL_LS,
+    CHANNEL_RS,
+    CHANNEL_RLS,
+    CHANNEL_RCENTER,
+    CHANNEL_RRS,
+    CHANNEL_LFE,
+  };
+
+  class ChannelLayout {
+  public:
+    ChannelLayout()
+      : mChannelMap(0)
+      , mValid(false)
+    {}
+    explicit ChannelLayout(uint32_t aChannels)
+      : ChannelLayout(aChannels, SMPTEDefault(aChannels))
+    {}
+    ChannelLayout(uint32_t aChannels, const Channel* aConfig)
+    {
+      mChannels.AppendElements(aConfig, aChannels);
+      UpdateChannelMap();
+    }
+    bool operator==(const ChannelLayout& aOther) const
+    {
+      return mChannels == aOther.mChannels;
+    }
+    bool operator!=(const ChannelLayout& aOther) const
+    {
+      return mChannels != aOther.mChannels;
+    }
+    const Channel& operator[](uint32_t aIndex) const
+    {
+      return mChannels[aIndex];
+    }
+    uint32_t Count() const
+    {
+      return mChannels.Length();
+    }
+    uint32_t Map() const
+    {
+      return mChannelMap;
+    }
+    // Calculate the mapping table from the current layout to aOther such that
+    // one can easily go from one layout to the other by doing:
+    // out[channel] = in[map[channel]].
+    // Returns true if the reordering is possible or false otherwise.
+    // If true, then aMap, if set, will be updated to contain the mapping table
+    // allowing conversion from the current layout to aOther.
+    // If aMap is nullptr, then MappingTable can be used to simply determine if
+    // the current layout can be easily reordered to aOther.
+    // aMap must be an array of size MAX_AUDIO_CHANNELS.
+    bool MappingTable(const ChannelLayout& aOther, uint8_t* aMap = nullptr) const;
+    bool IsValid() const {
+      return mValid;
+    }
+    bool HasChannel(Channel aChannel) const
+    {
+      return mChannelMap & (1 << aChannel);
+    }
+  private:
+    void UpdateChannelMap();
+    const Channel* SMPTEDefault(uint32_t aChannels) const;
+    AutoTArray<Channel, MAX_AUDIO_CHANNELS> mChannels;
+    uint32_t mChannelMap;
+    bool mValid;
+  };
+
+  enum SampleFormat {
+    FORMAT_NONE = 0,
+    FORMAT_U8,
+    FORMAT_S16,
+    FORMAT_S24LSB,
+    FORMAT_S24,
+    FORMAT_S32,
+    FORMAT_FLT,
+#if defined(MOZ_SAMPLE_TYPE_FLOAT32)
+    FORMAT_DEFAULT = FORMAT_FLT
+#elif defined(MOZ_SAMPLE_TYPE_S16)
+    FORMAT_DEFAULT = FORMAT_S16
+#else
+#error "Not supported audio type"
+#endif
+  };
+
+  AudioConfig(const ChannelLayout& aChannelLayout, uint32_t aRate,
+              AudioConfig::SampleFormat aFormat = FORMAT_DEFAULT,
+              bool aInterleaved = true);
+  // Will create a channel configuration from default SMPTE ordering.
+  AudioConfig(uint32_t aChannels, uint32_t aRate,
+              AudioConfig::SampleFormat aFormat = FORMAT_DEFAULT,
+              bool aInterleaved = true);
+
+  const ChannelLayout& Layout() const
+  {
+    return mChannelLayout;
+  }
+  uint32_t Channels() const
+  {
+    if (!mChannelLayout.IsValid()) {
+      return mChannels;
+    }
+    return mChannelLayout.Count();
+  }
+  uint32_t Rate() const
+  {
+    return mRate;
+  }
+  SampleFormat Format() const
+  {
+    return mFormat;
+  }
+  bool Interleaved() const
+  {
+    return mInterleaved;
+  }
+
+  static const char* FormatToString(SampleFormat aFormat);
+  static uint32_t SampleSize(SampleFormat aFormat);
+  static uint32_t FormatToBits(SampleFormat aFormat);
+
+private:
+  // Channels configuration.
+  ChannelLayout mChannelLayout;
+
+  // Channel count.
+  uint32_t mChannels;
+
+  // Sample rate.
+  uint32_t mRate;
+
+  // Sample format.
+  SampleFormat mFormat;
+
+  bool mInterleaved;
+};
+
 } // namespace mozilla
 
 #endif // MediaInfo_h
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -135,70 +135,16 @@ media::TimeIntervals GetEstimatedBuffere
 
                               media::TimeUnit::FromMicroseconds(endUs));
     }
     startOffset = aStream->GetNextCachedData(endOffset);
   }
   return buffered;
 }
 
-int DownmixAudioToStereo(mozilla::AudioDataValue* buffer,
-                         int channels, uint32_t frames)
-{
-  int outChannels;
-  outChannels = 2;
-#ifdef MOZ_SAMPLE_TYPE_FLOAT32
-  // Downmix matrix. Per-row normalization 1 for rows 3,4 and 2 for rows 5-8.
-  static const float dmatrix[6][8][2]= {
-      /*3*/{{0.5858f,0},{0.4142f,0.4142f},{0,     0.5858f}},
-      /*4*/{{0.4226f,0},{0,      0.4226f},{0.366f,0.2114f},{0.2114f,0.366f}},
-      /*5*/{{0.6510f,0},{0.4600f,0.4600f},{0,     0.6510f},{0.5636f,0.3254f},{0.3254f,0.5636f}},
-      /*6*/{{0.5290f,0},{0.3741f,0.3741f},{0,     0.5290f},{0.4582f,0.2645f},{0.2645f,0.4582f},{0.3741f,0.3741f}},
-      /*7*/{{0.4553f,0},{0.3220f,0.3220f},{0,     0.4553f},{0.3943f,0.2277f},{0.2277f,0.3943f},{0.2788f,0.2788f},{0.3220f,0.3220f}},
-      /*8*/{{0.3886f,0},{0.2748f,0.2748f},{0,     0.3886f},{0.3366f,0.1943f},{0.1943f,0.3366f},{0.3366f,0.1943f},{0.1943f,0.3366f},{0.2748f,0.2748f}},
-  };
-  // Re-write the buffer with downmixed data
-  for (uint32_t i = 0; i < frames; i++) {
-    float sampL = 0.0;
-    float sampR = 0.0;
-    for (int j = 0; j < channels; j++) {
-      sampL+=buffer[i*channels+j]*dmatrix[channels-3][j][0];
-      sampR+=buffer[i*channels+j]*dmatrix[channels-3][j][1];
-    }
-    buffer[i*outChannels]=sampL;
-    buffer[i*outChannels+1]=sampR;
-  }
-#else
-  // Downmix matrix. Per-row normalization 1 for rows 3,4 and 2 for rows 5-8.
-  // Coefficients in Q14.
-  static const int16_t dmatrix[6][8][2]= {
-      /*3*/{{9598, 0},{6786,6786},{0,   9598}},
-      /*4*/{{6925, 0},{0,   6925},{5997,3462},{3462,5997}},
-      /*5*/{{10663,0},{7540,7540},{0,  10663},{9234,5331},{5331,9234}},
-      /*6*/{{8668, 0},{6129,6129},{0,   8668},{7507,4335},{4335,7507},{6129,6129}},
-      /*7*/{{7459, 0},{5275,5275},{0,   7459},{6460,3731},{3731,6460},{4568,4568},{5275,5275}},
-      /*8*/{{6368, 0},{4502,4502},{0,   6368},{5514,3184},{3184,5514},{5514,3184},{3184,5514},{4502,4502}}
-  };
-  // Re-write the buffer with downmixed data
-  for (uint32_t i = 0; i < frames; i++) {
-    int32_t sampL = 0;
-    int32_t sampR = 0;
-    for (int j = 0; j < channels; j++) {
-      sampL+=buffer[i*channels+j]*dmatrix[channels-3][j][0];
-      sampR+=buffer[i*channels+j]*dmatrix[channels-3][j][1];
-    }
-    sampL = (sampL + 8192)>>14;
-    buffer[i*outChannels] = static_cast<mozilla::AudioDataValue>(MOZ_CLIP_TO_15(sampL));
-    sampR = (sampR + 8192)>>14;
-    buffer[i*outChannels+1] = static_cast<mozilla::AudioDataValue>(MOZ_CLIP_TO_15(sampR));
-  }
-#endif
-  return outChannels;
-}
-
 void DownmixStereoToMono(mozilla::AudioDataValue* aBuffer,
                          uint32_t aFrames)
 {
   MOZ_ASSERT(aBuffer);
   const int channels = 2;
   for (uint32_t fIdx = 0; fIdx < aFrames; ++fIdx) {
 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
     float sample = 0.0;
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -158,23 +158,16 @@ nsresult SecondsToUsecs(double aSeconds,
 static const int32_t MAX_VIDEO_WIDTH = 8192;
 static const int32_t MAX_VIDEO_HEIGHT = 4608;
 
 // Scales the display rect aDisplay by aspect ratio aAspectRatio.
 // Note that aDisplay must be validated by IsValidVideoRegion()
 // before being used!
 void ScaleDisplayByAspectRatio(nsIntSize& aDisplay, float aAspectRatio);
 
-// Downmix multichannel Audio samples to Stereo.
-// Input are the buffer contains multichannel data,
-// the number of channels and the number of frames.
-int DownmixAudioToStereo(mozilla::AudioDataValue* buffer,
-                         int channels,
-                         uint32_t frames);
-
 // Downmix Stereo audio samples to Mono.
 // Input are the buffer contains stereo data and the number of frames.
 void DownmixStereoToMono(mozilla::AudioDataValue* aBuffer,
                          uint32_t aFrames);
 
 bool IsVideoContentType(const nsCString& aContentType);
 
 // Returns true if it's safe to use aPicture as the picture to be
--- a/dom/media/eme/EMEUtils.cpp
+++ b/dom/media/eme/EMEUtils.cpp
@@ -78,16 +78,17 @@ ParseKeySystem(const nsAString& aExpecte
   aOutCDMVersion = version;
 
   return true;
 }
 
 static const char16_t* sKeySystems[] = {
   MOZ_UTF16("org.w3.clearkey"),
   MOZ_UTF16("com.adobe.primetime"),
+  MOZ_UTF16("com.widevine.alpha"),
 };
 
 bool
 ParseKeySystem(const nsAString& aInputKeySystem,
                nsAString& aOutKeySystem,
                int32_t& aOutCDMVersion)
 {
   for (const char16_t* keySystem : sKeySystems) {
@@ -135,13 +136,16 @@ nsString
 KeySystemToGMPName(const nsAString& aKeySystem)
 {
   if (aKeySystem.EqualsLiteral("com.adobe.primetime")) {
     return NS_LITERAL_STRING("gmp-eme-adobe");
   }
   if (aKeySystem.EqualsLiteral("org.w3.clearkey")) {
     return NS_LITERAL_STRING("gmp-clearkey");
   }
+  if (aKeySystem.EqualsLiteral("com.widevine.alpha")) {
+    return NS_LITERAL_STRING("gmp-widevinecdm");
+  }
   MOZ_ASSERT(false, "We should only call this for known GMPs");
   return EmptyString();
 }
 
 } // namespace mozilla
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -28,19 +28,16 @@
 #include "mozilla/EMEUtils.h"
 #include "GMPUtils.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsXULAppAPI.h"
 #include "gmp-audio-decode.h"
 #include "gmp-video-decode.h"
-#ifdef XP_WIN
-#include "WMFDecoderModule.h"
-#endif
 
 #if defined(XP_WIN) || defined(XP_MACOSX)
 #define PRIMETIME_EME_SUPPORTED 1
 #endif
 
 namespace mozilla {
 namespace dom {
 
@@ -301,116 +298,104 @@ MediaKeySystemAccess::GetKeySystemStatus
       aOutMessage = NS_LITERAL_CSTRING("Minimum MacOSX version not met for Adobe EME");
       return MediaKeySystemStatus::Cdm_not_supported;
     }
 #endif
     return EnsureMinCDMVersion(mps, aKeySystem, aMinCdmVersion, aOutMessage, aOutCdmVersion);
   }
 #endif
 
+#ifdef MOZ_WIDEVINE_EME
+  if (aKeySystem.EqualsLiteral("com.widevine.alpha")) {
+#ifdef XP_WIN
+    // Win Vista and later only.
+    if (!IsVistaOrLater()) {
+      aOutMessage = NS_LITERAL_CSTRING("Minimum Windows version not met for Widevine EME");
+      return MediaKeySystemStatus::Cdm_not_supported;
+    }
+#endif
+    if (!Preferences::GetBool("media.gmp-widevinecdm.enabled", false)) {
+      aOutMessage = NS_LITERAL_CSTRING("Widevine EME disabled");
+      return MediaKeySystemStatus::Cdm_disabled;
+    }
+    return EnsureMinCDMVersion(mps, aKeySystem, aMinCdmVersion, aOutMessage, aOutCdmVersion);
+  }
+#endif
+
   return MediaKeySystemStatus::Cdm_not_supported;
 }
 
 static bool
 GMPDecryptsAndDecodesAAC(mozIGeckoMediaPluginService* aGMPS,
                          const nsAString& aKeySystem)
 {
   MOZ_ASSERT(HaveGMPFor(aGMPS,
                         NS_ConvertUTF16toUTF8(aKeySystem),
                         NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
   return HaveGMPFor(aGMPS,
                     NS_ConvertUTF16toUTF8(aKeySystem),
                     NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
-                    NS_LITERAL_CSTRING("aac"))
-#ifdef XP_WIN
-    // Clearkey on Windows advertises that it can decode in its GMP info
-    // file, but uses Windows Media Foundation to decode. That's not present
-    // on Windows XP, and on some Vista, Windows N, and KN variants without
-    // certain services packs. So for ClearKey we must check that WMF will
-    // work.
-    && (!aKeySystem.EqualsLiteral("org.w3.clearkey") || WMFDecoderModule::HasAAC())
-#endif
-  ;
+                    NS_LITERAL_CSTRING("aac"));
 }
 
 static bool
 GMPDecryptsAndDecodesH264(mozIGeckoMediaPluginService* aGMPS,
                           const nsAString& aKeySystem)
 {
   MOZ_ASSERT(HaveGMPFor(aGMPS,
                         NS_ConvertUTF16toUTF8(aKeySystem),
                         NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
   return HaveGMPFor(aGMPS,
                     NS_ConvertUTF16toUTF8(aKeySystem),
                     NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
-                    NS_LITERAL_CSTRING("h264"))
-#ifdef XP_WIN
-    // Clearkey on Windows advertises that it can decode in its GMP info
-    // file, but uses Windows Media Foundation to decode. That's not present
-    // on Windows XP, and on some Vista, Windows N, and KN variants without
-    // certain services packs. So for ClearKey we must check that WMF will
-    // work.
-    && (!aKeySystem.EqualsLiteral("org.w3.clearkey") || WMFDecoderModule::HasH264())
-#endif
-  ;
+                    NS_LITERAL_CSTRING("h264"));
 }
 
 // If this keysystem's CDM explicitly says it doesn't support decoding,
 // that means it's OK with passing the decrypted samples back to Gecko
-// for decoding. Note we special case Clearkey on Windows, where we need
-// to check for whether WMF is usable because the CDM uses that
-// to decode.
+// for decoding.
 static bool
 GMPDecryptsAndGeckoDecodesH264(mozIGeckoMediaPluginService* aGMPService,
                                const nsAString& aKeySystem,
                                const nsAString& aContentType)
 {
   MOZ_ASSERT(HaveGMPFor(aGMPService,
                         NS_ConvertUTF16toUTF8(aKeySystem),
                         NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
   MOZ_ASSERT(IsH264ContentType(aContentType));
-  return
-    (!HaveGMPFor(aGMPService,
-                 NS_ConvertUTF16toUTF8(aKeySystem),
-                 NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
-                 NS_LITERAL_CSTRING("h264"))
-#ifdef XP_WIN
-    // Clearkey on Windows advertises that it can decode in its GMP info
-    // file, but uses Windows Media Foundation to decode. That's not present
-    // on Windows XP, and on some Vista, Windows N, and KN variants without
-    // certain services packs. So don't try to use gmp-clearkey for decoding
-    // if we don't have a decoder here.
-    || (aKeySystem.EqualsLiteral("org.w3.clearkey") && !WMFDecoderModule::HasH264())
-#endif
-    ) && MP4Decoder::CanHandleMediaType(aContentType);
+  return !HaveGMPFor(aGMPService,
+                     NS_ConvertUTF16toUTF8(aKeySystem),
+                     NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
+                     NS_LITERAL_CSTRING("h264")) &&
+         MP4Decoder::CanHandleMediaType(aContentType);
 }
 
 static bool
 GMPDecryptsAndGeckoDecodesAAC(mozIGeckoMediaPluginService* aGMPService,
                               const nsAString& aKeySystem,
                               const nsAString& aContentType)
 {
   MOZ_ASSERT(HaveGMPFor(aGMPService,
                         NS_ConvertUTF16toUTF8(aKeySystem),
                         NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
   MOZ_ASSERT(IsAACContentType(aContentType));
-  return
-    (!HaveGMPFor(aGMPService,
-                 NS_ConvertUTF16toUTF8(aKeySystem),
-                 NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
-                 NS_LITERAL_CSTRING("aac"))
-#ifdef XP_WIN
-    // Clearkey on Windows advertises that it can decode in its GMP info
-    // file, but uses Windows Media Foundation to decode. That's not present
-    // on Windows XP, and on some Vista, Windows N, and KN variants without
-    // certain services packs. So don't try to use gmp-clearkey for decoding
-    // if we don't have a decoder here.
-    || (aKeySystem.EqualsLiteral("org.w3.clearkey") && !WMFDecoderModule::HasAAC())
+
+  return !HaveGMPFor(aGMPService,
+                     NS_ConvertUTF16toUTF8(aKeySystem),
+                     NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
+                     NS_LITERAL_CSTRING("aac")) &&
+#if defined(MOZ_WIDEVINE_EME) && defined(XP_WIN)
+         // Widevine CDM doesn't include an AAC decoder. So if WMF can't
+         // decode AAC, and a codec wasn't specified, be conservative
+         // and reject the MediaKeys request, since our policy is to prevent
+         //  the Adobe GMP's unencrypted AAC decoding path being used to
+         // decode content decrypted by the Widevine CDM.
+        (!aKeySystem.EqualsLiteral("com.widevine.alpha") || WMFDecoderModule::HasAAC()) &&
 #endif
-    ) && MP4Decoder::CanHandleMediaType(aContentType);
+    MP4Decoder::CanHandleMediaType(aContentType);
 }
 
 static bool
 IsSupportedAudio(mozIGeckoMediaPluginService* aGMPService,
                  const nsAString& aKeySystem,
                  const nsAString& aAudioType)
 {
   return IsAACContentType(aAudioType) &&
@@ -453,33 +438,41 @@ IsSupported(mozIGeckoMediaPluginService*
       !IsSupportedVideo(aGMPService, aKeySystem, aConfig.mVideoType)) {
     return false;
   }
 
   return true;
 }
 
 static bool
+IsSupportedInitDataType(const nsString& aCandidate, const nsAString& aKeySystem)
+{
+  // All supported keySystems can handle "cenc" initDataType.
+  // ClearKey also supports "keyids" and "webm" initDataTypes.
+  return aCandidate.EqualsLiteral("cenc") ||
+    ((aKeySystem.EqualsLiteral("org.w3.clearkey")
+#ifdef MOZ_WIDEVINE_EME
+    || aKeySystem.EqualsLiteral("com.widevine.alpha")
+#endif
+    ) &&
+    (aCandidate.EqualsLiteral("keyids") || aCandidate.EqualsLiteral("webm)")));
+}
+
+static bool
 GetSupportedConfig(mozIGeckoMediaPluginService* aGMPService,
                    const nsAString& aKeySystem,
                    const MediaKeySystemConfiguration& aCandidate,
                    MediaKeySystemConfiguration& aOutConfig)
 {
   MediaKeySystemConfiguration config;
   config.mLabel = aCandidate.mLabel;
   if (aCandidate.mInitDataTypes.WasPassed()) {
     nsTArray<nsString> initDataTypes;
     for (const nsString& candidate : aCandidate.mInitDataTypes.Value()) {
-      // All supported keySystems can handle "cenc" initDataType.
-      // ClearKey also supports "keyids" and "webm" initDataTypes.
-      if (candidate.EqualsLiteral("cenc")) {
-        initDataTypes.AppendElement(candidate);
-      } else if ((candidate.EqualsLiteral("keyids") ||
-                  candidate.EqualsLiteral("webm)")) &&
-                 aKeySystem.EqualsLiteral("org.w3.clearkey")) {
+      if (IsSupportedInitDataType(candidate, aKeySystem)) {
         initDataTypes.AppendElement(candidate);
       }
     }
     if (initDataTypes.IsEmpty()) {
       return false;
     }
     config.mInitDataTypes.Construct();
     config.mInitDataTypes.Value().Assign(initDataTypes);
@@ -506,16 +499,27 @@ GetSupportedConfig(mozIGeckoMediaPluginS
     }
     if (caps.IsEmpty()) {
       return false;
     }
     config.mVideoCapabilities.Construct();
     config.mVideoCapabilities.Value().Assign(caps);
   }
 
+#if defined(MOZ_WIDEVINE_EME) && defined(XP_WIN)
+  // Widevine CDM doesn't include an AAC decoder. So if WMF can't decode AAC,
+  // and a codec wasn't specified, be conservative and reject the MediaKeys request.
+  if (aKeySystem.EqualsLiteral("com.widevine.alpha") &&
+      (!aCandidate.mAudioCapabilities.WasPassed() ||
+       !aCandidate.mVideoCapabilities.WasPassed()) &&
+     !WMFDecoderModule::HasAAC()) {
+    return false;
+  }
+#endif
+
   aOutConfig = config;
 
   return true;
 }
 
 // Backwards compatibility with legacy requestMediaKeySystemAccess with fields
 // from old MediaKeySystemOptions dictionary.
 /* static */
--- a/dom/media/eme/moz.build
+++ b/dom/media/eme/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+for cdm in CONFIG['MOZ_EME_MODULES']:
+    DEFINES['MOZ_%s_EME' % cdm.upper()] = True
+
 EXPORTS.mozilla.dom += [
     'MediaEncryptedEvent.h',
     'MediaKeyError.h',
     'MediaKeyMessageEvent.h',
     'MediaKeys.h',
     'MediaKeySession.h',
     'MediaKeyStatusMap.h',
     'MediaKeySystemAccess.h',
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -16,16 +16,19 @@
 #include "nsIFile.h"
 #include "nsXULAppAPI.h"
 #include "gmp-video-decode.h"
 #include "gmp-video-encode.h"
 #include "GMPPlatform.h"
 #include "mozilla/dom/CrashReporterChild.h"
 #include "GMPUtils.h"
 #include "prio.h"
+#ifdef MOZ_WIDEVINE_EME
+#include "widevine-adapter/WidevineAdapter.h"
+#endif
 
 using mozilla::dom::CrashReporterChild;
 
 static const int MAX_VOUCHER_LENGTH = 500000;
 
 #ifdef XP_WIN
 #include <stdlib.h> // for _exit()
 #else
@@ -340,17 +343,17 @@ GMPChild::GetUTF8LibPath(nsACString& aOu
   libFile->GetPath(path);
   aOutLibPath = NS_ConvertUTF16toUTF8(path);
 
   return true;
 #endif
 }
 
 bool
-GMPChild::AnswerStartPlugin()
+GMPChild::AnswerStartPlugin(const nsString& aAdapter)
 {
   LOGD("%s", __FUNCTION__);
 
   if (!PreLoadPluginVoucher()) {
     NS_WARNING("Plugin voucher failed to load!");
     return false;
   }
   PreLoadSandboxVoucher();
@@ -373,21 +376,28 @@ GMPChild::AnswerStartPlugin()
 #if defined(MOZ_GMP_SANDBOX) && defined(XP_MACOSX)
   if (!SetMacSandboxInfo()) {
     NS_WARNING("Failed to set Mac GMP sandbox info");
     delete platformAPI;
     return false;
   }
 #endif
 
+  GMPAdapter* adapter = nullptr;
+#ifdef MOZ_WIDEVINE_EME
+  if (aAdapter.EqualsLiteral("widevine")) {
+    adapter = new WidevineAdapter();
+  }
+#endif
   if (!mGMPLoader->Load(libPath.get(),
                         libPath.Length(),
                         mNodeId.BeginWriting(),
                         mNodeId.Length(),
-                        platformAPI)) {
+                        platformAPI,
+                        adapter)) {
     NS_WARNING("Failed to load GMP");
     delete platformAPI;
     return false;
   }
 
   void* sh = nullptr;
   GMPAsyncShutdownHost* host = static_cast<GMPAsyncShutdownHost*>(this);
   GMPErr err = GetAPI(GMP_API_ASYNC_SHUTDOWN, host, &sh);
--- a/dom/media/gmp/GMPChild.h
+++ b/dom/media/gmp/GMPChild.h
@@ -48,17 +48,17 @@ private:
   friend class GMPContentChild;
 
   bool PreLoadPluginVoucher();
   void PreLoadSandboxVoucher();
 
   bool GetUTF8LibPath(nsACString& aOutLibPath);
 
   bool RecvSetNodeId(const nsCString& aNodeId) override;
-  bool AnswerStartPlugin() override;
+  bool AnswerStartPlugin(const nsString& aAdapter) override;
   bool RecvPreloadLibs(const nsCString& aLibs) override;
 
   PCrashReporterChild* AllocPCrashReporterChild(const NativeThreadId& aThread) override;
   bool DeallocPCrashReporterChild(PCrashReporterChild*) override;
 
   PGMPTimerChild* AllocPGMPTimerChild() override;
   bool DeallocPGMPTimerChild(PGMPTimerChild* aActor) override;
 
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -164,25 +164,30 @@ GMPDecryptorParent::Decrypt(uint32_t aId
   LOGV(("GMPDecryptorParent[%p]::Decrypt(id=%d)", this, aId));
 
   if (!mIsOpen) {
     NS_WARNING("Trying to use a dead GMP decrypter!");
     return;
   }
 
   // Caller should ensure parameters passed in are valid.
-  MOZ_ASSERT(!aBuffer.IsEmpty() && aCrypto.mValid);
+  MOZ_ASSERT(!aBuffer.IsEmpty());
 
-  GMPDecryptionData data(aCrypto.mKeyId,
-                         aCrypto.mIV,
-                         aCrypto.mPlainSizes,
-                         aCrypto.mEncryptedSizes,
-                         aCrypto.mSessionIds);
+  if (aCrypto.mValid) {
+    GMPDecryptionData data(aCrypto.mKeyId,
+                           aCrypto.mIV,
+                           aCrypto.mPlainSizes,
+                           aCrypto.mEncryptedSizes,
+                           aCrypto.mSessionIds);
 
-  Unused << SendDecrypt(aId, aBuffer, data);
+    Unused << SendDecrypt(aId, aBuffer, data);
+  } else {
+    GMPDecryptionData data;
+    Unused << SendDecrypt(aId, aBuffer, data);
+  }
 }
 
 bool
 GMPDecryptorParent::RecvSetSessionId(const uint32_t& aCreateSessionId,
                                      const nsCString& aSessionId)
 {
   LOGD(("GMPDecryptorParent[%p]::RecvSetSessionId(token=%u, sessionId='%s')",
         this, aCreateSessionId, aSessionId.get()));
--- a/dom/media/gmp/GMPLoader.cpp
+++ b/dom/media/gmp/GMPLoader.cpp
@@ -48,45 +48,109 @@
 
 namespace mozilla {
 namespace gmp {
 
 class GMPLoaderImpl : public GMPLoader {
 public:
   explicit GMPLoaderImpl(SandboxStarter* aStarter)
     : mSandboxStarter(aStarter)
+    , mAdapter(nullptr)
   {}
   virtual ~GMPLoaderImpl() {}
 
   bool Load(const char* aUTF8LibPath,
             uint32_t aUTF8LibPathLen,
             char* aOriginSalt,
             uint32_t aOriginSaltLen,
-            const GMPPlatformAPI* aPlatformAPI) override;
+            const GMPPlatformAPI* aPlatformAPI,
+            GMPAdapter* aAdapter) override;
 
   GMPErr GetAPI(const char* aAPIName,
                 void* aHostAPI,
                 void** aPluginAPI) override;
 
   void Shutdown() override;
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   void SetSandboxInfo(MacSandboxInfo* aSandboxInfo) override;
 #endif
 
 private:
-  PRLibrary* mLib;
-  GMPGetAPIFunc mGetAPIFunc;
   SandboxStarter* mSandboxStarter;
+  UniquePtr<GMPAdapter> mAdapter;
 };
 
 GMPLoader* CreateGMPLoader(SandboxStarter* aStarter) {
   return static_cast<GMPLoader*>(new GMPLoaderImpl(aStarter));
 }
 
+class PassThroughGMPAdapter : public GMPAdapter {
+public:
+  ~PassThroughGMPAdapter() {
+    // Ensure we're always shutdown, even if caller forgets to call GMPShutdown().
+    GMPShutdown();
+  }
+
+  void SetAdaptee(PRLibrary* aLib) override
+  {
+    mLib = aLib;
+  }
+
+  GMPErr GMPInit(const GMPPlatformAPI* aPlatformAPI) override
+  {
+    if (!mLib) {
+      return GMPGenericErr;
+    }
+    GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(mLib, "GMPInit"));
+    if (!initFunc) {
+      return GMPNotImplementedErr;
+    }
+    return initFunc(aPlatformAPI);
+  }
+
+  GMPErr GMPGetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) override
+  {
+    if (!mLib) {
+      return GMPGenericErr;
+    }
+    GMPGetAPIFunc getapiFunc = reinterpret_cast<GMPGetAPIFunc>(PR_FindFunctionSymbol(mLib, "GMPGetAPI"));
+    if (!getapiFunc) {
+      return GMPNotImplementedErr;
+    }
+    return getapiFunc(aAPIName, aHostAPI, aPluginAPI);
+  }
+
+  void GMPShutdown() override
+  {
+    if (mLib) {
+      GMPShutdownFunc shutdownFunc = reinterpret_cast<GMPShutdownFunc>(PR_FindFunctionSymbol(mLib, "GMPShutdown"));
+      if (shutdownFunc) {
+        shutdownFunc();
+      }
+      PR_UnloadLibrary(mLib);
+      mLib = nullptr;
+    }
+  }
+
+  void GMPSetNodeId(const char* aNodeId, uint32_t aLength) override
+  {
+    if (!mLib) {
+      return;
+    }
+    GMPSetNodeIdFunc setNodeIdFunc = reinterpret_cast<GMPSetNodeIdFunc>(PR_FindFunctionSymbol(mLib, "GMPSetNodeId"));
+    if (setNodeIdFunc) {
+      setNodeIdFunc(aNodeId, aLength);
+    }
+  }
+
+private:
+  PRLibrary* mLib = nullptr;
+};
+
 #if defined(XP_WIN) && defined(HASH_NODE_ID_WITH_DEVICE_ID)
 MOZ_NEVER_INLINE
 static bool
 GetStackAfterCurrentFrame(uint8_t** aOutTop, uint8_t** aOutBottom)
 {
   // "Top" of the free space on the stack is directly after the memory
   // holding our return address.
   uint8_t* top = (uint8_t*)_AddressOfReturnAddress();
@@ -170,17 +234,18 @@ static void SecureMemset(void* start, ui
 }
 #endif
 
 bool
 GMPLoaderImpl::Load(const char* aUTF8LibPath,
                     uint32_t aUTF8LibPathLen,
                     char* aOriginSalt,
                     uint32_t aOriginSaltLen,
-                    const GMPPlatformAPI* aPlatformAPI)
+                    const GMPPlatformAPI* aPlatformAPI,
+                    GMPAdapter* aAdapter)
 {
   std::string nodeId;
 #ifdef HASH_NODE_ID_WITH_DEVICE_ID
   if (aOriginSaltLen > 0) {
     std::vector<uint8_t> deviceId;
     int volumeId;
     if (!rlz_lib::GetRawMachineId(&deviceId, &volumeId)) {
       return false;
@@ -254,62 +319,54 @@ GMPLoaderImpl::Load(const char* aUTF8Lib
   }
 
   libSpec.value.pathname_u = widePath.get();
   libSpec.type = PR_LibSpec_PathnameU;
 #else
   libSpec.value.pathname = aUTF8LibPath;
   libSpec.type = PR_LibSpec_Pathname;
 #endif
-  mLib = PR_LoadLibraryWithFlags(libSpec, 0);
-  if (!mLib) {
-    return false;
-  }
-
-  GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(mLib, "GMPInit"));
-  if (!initFunc) {
+  PRLibrary* lib = PR_LoadLibraryWithFlags(libSpec, 0);
+  if (!lib) {
     return false;
   }
 
-  if (initFunc(aPlatformAPI) != GMPNoErr) {
+  GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(lib, "GMPInit"));
+  if ((initFunc && aAdapter) ||
+      (!initFunc && !aAdapter)) {
+    // Ensure that if we're dealing with a GMP we do *not* use an adapter
+    // provided from the outside world. This is important as it means we
+    // don't call code not covered by Adobe's plugin-container voucher
+    // before we pass the node Id to Adobe's GMP.
     return false;
   }
 
-  GMPSetNodeIdFunc setNodeIdFunc = reinterpret_cast<GMPSetNodeIdFunc>(PR_FindFunctionSymbol(mLib, "GMPSetNodeId"));
-  if (setNodeIdFunc) {
-    setNodeIdFunc(nodeId.c_str(), nodeId.size());
-  }
+  // Note: PassThroughGMPAdapter's code must remain in this file so that it's
+  // covered by Adobe's plugin-container voucher.
+  mAdapter.reset((!aAdapter) ? new PassThroughGMPAdapter() : aAdapter);
+  mAdapter->SetAdaptee(lib);
 
-  mGetAPIFunc = reinterpret_cast<GMPGetAPIFunc>(PR_FindFunctionSymbol(mLib, "GMPGetAPI"));
-  if (!mGetAPIFunc) {
-    return false;
-  }
+  mAdapter->GMPInit(aPlatformAPI);
 
   return true;
 }
 
 GMPErr
 GMPLoaderImpl::GetAPI(const char* aAPIName,
                       void* aHostAPI,
                       void** aPluginAPI)
 {
-  return mGetAPIFunc ? mGetAPIFunc(aAPIName, aHostAPI, aPluginAPI)
-                     : GMPGenericErr;
+  return mAdapter->GMPGetAPI(aAPIName, aHostAPI, aPluginAPI);
 }
 
 void
 GMPLoaderImpl::Shutdown()
 {
-  if (mLib) {
-    GMPShutdownFunc shutdownFunc = reinterpret_cast<GMPShutdownFunc>(PR_FindFunctionSymbol(mLib, "GMPShutdown"));
-    if (shutdownFunc) {
-      shutdownFunc();
-    }
-    PR_UnloadLibrary(mLib);
-    mLib = nullptr;
+  if (mAdapter) {
+    mAdapter->GMPShutdown();
   }
 }
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
 void
 GMPLoaderImpl::SetSandboxInfo(MacSandboxInfo* aSandboxInfo)
 {
   if (mSandboxStarter) {
--- a/dom/media/gmp/GMPLoader.h
+++ b/dom/media/gmp/GMPLoader.h
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GMP_LOADER_H__
 #define GMP_LOADER_H__
 
 #include <stdint.h>
+#include "prlink.h"
 #include "gmp-entrypoints.h"
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
 #include "mozilla/Sandbox.h"
 #endif
 
 namespace mozilla {
 namespace gmp {
@@ -24,44 +25,66 @@ public:
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   // On OS X we need to set Mac-specific sandbox info just before we start the
   // sandbox, which we don't yet know when the GMPLoader and SandboxStarter
   // objects are created.
   virtual void SetSandboxInfo(MacSandboxInfo* aSandboxInfo) = 0;
 #endif
 };
 
+// Interface that adapts a plugin to the GMP API.
+class GMPAdapter {
+public:
+  virtual ~GMPAdapter() {}
+  // Sets the adapted to plugin library module.
+  // Note: the GMPAdapter is responsible for calling PR_UnloadLibrary on aLib
+  // when it's finished with it.
+  virtual void SetAdaptee(PRLibrary* aLib) = 0;
+
+  // These are called in place of the corresponding GMP API functions.
+  virtual GMPErr GMPInit(const GMPPlatformAPI* aPlatformAPI) = 0;
+  virtual GMPErr GMPGetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) = 0;
+  virtual void GMPShutdown() = 0;
+  virtual void GMPSetNodeId(const char* aNodeId, uint32_t aLength) = 0;
+};
+
 // Encapsulates generating the device-bound node id, activating the sandbox,
 // loading the GMP, and passing the node id to the GMP (in that order).
 //
 // In Desktop Gecko, the implementation of this lives in plugin-container,
 // and is passed into XUL code from on startup. The GMP IPC child protocol actor
 // uses this interface to load and retrieve interfaces from the GMPs.
 //
 // In Desktop Gecko the implementation lives in the plugin-container so that
 // it can be covered by DRM vendor's voucher.
 //
 // On Android the GMPLoader implementation lives in libxul (because for the time
 // being GMPLoader relies upon NSPR, which we can't use in plugin-container
 // on Android).
 //
 // There is exactly one GMPLoader per GMP child process, and only one GMP
 // per child process (so the GMPLoader only loads one GMP).
+//
+// Load() takes an optional GMPAdapter which can be used to adapt non-GMPs
+// to adhere to the GMP API.
 class GMPLoader {
 public:
   virtual ~GMPLoader() {}
 
   // Calculates the device-bound node id, then activates the sandbox,
   // then loads the GMP library and (if applicable) passes the bound node id
-  // to the GMP.
+  // to the GMP. If aAdapter is non-null, the lib path is assumed to be
+  // a non-GMP, and the adapter is initialized with the lib and the adapter
+  // is used to interact with the plugin.
   virtual bool Load(const char* aUTF8LibPath,
                     uint32_t aLibPathLen,
                     char* aOriginSalt,
                     uint32_t aOriginSaltLen,
-                    const GMPPlatformAPI* aPlatformAPI) = 0;
+                    const GMPPlatformAPI* aPlatformAPI,
+                    GMPAdapter* aAdapter = nullptr) = 0;
 
   // Retrieves an interface pointer from the GMP.
   virtual GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) = 0;
 
   // Calls the GMPShutdown function exported by the GMP lib, and unloads the
   // plugin library.
   virtual void Shutdown() = 0;
 
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -30,16 +30,25 @@ using mozilla::ipc::GeckoChildProcessHos
 #ifdef MOZ_CRASHREPORTER
 #include "nsPrintfCString.h"
 using CrashReporter::AnnotationTable;
 using CrashReporter::GetIDFromMinidump;
 #endif
 
 #include "mozilla/Telemetry.h"
 
+#ifdef XP_WIN
+#include "WMFDecoderModule.h"
+#endif
+
+#ifdef MOZ_WIDEVINE_EME
+#include "mozilla/dom/WidevineCDMManifestBinding.h"
+#include "widevine-adapter/WidevineAdapter.h"
+#endif
+
 namespace mozilla {
 
 #undef LOG
 #undef LOGD
 
 extern LogModule* GetGMPLog();
 #define LOG(level, x, ...) MOZ_LOG(GetGMPLog(), (level), (x, ##__VA_ARGS__))
 #define LOGD(x, ...) LOG(mozilla::LogLevel::Debug, "GMPParent[%p|childPid=%d] " x, this, mChildPid, ##__VA_ARGS__)
@@ -75,40 +84,54 @@ GMPParent::~GMPParent()
   MOZ_ASSERT(!mProcess);
 }
 
 nsresult
 GMPParent::CloneFrom(const GMPParent* aOther)
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
   MOZ_ASSERT(aOther->mDirectory && aOther->mService, "null plugin directory");
-  return Init(aOther->mService, aOther->mDirectory);
+
+  mService = aOther->mService;
+  mDirectory = aOther->mDirectory;
+  mName = aOther->mName;
+  mVersion = aOther->mVersion;
+  mDescription = aOther->mDescription;
+  mDisplayName = aOther->mDisplayName;
+#ifdef XP_WIN
+  mLibs = aOther->mLibs;
+#endif
+  for (const GMPCapability& cap : aOther->mCapabilities) {
+    mCapabilities.AppendElement(cap);
+  }
+  mAdapter = aOther->mAdapter;
+  return NS_OK;
 }
 
-nsresult
+RefPtr<GenericPromise>
 GMPParent::Init(GeckoMediaPluginServiceParent* aService, nsIFile* aPluginDir)
 {
   MOZ_ASSERT(aPluginDir);
   MOZ_ASSERT(aService);
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
   mService = aService;
   mDirectory = aPluginDir;
 
   // aPluginDir is <profile-dir>/<gmp-plugin-id>/<version>
   // where <gmp-plugin-id> should be gmp-gmpopenh264
   nsCOMPtr<nsIFile> parent;
   nsresult rv = aPluginDir->GetParent(getter_AddRefs(parent));
   if (NS_FAILED(rv)) {
-    return rv;
+    return GenericPromise::CreateAndReject(rv, __func__);
   }
   nsAutoString parentLeafName;
   rv = parent->GetLeafName(parentLeafName);
   if (NS_FAILED(rv)) {
-    return rv;
+    return GenericPromise::CreateAndReject(rv, __func__);
   }
   LOGD("%s: for %s", __FUNCTION__, NS_LossyConvertUTF16toASCII(parentLeafName).get());
 
   MOZ_ASSERT(parentLeafName.Length() > 4);
   mName = Substring(parentLeafName, 4);
 
   return ReadGMPMetaData();
 }
@@ -170,17 +193,17 @@ GMPParent::LoadProcess()
         LOGD("%s: Failed to send preload-libs to child process", __FUNCTION__);
         return NS_ERROR_FAILURE;
       }
       LOGD("%s: Sent preload-libs ('%s') to child process", __FUNCTION__, mLibs.get());
     }
 #endif
 
     // Intr call to block initialization on plugin load.
-    ok = CallStartPlugin();
+    ok = CallStartPlugin(mAdapter);
     if (!ok) {
       LOGD("%s: Failed to send start to child process", __FUNCTION__);
       return NS_ERROR_FAILURE;
     }
     LOGD("%s: Sent StartPlugin to child process", __FUNCTION__);
   }
 
   mState = GMPStateLoaded;
@@ -541,20 +564,20 @@ GMPParent::GMPThread()
 
   return mGMPThread;
 }
 
 bool
 GMPParent::SupportsAPI(const nsCString& aAPI, const nsCString& aTag)
 {
   for (uint32_t i = 0; i < mCapabilities.Length(); i++) {
-    if (!mCapabilities[i]->mAPIName.Equals(aAPI)) {
+    if (!mCapabilities[i].mAPIName.Equals(aAPI)) {
       continue;
     }
-    nsTArray<nsCString>& tags = mCapabilities[i]->mAPITags;
+    nsTArray<nsCString>& tags = mCapabilities[i].mAPITags;
     for (uint32_t j = 0; j < tags.Length(); j++) {
       if (tags[j].Equals(aTag)) {
         return true;
       }
     }
   }
   return false;
 }
@@ -747,40 +770,61 @@ ReadInfoField(GMPInfoFileParser& aParser
 {
   if (!aParser.Contains(aKey) || aParser.Get(aKey).IsEmpty()) {
     return false;
   }
   aOutValue = aParser.Get(aKey);
   return true;
 }
 
-nsresult
+RefPtr<GenericPromise>
 GMPParent::ReadGMPMetaData()
 {
   MOZ_ASSERT(mDirectory, "Plugin directory cannot be NULL!");
   MOZ_ASSERT(!mName.IsEmpty(), "Plugin mName cannot be empty!");
 
   nsCOMPtr<nsIFile> infoFile;
   nsresult rv = mDirectory->Clone(getter_AddRefs(infoFile));
   if (NS_FAILED(rv)) {
-    return rv;
+    return GenericPromise::CreateAndReject(rv, __func__);
   }
   infoFile->AppendRelativePath(mName + NS_LITERAL_STRING(".info"));
 
+  if (FileExists(infoFile)) {
+    return ReadGMPInfoFile(infoFile);
+  }
+
+#ifdef MOZ_WIDEVINE_EME
+  // Maybe this is the Widevine adapted plugin?
+  nsCOMPtr<nsIFile> manifestFile;
+  rv = mDirectory->Clone(getter_AddRefs(manifestFile));
+  if (NS_FAILED(rv)) {
+    return GenericPromise::CreateAndReject(rv, __func__);
+  }
+  manifestFile->AppendRelativePath(NS_LITERAL_STRING("manifest.json"));
+  return ReadChromiumManifestFile(manifestFile);
+#else
+  return GenericPromise::CreateAndReject(rv, __func__);
+#endif
+}
+
+RefPtr<GenericPromise>
+GMPParent::ReadGMPInfoFile(nsIFile* aFile)
+{
   GMPInfoFileParser parser;
-  if (!parser.Init(infoFile)) {
-    return NS_ERROR_FAILURE;
+  if (!parser.Init(aFile)) {
+    return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   nsAutoCString apis;
   if (!ReadInfoField(parser, NS_LITERAL_CSTRING("name"), mDisplayName) ||
       !ReadInfoField(parser, NS_LITERAL_CSTRING("description"), mDescription) ||
       !ReadInfoField(parser, NS_LITERAL_CSTRING("version"), mVersion) ||
       !ReadInfoField(parser, NS_LITERAL_CSTRING("apis"), apis)) {
-    return NS_ERROR_FAILURE;
+    return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
 #ifdef XP_WIN
   // "Libraries" field is optional.
   ReadInfoField(parser, NS_LITERAL_CSTRING("libraries"), mLibs);
 #endif
 
   nsTArray<nsCString> apiTokens;
@@ -788,84 +832,151 @@ GMPParent::ReadGMPMetaData()
   for (nsCString api : apiTokens) {
     int32_t tagsStart = api.FindChar('[');
     if (tagsStart == 0) {
       // Not allowed to be the first character.
       // API name must be at least one character.
       continue;
     }
 
-    auto cap = new GMPCapability();
+    GMPCapability cap;
     if (tagsStart == -1) {
       // No tags.
-      cap->mAPIName.Assign(api);
+      cap.mAPIName.Assign(api);
     } else {
       auto tagsEnd = api.FindChar(']');
       if (tagsEnd == -1 || tagsEnd < tagsStart) {
         // Invalid syntax, skip whole capability.
-        delete cap;
         continue;
       }
 
-      cap->mAPIName.Assign(Substring(api, 0, tagsStart));
+      cap.mAPIName.Assign(Substring(api, 0, tagsStart));
 
       if ((tagsEnd - tagsStart) > 1) {
         const nsDependentCSubstring ts(Substring(api, tagsStart + 1, tagsEnd - tagsStart - 1));
         nsTArray<nsCString> tagTokens;
         SplitAt(":", ts, tagTokens);
         for (nsCString tag : tagTokens) {
-          cap->mAPITags.AppendElement(tag);
+          cap.mAPITags.AppendElement(tag);
         }
       }
     }
 
     // We support the current GMPDecryptor version, and the previous.
     // We Adapt the previous to the current in the GMPContentChild.
-    if (cap->mAPIName.EqualsLiteral(GMP_API_DECRYPTOR_BACKWARDS_COMPAT)) {
-      cap->mAPIName.AssignLiteral(GMP_API_DECRYPTOR);
+    if (cap.mAPIName.EqualsLiteral(GMP_API_DECRYPTOR_BACKWARDS_COMPAT)) {
+      cap.mAPIName.AssignLiteral(GMP_API_DECRYPTOR);
     }
 
-    if (cap->mAPIName.EqualsLiteral(GMP_API_DECRYPTOR)) {
+    if (cap.mAPIName.EqualsLiteral(GMP_API_DECRYPTOR)) {
       mCanDecrypt = true;
 
 #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
       if (!mozilla::SandboxInfo::Get().CanSandboxMedia()) {
         printf_stderr("GMPParent::ReadGMPMetaData: Plugin \"%s\" is an EME CDM"
                       " but this system can't sandbox it; not loading.\n",
                       mDisplayName.get());
-        delete cap;
-        return NS_ERROR_FAILURE;
+        return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
       }
 #endif
 #ifdef XP_WIN
       // Adobe GMP doesn't work without SSE2. Check the tags to see if
       // the decryptor is for the Adobe GMP, and refuse to load it if
       // SSE2 isn't supported.
-      for (const nsCString& tag : cap->mAPITags) {
-        if (!tag.EqualsLiteral("com.adobe.primetime")) {
-          continue;
-        }
-        if (!mozilla::supports_sse2()) {
-          return NS_ERROR_FAILURE;
-        }
-        break;
+      if (cap.mAPITags.Contains(NS_LITERAL_CSTRING("com.adobe.primetime")) &&
+          !mozilla::supports_sse2()) {
+        return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
       }
 #endif // XP_WIN
     }
 
-    mCapabilities.AppendElement(cap);
+#ifdef XP_WIN
+    // Clearkey on Windows advertises that it can decode in its GMP info
+    // file, but uses Windows Media Foundation to decode. That's not present
+    // on Windows XP, and on some Vista, Windows N, and KN variants without
+    // certain services packs. So don't add the decoding capability to
+    // gmp-clearkey's GMPParent if it's not going to be able to use WMF to
+    // decode.
+    if (cap.mAPIName.EqualsLiteral(GMP_API_VIDEO_DECODER) &&
+        cap.mAPITags.Contains(NS_LITERAL_CSTRING("org.w3.clearkey")) &&
+        !WMFDecoderModule::HasH264()) {
+      continue;
+    }
+    if (cap.mAPIName.EqualsLiteral(GMP_API_AUDIO_DECODER) &&
+        cap.mAPITags.Contains(NS_LITERAL_CSTRING("org.w3.clearkey")) &&
+        !WMFDecoderModule::HasAAC()) {
+      continue;
+    }
+#endif
+
+    mCapabilities.AppendElement(Move(cap));
   }
 
   if (mCapabilities.IsEmpty()) {
-    return NS_ERROR_FAILURE;
+    return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+  }
+
+  return GenericPromise::CreateAndResolve(true, __func__);
+}
+
+#ifdef MOZ_WIDEVINE_EME
+RefPtr<GenericPromise>
+GMPParent::ReadChromiumManifestFile(nsIFile* aFile)
+{
+  nsAutoCString json;
+  if (!ReadIntoString(aFile, json, 5 * 1024)) {
+    return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
-  return NS_OK;
+  // DOM JSON parsing needs to run on the main thread.
+  return InvokeAsync(AbstractThread::MainThread(), this, __func__,
+    &GMPParent::ParseChromiumManifest, NS_ConvertUTF8toUTF16(json));
 }
 
+RefPtr<GenericPromise>
+GMPParent::ParseChromiumManifest(nsString aJSON)
+{
+  LOGD("%s: for '%s'", __FUNCTION__, NS_LossyConvertUTF16toASCII(aJSON).get());
+
+  MOZ_ASSERT(NS_IsMainThread());
+  mozilla::dom::WidevineCDMManifest m;
+  if (!m.Init(aJSON)) {
+    return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+  }
+
+  nsresult ignored; // Note: ToInteger returns 0 on failure.
+  if (!WidevineAdapter::Supports(m.mX_cdm_module_versions.ToInteger(&ignored),
+                                 m.mX_cdm_interface_versions.ToInteger(&ignored),
+                                 m.mX_cdm_host_versions.ToInteger(&ignored))) {
+    return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+  }
+
+  mDisplayName = NS_ConvertUTF16toUTF8(m.mName);
+  mDescription = NS_ConvertUTF16toUTF8(m.mDescription);
+  mVersion = NS_ConvertUTF16toUTF8(m.mVersion);
+
+  GMPCapability video(NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER));
+  video.mAPITags.AppendElement(NS_LITERAL_CSTRING("h264"));
+  video.mAPITags.AppendElement(NS_LITERAL_CSTRING("com.widevine.alpha"));
+  mCapabilities.AppendElement(Move(video));
+
+  GMPCapability decrypt(NS_LITERAL_CSTRING(GMP_API_DECRYPTOR));
+  decrypt.mAPITags.AppendElement(NS_LITERAL_CSTRING("com.widevine.alpha"));
+  mCapabilities.AppendElement(Move(decrypt));
+
+  MOZ_ASSERT(mName.EqualsLiteral("widevinecdm"));
+  mAdapter = NS_LITERAL_STRING("widevine");
+#ifdef XP_WIN
+  mLibs = NS_LITERAL_CSTRING("dxva2.dll");
+#endif
+
+  return GenericPromise::CreateAndResolve(true, __func__);
+}
+#endif
+
 bool
 GMPParent::CanBeSharedCrossNodeIds() const
 {
   return !mAsyncShutdownInProgress &&
          mNodeId.IsEmpty() &&
          // XXX bug 1159300 hack -- maybe remove after openh264 1.4
          // We don't want to use CDM decoders for non-encrypted playback
          // just yet; especially not for WebRTC. Don't allow CDMs to be used
--- a/dom/media/gmp/GMPParent.h
+++ b/dom/media/gmp/GMPParent.h
@@ -16,16 +16,17 @@
 #include "GMPStorageParent.h"
 #include "mozilla/gmp/PGMPParent.h"
 #include "nsCOMPtr.h"
 #include "nscore.h"
 #include "nsISupports.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsIFile.h"
+#include "mozilla/MozPromise.h"
 
 class nsIThread;
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 
 namespace mozilla {
 namespace dom {
@@ -36,16 +37,26 @@ class CrashReporterParent;
 #endif
 
 namespace mozilla {
 namespace gmp {
 
 class GMPCapability
 {
 public:
+  explicit GMPCapability() {}
+  GMPCapability(GMPCapability&& aOther)
+    : mAPIName(Move(aOther.mAPIName))
+    , mAPITags(Move(aOther.mAPITags))
+  {
+  }
+  explicit GMPCapability(const nsCString& aAPIName)
+    : mAPIName(aAPIName)
+  {}
+  explicit GMPCapability(const GMPCapability& aOther) = default;
   nsCString mAPIName;
   nsTArray<nsCString> mAPITags;
 };
 
 enum GMPState {
   GMPStateNotLoaded,
   GMPStateLoaded,
   GMPStateUnloading,
@@ -70,17 +81,17 @@ public:
 
 class GMPParent final : public PGMPParent
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPParent)
 
   GMPParent();
 
-  nsresult Init(GeckoMediaPluginServiceParent* aService, nsIFile* aPluginDir);
+  RefPtr<GenericPromise> Init(GeckoMediaPluginServiceParent* aService, nsIFile* aPluginDir);
   nsresult CloneFrom(const GMPParent* aOther);
 
   void Crash();
 
   nsresult LoadProcess();
 
   // Called internally to close this if we don't need it
   void CloseIfUnused();
@@ -146,19 +157,25 @@ public:
   already_AddRefed<GMPContentParent> ForgetGMPContentParent();
 
   bool EnsureProcessLoaded(base::ProcessId* aID);
 
   bool Bridge(GMPServiceParent* aGMPServiceParent);
 
 private:
   ~GMPParent();
+
   RefPtr<GeckoMediaPluginServiceParent> mService;
   bool EnsureProcessLoaded();
-  nsresult ReadGMPMetaData();
+  RefPtr<GenericPromise> ReadGMPMetaData();
+  RefPtr<GenericPromise> ReadGMPInfoFile(nsIFile* aFile);
+#ifdef MOZ_WIDEVINE_EME
+  RefPtr<GenericPromise> ParseChromiumManifest(nsString aJSON); // Main thread.
+  RefPtr<GenericPromise> ReadChromiumManifestFile(nsIFile* aFile); // GMP thread.
+#endif
 #ifdef MOZ_CRASHREPORTER
   void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes);
   void GetCrashID(nsString& aResult);
 #endif
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   PCrashReporterParent* AllocPCrashReporterParent(const NativeThreadId& aThread) override;
   bool DeallocPCrashReporterParent(PCrashReporterParent* aCrashReporter) override;
@@ -191,18 +208,19 @@ private:
   nsCOMPtr<nsIFile> mDirectory; // plugin directory on disk
   nsString mName; // base name of plugin on disk, UTF-16 because used for paths
   nsCString mDisplayName; // name of plugin displayed to users
   nsCString mDescription; // description of plugin for display to users
   nsCString mVersion;
 #ifdef XP_WIN
   nsCString mLibs;
 #endif
+  nsString mAdapter;
   uint32_t mPluginId;
-  nsTArray<nsAutoPtr<GMPCapability>> mCapabilities;
+  nsTArray<GMPCapability> mCapabilities;
   GMPProcessParent* mProcess;
   bool mDeleteProcessOnlyOnUnload;
   bool mAbnormalShutdownInProgress;
   bool mIsBlockingDeletion;
 
   bool mCanDecrypt;
 
   nsTArray<RefPtr<GMPTimerParent>> mTimers;
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -352,26 +352,34 @@ GeckoMediaPluginService::GetThread(nsITh
       return NS_ERROR_FAILURE;
     }
 
     nsresult rv = NS_NewNamedThread("GMPThread", getter_AddRefs(mGMPThread));
     if (NS_FAILED(rv)) {
       return rv;
     }
 
+    mAbstractGMPThread = AbstractThread::CreateXPCOMThreadWrapper(mGMPThread, false);
+
     // Tell the thread to initialize plugins
     InitializePlugins();
   }
 
   nsCOMPtr<nsIThread> copy = mGMPThread;
   copy.forget(aThread);
 
   return NS_OK;
 }
 
+RefPtr<AbstractThread>
+GeckoMediaPluginService::GetAbstractGMPThread()
+{
+  return mAbstractGMPThread;
+}
+
 class GetGMPContentParentForAudioDecoderDone : public GetGMPContentParentCallback
 {
 public:
   explicit GetGMPContentParentForAudioDecoderDone(UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
    : mCallback(Move(aCallback))
   {
   }
 
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -14,16 +14,17 @@
 #include "mozilla/Monitor.h"
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsIThread.h"
 #include "nsThreadUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDocument.h"
 #include "nsIWeakReference.h"
+#include "mozilla/AbstractThread.h"
 
 template <class> struct already_AddRefed;
 
 namespace mozilla {
 
 extern LogModule* GetGMPLog();
 
 namespace gmp {
@@ -69,16 +70,18 @@ public:
                                const nsACString& aPluginName);
 
   // Sets the window to which 'PluginCrashed' chromeonly event is dispatched.
   // Note: if the plugin has crashed before the target window has been set,
   // the 'PluginCrashed' event is dispatched as soon as a target window is set.
   void AddPluginCrashedEventTarget(const uint32_t aPluginId,
                                    nsPIDOMWindowInner* aParentWindow);
 
+  RefPtr<AbstractThread> GetAbstractGMPThread();
+
 protected:
   GeckoMediaPluginService();
   virtual ~GeckoMediaPluginService();
 
   void RemoveObsoletePluginCrashCallbacks(); // Called from add/run.
 
   virtual void InitializePlugins() = 0;
   virtual bool GetContentParentFrom(const nsACString& aNodeId,
@@ -87,16 +90,17 @@ protected:
                                     UniquePtr<GetGMPContentParentCallback>&& aCallback) = 0;
 
   nsresult GMPDispatch(nsIRunnable* event, uint32_t flags = NS_DISPATCH_NORMAL);
   void ShutdownGMPThread();
 
   Mutex mMutex; // Protects mGMPThread and mGMPThreadShutdown and some members
                 // in derived classes.
   nsCOMPtr<nsIThread> mGMPThread;
+  RefPtr<AbstractThread> mAbstractGMPThread;
   bool mGMPThreadShutdown;
   bool mShuttingDownOnGMPThread;
 
   class GMPCrashCallback
   {
   public:
     NS_INLINE_DECL_REFCOUNTING(GMPCrashCallback)
 
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -86,26 +86,29 @@ static bool sHaveSetGMPServiceParentPref
 
 GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent()
   : mShuttingDown(false)
 #ifdef MOZ_CRASHREPORTER
   , mAsyncShutdownPluginStatesMutex("GeckoMediaPluginService::mAsyncShutdownPluginStatesMutex")
 #endif
   , mScannedPluginOnDisk(false)
   , mWaitingForPluginsSyncShutdown(false)
+  , mInitPromiseMonitor("GeckoMediaPluginServiceParent::mInitPromiseMonitor")
+  , mLoadPluginsFromDiskComplete(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!sHaveSetGMPServiceParentPrefCaches) {
     sHaveSetGMPServiceParentPrefCaches = true;
     Preferences::AddIntVarCache(&sMaxAsyncShutdownWaitMs,
                                 "media.gmp.async-shutdown-timeout",
                                 GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT);
     Preferences::AddBoolVarCache(&sAllowInsecureGMP,
                                  "media.gmp.insecure.allow", false);
   }
+  mInitPromise.SetMonitor(&mInitPromiseMonitor);
 }
 
 GeckoMediaPluginServiceParent::~GeckoMediaPluginServiceParent()
 {
   MOZ_ASSERT(mPlugins.IsEmpty());
   MOZ_ASSERT(mAsyncShutdownPlugins.IsEmpty());
 }
 
@@ -503,40 +506,82 @@ GeckoMediaPluginServiceParent::Observe(n
     return GMPDispatch(NS_NewRunnableMethodWithArg<PRTime>(
         this, &GeckoMediaPluginServiceParent::ClearRecentHistoryOnGMPThread,
         t));
   }
 
   return NS_OK;
 }
 
+RefPtr<GenericPromise>
+GeckoMediaPluginServiceParent::EnsureInitialized() {
+  MonitorAutoLock lock(mInitPromiseMonitor);
+  if (mLoadPluginsFromDiskComplete) {
+    return GenericPromise::CreateAndResolve(true, __func__);
+  }
+  // We should have an init promise in flight.
+  MOZ_ASSERT(!mInitPromise.IsEmpty());
+  return mInitPromise.Ensure(__func__);
+}
+
 bool
 GeckoMediaPluginServiceParent::GetContentParentFrom(const nsACString& aNodeId,
                                                     const nsCString& aAPI,
                                                     const nsTArray<nsCString>& aTags,
                                                     UniquePtr<GetGMPContentParentCallback>&& aCallback)
 {
-  RefPtr<GMPParent> gmp = SelectPluginForAPI(aNodeId, aAPI, aTags);
-
-  nsCString api = aTags[0];
-  LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get()));
-
-  if (!gmp) {
-    return false;
-  }
-
-  return gmp->GetGMPContentParent(Move(aCallback));
+  RefPtr<GeckoMediaPluginServiceParent> self(this);
+  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
+  nsCString nodeId(aNodeId);
+  nsTArray<nsCString> tags(aTags);
+  nsCString api(aAPI);
+  GetGMPContentParentCallback* rawCallback = aCallback.release();
+  EnsureInitialized()->Then(thread, __func__,
+    [self, tags, api, nodeId, rawCallback]() -> void {
+      UniquePtr<GetGMPContentParentCallback> callback(rawCallback);
+      RefPtr<GMPParent> gmp = self->SelectPluginForAPI(nodeId, api, tags);
+      LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)self, (void *)gmp, api.get()));
+      if (!gmp) {
+        NS_WARNING("GeckoMediaPluginServiceParent::GetContentParentFrom failed");
+        callback->Done(nullptr);
+        return;
+      }
+      gmp->GetGMPContentParent(Move(callback));
+    },
+    [rawCallback]() -> void {
+      UniquePtr<GetGMPContentParentCallback> callback(rawCallback);
+      NS_WARNING("GMPService::EnsureInitialized failed.");
+      callback->Done(nullptr);
+    });
+  return true;
 }
 
 void
 GeckoMediaPluginServiceParent::InitializePlugins()
 {
-  mGMPThread->Dispatch(
-    NS_NewRunnableMethod(this, &GeckoMediaPluginServiceParent::LoadFromEnvironment),
-    NS_DISPATCH_NORMAL);
+  MonitorAutoLock lock(mInitPromiseMonitor);
+  if (mLoadPluginsFromDiskComplete) {
+    return;
+  }
+
+  RefPtr<GeckoMediaPluginServiceParent> self(this);
+  RefPtr<GenericPromise> p = mInitPromise.Ensure(__func__);
+  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
+  InvokeAsync(thread, this, __func__, &GeckoMediaPluginServiceParent::LoadFromEnvironment)
+    ->Then(thread, __func__,
+      [self]() -> void {
+        MonitorAutoLock lock(self->mInitPromiseMonitor);
+        self->mLoadPluginsFromDiskComplete = true;
+        self->mInitPromise.Resolve(true, __func__);
+      },
+      [self]() -> void {
+        MonitorAutoLock lock(self->mInitPromiseMonitor);
+        self->mLoadPluginsFromDiskComplete = true;
+        self->mInitPromise.Reject(NS_ERROR_FAILURE, __func__);
+      });
 }
 
 void
 GeckoMediaPluginServiceParent::AsyncShutdownNeeded(GMPParent* aParent)
 {
   LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
@@ -697,46 +742,49 @@ GeckoMediaPluginServiceParent::CrashPlug
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
   MutexAutoLock lock(mMutex);
   for (size_t i = 0; i < mPlugins.Length(); i++) {
     mPlugins[i]->Crash();
   }
 }
 
-void
+RefPtr<GenericPromise::AllPromiseType>
 GeckoMediaPluginServiceParent::LoadFromEnvironment()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
   const char* env = PR_GetEnv("MOZ_GMP_PATH");
   if (!env || !*env) {
-    return;
+    return GenericPromise::AllPromiseType::CreateAndResolve(true, __func__);
   }
 
   nsString allpaths;
   if (NS_WARN_IF(NS_FAILED(NS_CopyNativeToUnicode(nsDependentCString(env), allpaths)))) {
-    return;
+    return GenericPromise::AllPromiseType::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
+  nsTArray<RefPtr<GenericPromise>> promises;
   uint32_t pos = 0;
   while (pos < allpaths.Length()) {
     // Loop over multiple path entries separated by colons (*nix) or
     // semicolons (Windows)
     int32_t next = allpaths.FindChar(XPCOM_ENV_PATH_SEPARATOR[0], pos);
     if (next == -1) {
-      AddOnGMPThread(nsDependentSubstring(allpaths, pos));
+      promises.AppendElement(AddOnGMPThread(nsString(Substring(allpaths, pos))));
       break;
     } else {
-      AddOnGMPThread(nsDependentSubstring(allpaths, pos, next - pos));
+      promises.AppendElement(AddOnGMPThread(nsString(Substring(allpaths, pos, next - pos))));
       pos = next + 1;
     }
   }
 
   mScannedPluginOnDisk = true;
+  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
+  return GenericPromise::All(thread, promises);
 }
 
 class NotifyObserversTask final : public nsRunnable {
 public:
   explicit NotifyObserversTask(const char* aTopic, nsString aData = EmptyString())
     : mTopic(aTopic)
     , mData(aData)
   {}
@@ -753,42 +801,67 @@ private:
   ~NotifyObserversTask() {}
   const char* mTopic;
   const nsString mData;
 };
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceParent::PathRunnable::Run()
 {
-  if (mOperation == ADD) {
-    mService->AddOnGMPThread(mPath);
-  } else {
-    mService->RemoveOnGMPThread(mPath,
-                                mOperation == REMOVE_AND_DELETE_FROM_DISK,
-                                mDefer);
-  }
+  mService->RemoveOnGMPThread(mPath,
+                              mOperation == REMOVE_AND_DELETE_FROM_DISK,
+                              mDefer);
 #ifndef MOZ_WIDGET_GONK // Bug 1214967: disabled on B2G due to inscrutable test failures.
   // For e10s, we must fire a notification so that all ContentParents notify
   // their children to update the codecs that the GMPDecoderModule can use.
   NS_DispatchToMainThread(new NotifyObserversTask("gmp-changed"), NS_DISPATCH_NORMAL);
   // For non-e10s, and for decoding in the chrome process, must update GMP
   // PDM's codecs list directly.
   NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void {
     GMPDecoderModule::UpdateUsableCodecs();
   }));
 #endif
   return NS_OK;
 }
 
+RefPtr<GenericPromise>
+GeckoMediaPluginServiceParent::AsyncAddPluginDirectory(const nsAString& aDirectory)
+{
+  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
+  nsString dir(aDirectory);
+  return InvokeAsync(thread, this, __func__, &GeckoMediaPluginServiceParent::AddOnGMPThread, dir)
+    ->Then(AbstractThread::MainThread(), __func__,
+      [dir]() -> void {
+        LOGD(("GeckoMediaPluginServiceParent::AsyncAddPluginDirectory %s succeeded",
+              NS_ConvertUTF16toUTF8(dir).get()));
+        MOZ_ASSERT(NS_IsMainThread());
+        // For e10s, we must fire a notification so that all ContentParents notify
+        // their children to update the codecs that the GMPDecoderModule can use.
+        nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
+        MOZ_ASSERT(obsService);
+        if (obsService) {
+          obsService->NotifyObservers(nullptr, "gmp-changed", nullptr);
+        }
+        // For non-e10s, and for decoding in the chrome process, must update GMP
+        // PDM's codecs list directly.
+        GMPDecoderModule::UpdateUsableCodecs();
+      },
+      [dir]() -> void {
+        LOGD(("GeckoMediaPluginServiceParent::AsyncAddPluginDirectory %s failed",
+              NS_ConvertUTF16toUTF8(dir).get()));
+      })
+    ->CompletionPromise();
+}
+
 NS_IMETHODIMP
 GeckoMediaPluginServiceParent::AddPluginDirectory(const nsAString& aDirectory)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  return GMPDispatch(new PathRunnable(this, aDirectory,
-                                      PathRunnable::EOperation::ADD));
+  RefPtr<GenericPromise> p = AsyncAddPluginDirectory(aDirectory);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceParent::RemovePluginDirectory(const nsAString& aDirectory)
 {
   MOZ_ASSERT(NS_IsMainThread());
   return GMPDispatch(new PathRunnable(this, aDirectory,
                                       PathRunnable::EOperation::REMOVE));
@@ -978,41 +1051,50 @@ GeckoMediaPluginServiceParent::ClonePlug
   }
 
   MutexAutoLock lock(mMutex);
   mPlugins.AppendElement(gmp);
 
   return gmp.get();
 }
 
-void
-GeckoMediaPluginServiceParent::AddOnGMPThread(const nsAString& aDirectory)
+RefPtr<GenericPromise>
+GeckoMediaPluginServiceParent::AddOnGMPThread(nsString aDirectory)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, NS_LossyConvertUTF16toASCII(aDirectory).get()));
 
   nsCOMPtr<nsIFile> directory;
   nsresult rv = NS_NewLocalFile(aDirectory, false, getter_AddRefs(directory));
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
+    return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   RefPtr<GMPParent> gmp = CreateGMPParent();
-  rv = gmp ? gmp->Init(this, directory) : NS_ERROR_NOT_AVAILABLE;
-  if (NS_FAILED(rv)) {
+  if (!gmp) {
     NS_WARNING("Can't Create GMPParent");
-    return;
+    return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
-  {
-    MutexAutoLock lock(mMutex);
-    mPlugins.AppendElement(gmp);
-  }
-
-  NS_DispatchToMainThread(new NotifyObserversTask("gmp-path-added"), NS_DISPATCH_NORMAL);
+  RefPtr<GeckoMediaPluginServiceParent> self(this);
+  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
+  nsCString dir = NS_ConvertUTF16toUTF8(aDirectory);
+  return gmp->Init(this, directory)->Then(thread, __func__,
+    [gmp, self, dir]() -> void {
+      LOGD(("%s::%s: %s Succeeded", __CLASS__, __FUNCTION__, dir.get()));
+      {
+        MutexAutoLock lock(self->mMutex);
+        self->mPlugins.AppendElement(gmp);
+      }
+      NS_DispatchToMainThread(new NotifyObserversTask("gmp-path-added"), NS_DISPATCH_NORMAL);
+    },
+    [dir]() -> void {
+      LOGD(("%s::%s: %s Failed", __CLASS__, __FUNCTION__, dir.get()));
+    })
+    ->CompletionPromise();
 }
 
 void
 GeckoMediaPluginServiceParent::RemoveOnGMPThread(const nsAString& aDirectory,
                                                  const bool aDeleteFromDisk,
                                                  const bool aCanDefer)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
@@ -1240,23 +1322,28 @@ GeckoMediaPluginServiceParent::GetNodeId
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: (%s, %s), %s", __CLASS__, __FUNCTION__,
        NS_ConvertUTF16toUTF8(aOrigin).get(),
        NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
        (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing")));
 
   nsresult rv;
 
-  if (aOrigin.EqualsLiteral("null") ||
+  if (aGMPName.EqualsLiteral("gmp-widevinecdm") ||
+      aOrigin.EqualsLiteral("null") ||
       aOrigin.IsEmpty() ||
       aTopLevelOrigin.EqualsLiteral("null") ||
       aTopLevelOrigin.IsEmpty()) {
-    // At least one of the (origin, topLevelOrigin) is null or empty;
-    // probably a local file. Generate a random node id, and don't store
-    // it so that the GMP's storage is temporary and not shared.
+    // This is for the Google Widevine CDM, which doesn't have persistent
+    // storage and which can't handle being used by more than one origin at
+    // once in the same plugin instance, or at least one of the
+    // (origin, topLevelOrigin) is null or empty; probably a local file.
+    // Generate a random node id, and don't store it so that the GMP's storage
+    // is temporary and the process for this GMP is not shared with GMP
+    // instances that have the same nodeId.
     nsAutoCString salt;
     rv = GenerateRandomPathName(salt, NodeIdSaltLength);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     aOutId = salt;
     mPersistentStorageAllowed.Put(salt, false);
     return NS_OK;
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -8,16 +8,17 @@
 
 #include "GMPService.h"
 #include "mozilla/gmp/PGMPServiceParent.h"
 #include "mozIGeckoMediaPluginChromeService.h"
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "mozilla/Atomics.h"
 #include "nsThreadUtils.h"
+#include "mozilla/MozPromise.h"
 
 template <class> struct already_AddRefed;
 
 namespace mozilla {
 namespace gmp {
 
 class GMPParent;
 
@@ -50,16 +51,18 @@ public:
 
   void AsyncShutdownNeeded(GMPParent* aParent);
   void AsyncShutdownComplete(GMPParent* aParent);
 
   int32_t AsyncShutdownTimeoutMs();
 #ifdef MOZ_CRASHREPORTER
   void SetAsyncShutdownPluginState(GMPParent* aGMPParent, char aId, const nsCString& aState);
 #endif // MOZ_CRASHREPORTER
+  RefPtr<GenericPromise> EnsureInitialized();
+  RefPtr<GenericPromise> AsyncAddPluginDirectory(const nsAString& aDirectory);
 
 private:
   friend class GMPServiceParent;
 
   virtual ~GeckoMediaPluginServiceParent();
 
   void ClearStorage();
 
@@ -75,20 +78,18 @@ private:
                      const nsAString& aGMPName,
                      bool aInPrivateBrowsing, nsACString& aOutId);
 
   void UnloadPlugins();
   void CrashPlugins();
   void NotifySyncShutdownComplete();
   void NotifyAsyncShutdownComplete();
 
-  void LoadFromEnvironment();
   void ProcessPossiblePlugin(nsIFile* aDir);
 
-  void AddOnGMPThread(const nsAString& aDirectory);
   void RemoveOnGMPThread(const nsAString& aDirectory,
                          const bool aDeleteFromDisk,
                          const bool aCanDefer);
 
   nsresult SetAsyncShutdownTimeout();
 
   struct DirectoryFilter {
     virtual bool operator()(nsIFile* aPath) = 0;
@@ -100,31 +101,32 @@ private:
   void ForgetThisSiteOnGMPThread(const nsACString& aOrigin);
   void ClearRecentHistoryOnGMPThread(PRTime aSince);
 
 protected:
   friend class GMPParent;
   void ReAddOnGMPThread(const RefPtr<GMPParent>& aOld);
   void PluginTerminated(const RefPtr<GMPParent>& aOld);
   void InitializePlugins() override;
+  RefPtr<GenericPromise::AllPromiseType> LoadFromEnvironment();
+  RefPtr<GenericPromise> AddOnGMPThread(nsString aDirectory);
   bool GetContentParentFrom(const nsACString& aNodeId,
                             const nsCString& aAPI,
                             const nsTArray<nsCString>& aTags,
                             UniquePtr<GetGMPContentParentCallback>&& aCallback)
     override;
 private:
   GMPParent* ClonePlugin(const GMPParent* aOriginal);
   nsresult EnsurePluginsOnDiskScanned();
   nsresult InitStorage();
 
   class PathRunnable : public nsRunnable
   {
   public:
     enum EOperation {
-      ADD,
       REMOVE,
       REMOVE_AND_DELETE_FROM_DISK,
     };
 
     PathRunnable(GeckoMediaPluginServiceParent* aService, const nsAString& aPath,
                  EOperation aOperation, bool aDefer = false)
       : mService(aService)
       , mPath(aPath)
@@ -188,16 +190,22 @@ private:
 
   // Hashes of (origin,topLevelOrigin) to the node id for
   // non-persistent sessions.
   nsClassHashtable<nsUint32HashKey, nsCString> mTempNodeIds;
 
   // Hashes node id to whether that node id is allowed to store data
   // persistently on disk.
   nsDataHashtable<nsCStringHashKey, bool> mPersistentStorageAllowed;
+
+  // Synchronization for barrier that ensures we've loaded GMPs from
+  // MOZ_GMP_PATH before allowing GetContentParentFrom() to proceed.
+  Monitor mInitPromiseMonitor;
+  MozPromiseHolder<GenericPromise> mInitPromise;
+  bool mLoadPluginsFromDiskComplete;
 };
 
 nsresult ReadSalt(nsIFile* aPath, nsACString& aOutData);
 bool MatchOrigin(nsIFile* aPath, const nsACString& aSite);
 
 class GMPServiceParent final : public PGMPServiceParent
 {
 public:
--- a/dom/media/gmp/GMPUtils.cpp
+++ b/dom/media/gmp/GMPUtils.cpp
@@ -133,17 +133,17 @@ ReadIntoArray(nsIFile* aFile,
     return false;
   }
   aOutDst.SetLength(length);
   int32_t bytesRead = PR_Read(fd, aOutDst.Elements(), length);
   PR_Close(fd);
   return (bytesRead == length);
 }
 
-static bool
+bool
 ReadIntoString(nsIFile* aFile,
                nsCString& aOutDst,
                size_t aMaxLength)
 {
   nsTArray<uint8_t> buf;
   bool rv = ReadIntoArray(aFile, buf, aMaxLength);
   if (rv) {
     buf.AppendElement(0); // Append null terminator, required by nsC*String.
--- a/dom/media/gmp/GMPUtils.h
+++ b/dom/media/gmp/GMPUtils.h
@@ -70,11 +70,16 @@ private:
   nsClassHashtable<nsCStringHashKey, nsCString> mValues;
 };
 
 bool
 ReadIntoArray(nsIFile* aFile,
               nsTArray<uint8_t>& aOutDst,
               size_t aMaxLength);
 
+bool
+ReadIntoString(nsIFile* aFile,
+               nsCString& aOutDst,
+               size_t aMaxLength);
+
 } // namespace mozilla
 
 #endif
--- a/dom/media/gmp/PGMP.ipdl
+++ b/dom/media/gmp/PGMP.ipdl
@@ -29,16 +29,16 @@ parent:
   async PGMPContentChildDestroyed();
 
   async AsyncShutdownComplete();
   async AsyncShutdownRequired();
 
 child:
   async BeginAsyncShutdown();
   async CrashPluginNow();
-  intr StartPlugin();
+  intr StartPlugin(nsString adapter);
   async SetNodeId(nsCString nodeId);
   async PreloadLibs(nsCString libs);
   async CloseActive();
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/moz.build
+++ b/dom/media/gmp/moz.build
@@ -1,14 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+for cdm in CONFIG['MOZ_EME_MODULES']:
+    DEFINES['MOZ_%s_EME' % cdm.upper()] = True
+
 XPIDL_MODULE = 'content_geckomediaplugins'
 
 XPIDL_SOURCES += [
     'mozIGeckoMediaPluginChromeService.idl',
     'mozIGeckoMediaPluginService.idl',
 ]
 
 EXPORTS += [
@@ -109,16 +112,21 @@ UNIFIED_SOURCES += [
     'GMPVideoPlaneImpl.cpp',
 ]
 
 if CONFIG['OS_TARGET'] in ('WINNT', 'Darwin'):
   DIRS += [
       'rlz',
   ]
 
+if 'widevine' in CONFIG['MOZ_EME_MODULES']:
+  DIRS += [
+      'widevine-adapter',
+  ]
+
 IPDL_SOURCES += [
   'GMPTypes.ipdlh',
   'PGMP.ipdl',
   'PGMPAudioDecoder.ipdl',
   'PGMPContent.ipdl',
   'PGMPDecryptor.ipdl',
   'PGMPService.ipdl',
   'PGMPStorage.ipdl',
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/widevine-adapter/WidevineAdapter.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WidevineAdapter.h"
+#include "content_decryption_module.h"
+#include "WidevineDecryptor.h"
+#include "WidevineUtils.h"
+#include "WidevineVideoDecoder.h"
+#include "gmp-api/gmp-entrypoints.h"
+#include "gmp-api/gmp-decryption.h"
+#include "gmp-api/gmp-video-codec.h"
+#include "gmp-api/gmp-platform.h"
+#include "mozilla/StaticPtr.h"
+
+static const GMPPlatformAPI* sPlatform = nullptr;
+
+namespace mozilla {
+
+const char* WidevineKeySystem = "com.widevine.alpha";
+StaticRefPtr<CDMWrapper> sCDMWrapper;
+
+GMPErr GMPGetCurrentTime(GMPTimestamp* aOutTime) {
+  return sPlatform->getcurrenttime(aOutTime);
+}
+
+// Call on main thread only.
+GMPErr GMPSetTimerOnMainThread(GMPTask* aTask, int64_t aTimeoutMS) {
+  return sPlatform->settimer(aTask, aTimeoutMS);
+}
+
+GMPErr GMPCreateRecord(const char* aRecordName,
+                       uint32_t aRecordNameSize,
+                       GMPRecord** aOutRecord,
+                       GMPRecordClient* aClient)
+{
+  return sPlatform->createrecord(aRecordName, aRecordNameSize, aOutRecord, aClient);
+}
+
+void
+WidevineAdapter::SetAdaptee(PRLibrary* aLib)
+{
+  mLib = aLib;
+}
+
+void* GetCdmHost(int aHostInterfaceVersion, void* aUserData)
+{
+  Log("GetCdmHostFunc(%d, %p)", aHostInterfaceVersion, aUserData);
+  WidevineDecryptor* decryptor = reinterpret_cast<WidevineDecryptor*>(aUserData);
+  MOZ_ASSERT(decryptor);
+  return static_cast<cdm::Host_8*>(decryptor);
+}
+
+#define STRINGIFY(s) _STRINGIFY(s)
+#define _STRINGIFY(s) #s
+
+GMPErr
+WidevineAdapter::GMPInit(const GMPPlatformAPI* aPlatformAPI)
+{
+#ifdef ENABLE_WIDEVINE_LOG
+  if (getenv("GMP_LOG_FILE")) {
+    // Clear log file.
+    FILE* f = fopen(getenv("GMP_LOG_FILE"), "w");
+    if (f) {
+      fclose(f);
+    }
+  }
+#endif
+
+  sPlatform = aPlatformAPI;
+  if (!mLib) {
+    return GMPGenericErr;
+  }
+
+  auto init = reinterpret_cast<decltype(::INITIALIZE_CDM_MODULE)*>(
+    PR_FindFunctionSymbol(mLib, STRINGIFY(INITIALIZE_CDM_MODULE)));
+  if (!init) {
+    return GMPGenericErr;
+  }
+
+  Log(STRINGIFY(INITIALIZE_CDM_MODULE)"()");
+  init();
+
+  return GMPNoErr;
+}
+
+GMPErr
+WidevineAdapter::GMPGetAPI(const char* aAPIName,
+                           void* aHostAPI,
+                           void** aPluginAPI)
+{
+  Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p) this=0x%p",
+      aAPIName, aHostAPI, aPluginAPI, this);
+  if (!strcmp(aAPIName, GMP_API_DECRYPTOR)) {
+    if (sCDMWrapper) {
+      // We only support one CDM instance per GMP process. Fail!
+      Log("WidevineAdapter::GMPGetAPI() Tried to create more than once CDM per process! FAIL!");
+      return GMPQuotaExceededErr;
+    }
+    auto create = reinterpret_cast<decltype(::CreateCdmInstance)*>(
+      PR_FindFunctionSymbol(mLib, "CreateCdmInstance"));
+    if (!create) {
+      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p) this=0x%p FAILED to create cdm",
+        aAPIName, aHostAPI, aPluginAPI, this);
+      return GMPGenericErr;
+    }
+
+    WidevineDecryptor* decryptor = new WidevineDecryptor();
+
+    auto cdm = reinterpret_cast<cdm::ContentDecryptionModule*>(
+      create(cdm::ContentDecryptionModule::kVersion,
+             WidevineKeySystem,
+             strlen(WidevineKeySystem),
+             &GetCdmHost,
+             decryptor));
+    Log("cdm: 0x%x", cdm);
+    sCDMWrapper = new CDMWrapper(cdm);
+    decryptor->SetCDM(RefPtr<CDMWrapper>(sCDMWrapper));
+
+    cdm->Initialize(false, /* allow_distinctive_identifier */
+                    false /* allow_persistent_state */);
+
+    *aPluginAPI = decryptor;
+
+  } else if (!strcmp(aAPIName, GMP_API_VIDEO_DECODER)) {
+    *aPluginAPI = new WidevineVideoDecoder(static_cast<GMPVideoHost*>(aHostAPI), RefPtr<CDMWrapper>(sCDMWrapper));
+
+  }
+  return *aPluginAPI ? GMPNoErr : GMPNotImplementedErr;
+}
+
+void
+WidevineAdapter::GMPShutdown()
+{
+  Log("WidevineAdapter::GMPShutdown()");
+
+  decltype(::DeinitializeCdmModule)* deinit;
+  deinit = (decltype(deinit))(PR_FindFunctionSymbol(mLib, "DeinitializeCdmModule"));
+  if (deinit) {
+    Log("DeinitializeCdmModule()");
+    deinit();
+  }
+}
+
+void
+WidevineAdapter::GMPSetNodeId(const char* aNodeId, uint32_t aLength)
+{
+
+}
+
+/* static */
+bool
+WidevineAdapter::Supports(int32_t aModuleVersion,
+                          int32_t aInterfaceVersion,
+                          int32_t aHostVersion)
+{
+  return aModuleVersion == CDM_MODULE_VERSION &&
+         aInterfaceVersion == cdm::ContentDecryptionModule::kVersion &&
+         aHostVersion == cdm::Host_8::kVersion;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/widevine-adapter/WidevineAdapter.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WidevineAdapter_h_
+#define WidevineAdapter_h_
+
+#include "GMPLoader.h"
+#include "prlink.h"
+#include "GMPUtils.h"
+
+struct GMPPlatformAPI;
+
+namespace mozilla {
+
+class WidevineAdapter : public gmp::GMPAdapter {
+public:
+
+  void SetAdaptee(PRLibrary* aLib) override;
+
+  // These are called in place of the corresponding GMP API functions.
+  GMPErr GMPInit(const GMPPlatformAPI* aPlatformAPI) override;
+  GMPErr GMPGetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) override;
+  void GMPShutdown() override;
+  void GMPSetNodeId(const char* aNodeId, uint32_t aLength) override;
+
+  static bool Supports(int32_t aModuleVersion,
+                       int32_t aInterfaceVersion,
+                       int32_t aHostVersion);
+
+private:
+  PRLibrary* mLib = nullptr;
+};
+
+GMPErr GMPCreateThread(GMPThread** aThread);
+GMPErr GMPRunOnMainThread(GMPTask* aTask);
+GMPErr GMPCreateMutex(GMPMutex** aMutex);
+
+// Call on main thread only.
+GMPErr GMPCreateRecord(const char* aRecordName,
+                       uint32_t aRecordNameSize,
+                       GMPRecord** aOutRecord,
+                       GMPRecordClient* aClient);
+
+// Call on main thread only.
+GMPErr GMPSetTimerOnMainThread(GMPTask* aTask, int64_t aTimeoutMS);
+
+GMPErr GMPGetCurrentTime(GMPTimestamp* aOutTime);
+
+GMPErr GMPCreateRecordIterator(RecvGMPRecordIteratorPtr aRecvIteratorFunc,
+                               void* aUserArg);
+
+} // namespace mozilla
+
+#endif // WidevineAdapter_h_
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp
@@ -0,0 +1,453 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WidevineDecryptor.h"
+
+#include "WidevineAdapter.h"
+#include "WidevineUtils.h"
+#include <mozilla/SizePrintfMacros.h>
+#include <stdarg.h>
+
+using namespace cdm;
+using namespace std;
+
+namespace mozilla {
+
+
+WidevineDecryptor::WidevineDecryptor()
+  : mCallback(nullptr)
+{
+  Log("WidevineDecryptor created this=%p", this);
+  AddRef(); // Released in DecryptingComplete().
+}
+
+WidevineDecryptor::~WidevineDecryptor()
+{
+  Log("WidevineDecryptor destroyed this=%p", this);
+}
+
+void
+WidevineDecryptor::SetCDM(RefPtr<CDMWrapper> aCDM)
+{
+  mCDM = aCDM;
+}
+
+void
+WidevineDecryptor::Init(GMPDecryptorCallback* aCallback)
+{
+  mCallback = aCallback;
+  mCallback->SetCapabilities(GMP_EME_CAP_DECRYPT_AND_DECODE_VIDEO |
+                             GMP_EME_CAP_DECRYPT_AUDIO);
+}
+
+static SessionType
+ToCDMSessionType(GMPSessionType aSessionType)
+{
+  switch (aSessionType) {
+    case kGMPTemporySession: return kTemporary;
+    case kGMPPersistentSession: return kPersistentLicense;
+    case kGMPSessionInvalid: return kTemporary;
+    // TODO: kPersistentKeyRelease
+  }
+  MOZ_ASSERT(false); // Not supposed to get here.
+  return kTemporary;
+}
+
+void
+WidevineDecryptor::CreateSession(uint32_t aCreateSessionToken,
+                                 uint32_t aPromiseId,
+                                 const char* aInitDataType,
+                                 uint32_t aInitDataTypeSize,
+                                 const uint8_t* aInitData,
+                                 uint32_t aInitDataSize,
+                                 GMPSessionType aSessionType)
+{
+  Log("Decryptor::CreateSession(token=%d, pid=%d)", aCreateSessionToken, aPromiseId);
+  MOZ_ASSERT(!strcmp(aInitDataType, "cenc"));
+  mPromiseIdToNewSessionTokens[aPromiseId] = aCreateSessionToken;
+  CDM()->CreateSessionAndGenerateRequest(aPromiseId,
+                                         ToCDMSessionType(aSessionType),
+                                         kCenc,
+                                         aInitData, aInitDataSize);
+}
+
+void
+WidevineDecryptor::LoadSession(uint32_t aPromiseId,
+                               const char* aSessionId,
+                               uint32_t aSessionIdLength)
+{
+  Log("Decryptor::LoadSession(pid=%d, %s)", aPromiseId, aSessionId);
+  // TODO: session type??
+  CDM()->LoadSession(aPromiseId, kPersistentLicense, aSessionId, aSessionIdLength);
+}
+
+void
+WidevineDecryptor::UpdateSession(uint32_t aPromiseId,
+                                 const char* aSessionId,
+                                 uint32_t aSessionIdLength,
+                                 const uint8_t* aResponse,
+                                 uint32_t aResponseSize)
+{
+  Log("Decryptor::UpdateSession(pid=%d, session=%s)", aPromiseId, aSessionId);
+  CDM()->UpdateSession(aPromiseId, aSessionId, aSessionIdLength, aResponse, aResponseSize);
+}
+
+void
+WidevineDecryptor::CloseSession(uint32_t aPromiseId,
+                                const char* aSessionId,
+                                uint32_t aSessionIdLength)
+{
+  Log("Decryptor::CloseSession(pid=%d, session=%s)", aPromiseId, aSessionId);
+  CDM()->CloseSession(aPromiseId, aSessionId, aSessionIdLength);
+}
+
+void
+WidevineDecryptor::RemoveSession(uint32_t aPromiseId,
+                                 const char* aSessionId,
+                                 uint32_t aSessionIdLength)
+{
+  Log("Decryptor::RemoveSession(%s)", aSessionId);
+  CDM()->RemoveSession(aPromiseId, aSessionId, aSessionIdLength);
+}
+
+void
+WidevineDecryptor::SetServerCertificate(uint32_t aPromiseId,
+                                        const uint8_t* aServerCert,
+                                        uint32_t aServerCertSize)
+{
+  Log("Decryptor::SetServerCertificate()");
+  CDM()->SetServerCertificate(aPromiseId, aServerCert, aServerCertSize);
+}
+
+class WidevineDecryptedBlock : public cdm::DecryptedBlock {
+public:
+
+  WidevineDecryptedBlock()
+    : mBuffer(nullptr)
+    , mTimestamp(0)
+  {
+  }
+
+  ~WidevineDecryptedBlock() {
+    if (mBuffer) {
+      mBuffer->Destroy();
+      mBuffer = nullptr;
+    }
+  }
+
+  void SetDecryptedBuffer(cdm::Buffer* aBuffer) override {
+    mBuffer = aBuffer;
+  }
+
+  cdm::Buffer* DecryptedBuffer() override {
+    return mBuffer;
+  }
+
+  void SetTimestamp(int64_t aTimestamp) override {
+    mTimestamp = aTimestamp;
+  }
+
+  int64_t Timestamp() const override {
+    return mTimestamp;
+  }
+
+private:
+  cdm::Buffer* mBuffer;
+  int64_t mTimestamp;
+};
+
+void
+WidevineDecryptor::Decrypt(GMPBuffer* aBuffer,
+                           GMPEncryptedBufferMetadata* aMetadata)
+{
+  const GMPEncryptedBufferMetadata* crypto = aMetadata;
+  InputBuffer sample;
+  nsTArray<SubsampleEntry> subsamples;
+  InitInputBuffer(crypto, aBuffer->Id(), aBuffer->Data(), aBuffer->Size(), sample, subsamples);
+  WidevineDecryptedBlock decrypted;
+  Status rv = CDM()->Decrypt(sample, &decrypted);
+  Log("Decryptor::Decrypt(timestamp=%lld) rv=%d sz=%d",
+      sample.timestamp, rv, decrypted.DecryptedBuffer()->Size());
+  if (rv == kSuccess) {
+    aBuffer->Resize(decrypted.DecryptedBuffer()->Size());
+    memcpy(aBuffer->Data(),
+           decrypted.DecryptedBuffer()->Data(),
+           decrypted.DecryptedBuffer()->Size());
+  }
+  mCallback->Decrypted(aBuffer, ToGMPErr(rv));
+}
+
+void
+WidevineDecryptor::DecryptingComplete()
+{
+  Log("WidevineDecryptor::DecryptingComplete() this=%p", this);
+  mCDM = nullptr;
+  Release();
+}
+
+class WidevineBuffer : public cdm::Buffer {
+public:
+  WidevineBuffer(size_t aSize) {
+    Log("WidevineBuffer(size=" PRIuSIZE ") created", aSize);
+    mBuffer.SetLength(aSize);
+  }
+  ~WidevineBuffer() {
+    Log("WidevineBuffer(size=" PRIuSIZE ") destroyed", Size());
+  }
+  void Destroy() override { delete this; }
+  uint32_t Capacity() const override { return mBuffer.Length(); };
+  uint8_t* Data() override { return mBuffer.Elements(); }
+  void SetSize(uint32_t aSize) override { mBuffer.SetLength(aSize); }
+  uint32_t Size() const override { return mBuffer.Length(); }
+
+private:
+  WidevineBuffer(const WidevineBuffer&);
+  void operator=(const WidevineBuffer&);
+
+  nsTArray<uint8_t> mBuffer;
+};
+
+Buffer*
+WidevineDecryptor::Allocate(uint32_t aCapacity)
+{
+  Log("Decryptor::Allocate(capacity=%u)", aCapacity);
+  return new WidevineBuffer(aCapacity);
+}
+
+class TimerTask : public GMPTask {
+public:
+  TimerTask(WidevineDecryptor* aDecryptor,
+            RefPtr<CDMWrapper> aCDM,
+            void* aContext)
+    : mDecryptor(aDecryptor)
+    , mCDM(aCDM)
+    , mContext(aContext)
+  {
+  }
+  ~TimerTask() override {}
+  void Run() override {
+    mCDM->GetCDM()->TimerExpired(mContext);
+  }
+  void Destroy() override { delete this; }
+private:
+  RefPtr<WidevineDecryptor> mDecryptor;
+  RefPtr<CDMWrapper> mCDM;
+  void* mContext;
+};
+
+void
+WidevineDecryptor::SetTimer(int64_t aDelayMs, void* aContext)
+{
+  Log("Decryptor::SetTimer(delay_ms=%lld, context=0x%x)", aDelayMs, aContext);
+  if (mCDM) {
+    GMPSetTimerOnMainThread(new TimerTask(this, mCDM, aContext), aDelayMs);
+  }
+}
+
+Time
+WidevineDecryptor::GetCurrentWallTime()
+{
+  GMPTimestamp gmpTime = 0;
+  GMPGetCurrentTime(&gmpTime);
+  double t = (double)gmpTime / 1e3;
+  Log("Decryptor::GetCurrentWallTime()= %lf", t);
+  return t;
+}
+
+void
+WidevineDecryptor::OnResolveNewSessionPromise(uint32_t aPromiseId,
+                                              const char* aSessionId,
+                                              uint32_t aSessionIdSize)
+{
+  Log("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d)", aPromiseId);
+  auto iter = mPromiseIdToNewSessionTokens.find(aPromiseId);
+  if (iter == mPromiseIdToNewSessionTokens.end()) {
+    Log("FAIL: Decryptor::OnResolveNewSessionPromise(aPromiseId=%d) unknown aPromiseId", aPromiseId);
+    return;
+  }
+  mCallback->SetSessionId(iter->second, aSessionId, aSessionIdSize);
+  mCallback->ResolvePromise(aPromiseId);
+  mPromiseIdToNewSessionTokens.erase(iter);
+}
+
+void
+WidevineDecryptor::OnResolvePromise(uint32_t aPromiseId)
+{
+  Log("Decryptor::OnResolvePromise(aPromiseId=%d)", aPromiseId);
+  mCallback->ResolvePromise(aPromiseId);
+}
+
+static GMPDOMException
+ToGMPDOMException(cdm::Error aError)
+{
+  switch (aError) {
+    case kNotSupportedError: return kGMPNotSupportedError;
+    case kInvalidStateError: return kGMPInvalidStateError;
+    case kInvalidAccessError: return kGMPInvalidAccessError;
+    case kQuotaExceededError: return kGMPQuotaExceededError;
+    case kUnknownError: return kGMPInvalidModificationError; // Note: Unique placeholder.
+    case kClientError: return kGMPAbortError; // Note: Unique placeholder.
+    case kOutputError: return kGMPSecurityError; // Note: Unique placeholder.
+  };
+  return kGMPTimeoutError; // Note: Unique placeholder.
+}
+
+void
+WidevineDecryptor::OnRejectPromise(uint32_t aPromiseId,
+                                   Error aError,
+                                   uint32_t aSystemCode,
+                                   const char* aErrorMessage,
+                                   uint32_t aErrorMessageSize)
+{
+  Log("Decryptor::OnRejectPromise(aPromiseId=%d, err=%d, sysCode=%d, msg=%s)",
+      aPromiseId, (int)aError, aSystemCode, aErrorMessage);
+  mCallback->RejectPromise(aPromiseId,
+                           ToGMPDOMException(aError),
+                           !aErrorMessageSize ? "" : aErrorMessage,
+                           aErrorMessageSize);
+}
+
+static GMPSessionMessageType
+ToGMPMessageType(MessageType message_type)
+{
+  switch (message_type) {
+    case kLicenseRequest: return kGMPLicenseRequest;
+    case kLicenseRenewal: return kGMPLicenseRenewal;
+    case kLicenseRelease: return kGMPLicenseRelease;
+  }
+  return kGMPMessageInvalid;
+}
+
+void
+WidevineDecryptor::OnSessionMessage(const char* aSessionId,
+                                    uint32_t aSessionIdSize,
+                                    MessageType aMessageType,
+                                    const char* aMessage,
+                                    uint32_t aMessageSize,
+                                    const char* aLegacyDestinationUrl,
+                                    uint32_t aLegacyDestinationUrlLength)
+{
+  Log("Decryptor::OnSessionMessage()");
+  mCallback->SessionMessage(aSessionId,
+                            aSessionIdSize,
+                            ToGMPMessageType(aMessageType),
+                            reinterpret_cast<const uint8_t*>(aMessage),
+                            aMessageSize);
+}
+
+static GMPMediaKeyStatus
+ToGMPKeyStatus(KeyStatus aStatus)
+{
+  switch (aStatus) {
+    case kUsable: return kGMPUsable;
+    case kInternalError: return kGMPInternalError;
+    case kExpired: return kGMPExpired;
+    case kOutputRestricted: return kGMPOutputRestricted;
+    case kOutputDownscaled: return kGMPOutputDownscaled;
+    case kStatusPending: return kGMPStatusPending;
+    case kReleased: return kGMPReleased;
+  }
+  return kGMPUnknown;
+}
+
+void
+WidevineDecryptor::OnSessionKeysChange(const char* aSessionId,
+                                       uint32_t aSessionIdSize,
+                                       bool aHasAdditionalUsableKey,
+                                       const KeyInformation* aKeysInfo,
+                                       uint32_t aKeysInfoCount)
+{
+  Log("Decryptor::OnSessionKeysChange()");
+  for (uint32_t i = 0; i < aKeysInfoCount; i++) {
+    mCallback->KeyStatusChanged(aSessionId,
+                                aSessionIdSize,
+                                aKeysInfo[i].key_id,
+                                aKeysInfo[i].key_id_size,
+                                ToGMPKeyStatus(aKeysInfo[i].status));
+  }
+}
+
+static GMPTimestamp
+ToGMPTime(Time aCDMTime)
+{
+  return static_cast<GMPTimestamp>(aCDMTime * 1000);
+}
+
+void
+WidevineDecryptor::OnExpirationChange(const char* aSessionId,
+                                      uint32_t aSessionIdSize,
+                                      Time aNewExpiryTime)
+{
+  Log("Decryptor::OnExpirationChange(sid=%s) t=%lf", aSessionId, aNewExpiryTime);
+  GMPTimestamp expiry = ToGMPTime(aNewExpiryTime);
+  if (aNewExpiryTime == 0) {
+    return;
+  }
+  mCallback->ExpirationChange(aSessionId, aSessionIdSize, expiry);
+}
+
+void
+WidevineDecryptor::OnSessionClosed(const char* aSessionId,
+                                   uint32_t aSessionIdSize)
+{
+  Log("Decryptor::OnSessionClosed(sid=%s)", aSessionId);
+  mCallback->SessionClosed(aSessionId, aSessionIdSize);
+}
+
+void
+WidevineDecryptor::OnLegacySessionError(const char* aSessionId,
+                                        uint32_t aSessionIdLength,
+                                        Error aError,
+                                        uint32_t aSystemCode,
+                                        const char* aErrorMessage,
+                                        uint32_t aErrorMessageLength)
+{
+  Log("Decryptor::OnSessionClosed(sid=%s, error=%d)", aSessionId, (int)aError);
+  mCallback->SessionError(aSessionId,
+                          aSessionIdLength,
+                          ToGMPDOMException(aError),
+                          aSystemCode,
+                          aErrorMessage,
+                          aErrorMessageLength);
+}
+
+void
+WidevineDecryptor::SendPlatformChallenge(const char* aServiceId,
+                                         uint32_t aServiceIdSize,
+                                         const char* aChallenge,
+                                         uint32_t aChallengeSize)
+{
+  Log("Decryptor::SendPlatformChallenge(service_id=%s)", aServiceId);
+}
+
+void
+WidevineDecryptor::EnableOutputProtection(uint32_t aDesiredProtectionMask)
+{
+  Log("Decryptor::EnableOutputProtection(mask=0x%x)", aDesiredProtectionMask);
+}
+
+void
+WidevineDecryptor::QueryOutputProtectionStatus()
+{
+  Log("Decryptor::QueryOutputProtectionStatus()");
+}
+
+void
+WidevineDecryptor::OnDeferredInitializationDone(StreamType aStreamType,
+                                                Status aDecoderStatus)
+{
+  Log("Decryptor::OnDeferredInitializationDone()");
+}
+
+FileIO*
+WidevineDecryptor::CreateFileIO(FileIOClient* aClient)
+{
+  Log("Decryptor::CreateFileIO()");
+  // Persistent storage not required or supported!
+  MOZ_ASSERT(false);
+  return nullptr;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/widevine-adapter/WidevineDecryptor.h
@@ -0,0 +1,127 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WidevineDecryptor_h_
+#define WidevineDecryptor_h_
+
+#include "stddef.h"
+#include "content_decryption_module.h"
+#include "gmp-api/gmp-decryption.h"
+#include "mozilla/RefPtr.h"
+#include "WidevineUtils.h"
+#include <map>
+
+namespace mozilla {
+
+class WidevineDecryptor : public GMPDecryptor
+                        , public cdm::Host_8
+{
+public:
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WidevineDecryptor)
+
+  WidevineDecryptor();
+
+  void SetCDM(RefPtr<CDMWrapper> aCDM);
+
+  // GMPDecryptor
+  void Init(GMPDecryptorCallback* aCallback) override;
+
+  void CreateSession(uint32_t aCreateSessionToken,
+                     uint32_t aPromiseId,
+                     const char* aInitDataType,
+                     uint32_t aInitDataTypeSize,
+                     const uint8_t* aInitData,
+                     uint32_t aInitDataSize,
+                     GMPSessionType aSessionType) override;
+
+  void LoadSession(uint32_t aPromiseId,
+                   const char* aSessionId,
+                   uint32_t aSessionIdLength) override;
+
+  void UpdateSession(uint32_t aPromiseId,
+                     const char* aSessionId,
+                     uint32_t aSessionIdLength,
+                     const uint8_t* aResponse,
+                     uint32_t aResponseSize) override;
+
+  void CloseSession(uint32_t aPromiseId,
+                    const char* aSessionId,
+                    uint32_t aSessionIdLength) override;
+
+  void RemoveSession(uint32_t aPromiseId,
+                     const char* aSessionId,
+                     uint32_t aSessionIdLength) override;
+
+  void SetServerCertificate(uint32_t aPromiseId,
+                            const uint8_t* aServerCert,
+                            uint32_t aServerCertSize) override;
+
+  void Decrypt(GMPBuffer* aBuffer,
+               GMPEncryptedBufferMetadata* aMetadata) override;
+
+  void DecryptingComplete() override;
+
+
+  // cdm::Host_8
+  cdm::Buffer* Allocate(uint32_t aCapacity) override;
+  void SetTimer(int64_t aDelayMs, void* aContext) override;
+  cdm::Time GetCurrentWallTime() override;
+  void OnResolveNewSessionPromise(uint32_t aPromiseId,
+                                  const char* aSessionId,
+                                  uint32_t aSessionIdSize) override;
+  void OnResolvePromise(uint32_t aPromiseId) override;
+  void OnRejectPromise(uint32_t aPromiseId,
+                       cdm::Error aError,
+                       uint32_t aSystemCode,
+                       const char* aErrorMessage,
+                       uint32_t aErrorMessageSize) override;
+  void OnSessionMessage(const char* aSessionId,
+                        uint32_t aSessionIdSize,
+                        cdm::MessageType aMessageType,
+                        const char* aMessage,
+                        uint32_t aMessageSize,
+                        const char* aLegacyDestinationUrl,
+                        uint32_t aLegacyDestinationUrlLength) override;
+  void OnSessionKeysChange(const char* aSessionId,
+                           uint32_t aSessionIdSize,
+                           bool aHasAdditionalUsableKey,
+                           const cdm::KeyInformation* aKeysInfo,
+                           uint32_t aKeysInfoCount) override;
+  void OnExpirationChange(const char* aSessionId,
+                          uint32_t aSessionIdSize,
+                          cdm::Time aNewExpiryTime) override;
+  void OnSessionClosed(const char* aSessionId,
+                       uint32_t aSessionIdSize) override;
+  void OnLegacySessionError(const char* aSessionId,
+                            uint32_t aSessionId_length,
+                            cdm::Error aError,
+                            uint32_t aSystemCode,
+                            const char* aErrorMessage,
+                            uint32_t aErrorMessageLength) override;
+  void SendPlatformChallenge(const char* aServiceId,
+                             uint32_t aServiceIdSize,
+                             const char* aChallenge,
+                             uint32_t aChallengeSize) override;
+  void EnableOutputProtection(uint32_t aDesiredProtectionMask) override;
+  void QueryOutputProtectionStatus() override;
+  void OnDeferredInitializationDone(cdm::StreamType aStreamType,
+                                    cdm::Status aDecoderStatus) override;
+  cdm::FileIO* CreateFileIO(cdm::FileIOClient* aClient) override;
+
+  GMPDecryptorCallback* Callback() const { return mCallback; }
+  RefPtr<CDMWrapper> GetCDMWrapper() const { return mCDM; }
+private:
+  ~WidevineDecryptor();
+  RefPtr<CDMWrapper> mCDM;
+  cdm::ContentDecryptionModule_8* CDM() { return mCDM->GetCDM(); }
+
+  GMPDecryptorCallback* mCallback;
+  std::map<uint32_t, uint32_t> mPromiseIdToNewSessionTokens;
+};
+
+} // namespace mozilla
+
+#endif // WidevineDecryptor_h_
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/widevine-adapter/WidevineUtils.cpp
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WidevineUtils.h"
+
+#include "gmp-api/gmp-errors.h"
+#include <stdarg.h>
+#include <stdio.h>
+
+namespace mozilla {
+
+#ifdef ENABLE_WIDEVINE_LOG
+void
+Log(const char* aFormat, ...)
+{
+  va_list ap;
+  va_start(ap, aFormat);
+  const size_t len = 1024;
+  char buf[len];
+  vsnprintf(buf, len, aFormat, ap);
+  va_end(ap);
+  if (getenv("GMP_LOG_FILE")) {
+    FILE* f = fopen(getenv("GMP_LOG_FILE"), "a");
+    if (f) {
+      fprintf(f, "%s\n", buf);
+      fflush(f);
+      fclose(f);
+      f = nullptr;
+    }
+  } else {
+    printf("LOG: %s\n", buf);
+  }
+}
+#endif // ENABLE_WIDEVINE_LOG
+
+GMPErr
+ToGMPErr(cdm::Status aStatus)
+{
+  switch (aStatus) {
+    case cdm::kSuccess: return GMPNoErr;
+    case cdm::kNeedMoreData: return GMPGenericErr;
+    case cdm::kNoKey: return GMPNoKeyErr;
+    case cdm::kSessionError: return GMPGenericErr;
+    case cdm::kDecryptError: return GMPCryptoErr;
+    case cdm::kDecodeError: return GMPDecodeErr;
+    case cdm::kDeferredInitialization: return GMPGenericErr;
+    default: return GMPGenericErr;
+  }
+}
+
+void InitInputBuffer(const GMPEncryptedBufferMetadata* aCrypto,
+                     int64_t aTimestamp,
+                     const uint8_t* aData,
+                     size_t aDataSize,
+                     cdm::InputBuffer &aInputBuffer,
+                     nsTArray<cdm::SubsampleEntry> &aSubsamples)
+{
+  if (aCrypto) {
+    aInputBuffer.key_id = aCrypto->KeyId();
+    aInputBuffer.key_id_size = aCrypto->KeyIdSize();
+    aInputBuffer.iv = aCrypto->IV();
+    aInputBuffer.iv_size = aCrypto->IVSize();
+    aInputBuffer.num_subsamples = aCrypto->NumSubsamples();
+    aSubsamples.SetCapacity(aInputBuffer.num_subsamples);
+    const uint16_t* clear = aCrypto->ClearBytes();
+    const uint32_t* cipher = aCrypto->CipherBytes();
+    for (size_t i = 0; i < aCrypto->NumSubsamples(); i++) {
+      aSubsamples.AppendElement(cdm::SubsampleEntry(clear[i], cipher[i]));
+    }
+  }
+  aInputBuffer.data = aData;
+  aInputBuffer.data_size = aDataSize;
+  aInputBuffer.subsamples = aSubsamples.Elements();
+  aInputBuffer.timestamp = aTimestamp;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/widevine-adapter/WidevineUtils.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WidevineUtils_h_
+#define WidevineUtils_h_
+
+#include "stddef.h"
+#include "content_decryption_module.h"
+#include "gmp-api/gmp-decryption.h"
+#include "gmp-api/gmp-platform.h"
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+// Uncomment for logging...
+//#define ENABLE_WIDEVINE_LOG 1
+#ifdef ENABLE_WIDEVINE_LOG
+void
+Log(const char* aFormat, ...);
+#else
+#define Log(...)
+#endif // ENABLE_WIDEVINE_LOG
+
+
+#define ENSURE_TRUE(condition, rv) { \
+  if (!(condition)) {\
+    Log("ENSURE_TRUE FAILED %s:%d", __FILE__, __LINE__); \
+    return rv; \
+  } \
+} \
+
+#define ENSURE_GMP_SUCCESS(err, rv) { \
+  if (GMP_FAILED(err)) {\
+    Log("ENSURE_GMP_SUCCESS FAILED %s:%d", __FILE__, __LINE__); \
+    return rv; \
+    } \
+} \
+
+GMPErr
+ToGMPErr(cdm::Status aStatus);
+
+class CDMWrapper {
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CDMWrapper)
+
+  CDMWrapper(cdm::ContentDecryptionModule_8* aCDM)
+    : mCDM(aCDM)
+  {}
+  cdm::ContentDecryptionModule_8* GetCDM() const { return mCDM; }
+private:
+  cdm::ContentDecryptionModule_8* mCDM;
+  ~CDMWrapper() {
+    Log("CDMWrapper destroying CDM=%p", mCDM);
+    mCDM->Destroy();
+    mCDM = nullptr;
+  }
+};
+
+void InitInputBuffer(const GMPEncryptedBufferMetadata* aCrypto,
+                     int64_t aTimestamp,
+                     const uint8_t* aData,
+                     size_t aDataSize,
+                     cdm::InputBuffer &aInputBuffer,
+                     nsTArray<cdm::SubsampleEntry> &aSubsamples);
+
+} // namespace mozilla
+
+#endif // WidevineUtils_h_
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WidevineVideoDecoder.h"
+
+#include "mp4_demuxer/AnnexB.h"
+#include "WidevineUtils.h"
+#include "WidevineVideoFrame.h"
+
+using namespace cdm;
+
+namespace mozilla {
+
+WidevineVideoDecoder::WidevineVideoDecoder(GMPVideoHost* aVideoHost,
+                                           RefPtr<CDMWrapper> aCDM)
+  : mVideoHost(aVideoHost)
+  , mCDM(aCDM)
+  , mExtraData(new MediaByteBuffer())
+  , mSentInput(false)
+{
+  Log("WidevineVideoDecoder created this=%p", this);
+
+  AddRef();
+}
+
+WidevineVideoDecoder::~WidevineVideoDecoder()
+{
+  Log("WidevineVideoDecoder destroyed this=%p", this);
+}
+
+static
+VideoDecoderConfig::VideoCodecProfile
+ToCDMH264Profile(uint8_t aProfile)
+{
+  switch (aProfile) {
+    case 66: return VideoDecoderConfig::kH264ProfileBaseline;
+    case 77: return VideoDecoderConfig::kH264ProfileMain;
+    case 88: return VideoDecoderConfig::kH264ProfileExtended;
+    case 100: return VideoDecoderConfig::kH264ProfileHigh;
+    case 110: return VideoDecoderConfig::kH264ProfileHigh10;
+    case 122: return VideoDecoderConfig::kH264ProfileHigh422;
+    case 144: return VideoDecoderConfig::kH264ProfileHigh444Predictive;
+  }
+  return VideoDecoderConfig::kUnknownVideoCodecProfile;
+}
+
+void
+WidevineVideoDecoder::InitDecode(const GMPVideoCodec& aCodecSettings,
+                                 const uint8_t* aCodecSpecific,
+                                 uint32_t aCodecSpecificLength,
+                                 GMPVideoDecoderCallback* aCallback,
+                                 int32_t aCoreCount)
+{
+  mCallback = aCallback;
+  VideoDecoderConfig config;
+  config.codec = VideoDecoderConfig::kCodecH264; // TODO: others.
+  const GMPVideoCodecH264* h264 = (const GMPVideoCodecH264*)(aCodecSpecific);
+  config.profile = ToCDMH264Profile(h264->mAVCC.mProfile);
+  config.format = kYv12;
+  config.coded_size = Size(aCodecSettings.mWidth, aCodecSettings.mHeight);
+  mExtraData->AppendElements(aCodecSpecific + 1, aCodecSpecificLength);
+  config.extra_data = mExtraData->Elements();
+  config.extra_data_size = mExtraData->Length();
+  Status rv = CDM()->InitializeVideoDecoder(config);
+  if (rv != kSuccess) {
+    mCallback->Error(ToGMPErr(rv));
+    return;
+  }
+  Log("WidevineVideoDecoder::InitDecode() rv=%d", rv);
+  mAnnexB = mp4_demuxer::AnnexB::ConvertExtraDataToAnnexB(mExtraData);
+}
+
+void
+WidevineVideoDecoder::Decode(GMPVideoEncodedFrame* aInputFrame,
+                             bool aMissingFrames,
+                             const uint8_t* aCodecSpecificInfo,
+                             uint32_t aCodecSpecificInfoLength,
+                             int64_t aRenderTimeMs)
+{
+  // We may not get the same out of the CDM decoder as we put in, and there
+  // may be some latency, i.e. we may need to input (say) 30 frames before
+  // we receive output. So we need to store the durations of the frames input,
+  // and retrieve them on output.
+  mFrameDurations[aInputFrame->TimeStamp()] = aInputFrame->Duration();
+
+  mSentInput = true;
+  InputBuffer sample;
+
+  RefPtr<MediaRawData> raw(new MediaRawData(aInputFrame->Buffer(), aInputFrame->Size()));
+  raw->mExtraData = mExtraData;
+  raw->mKeyframe = (aInputFrame->FrameType() == kGMPKeyFrame);
+  // Convert input from AVCC, which GMPAPI passes in, to AnnexB, which
+  // Chromium uses internally.
+  mp4_demuxer::AnnexB::ConvertSampleToAnnexB(raw);
+
+  const GMPEncryptedBufferMetadata* crypto = aInputFrame->GetDecryptionData();
+  nsTArray<SubsampleEntry> subsamples;
+  InitInputBuffer(crypto, aInputFrame->TimeStamp(), raw->Data(), raw->Size(), sample, subsamples);
+
+  // For keyframes, ConvertSampleToAnnexB will stick the AnnexB extra data
+  // at the start of the input. So we need to account for that as clear data
+  // in the subsamples.
+  if (raw->mKeyframe && !subsamples.IsEmpty()) {
+    subsamples[0].clear_bytes += mAnnexB->Length();
+  }
+
+  WidevineVideoFrame frame;
+  Status rv = CDM()->DecryptAndDecodeFrame(sample, &frame);
+  Log("WidevineVideoDecoder::Decode(timestamp=%lld) rv=%d", sample.timestamp, rv);
+
+  // Destroy frame, so that the shmem is now free to be used to return
+  // output to the Gecko process.
+  aInputFrame->Destroy();
+  aInputFrame = nullptr;
+
+  if (rv == kSuccess) {
+    if (!ReturnOutput(frame)) {
+      Log("WidevineVideoDecoder::Decode() Failed in ReturnOutput()");
+      mCallback->Error(GMPDecodeErr);
+      return;
+    }
+    mCallback->InputDataExhausted();
+  } else if (rv == kNeedMoreData) {
+    mCallback->InputDataExhausted();
+  } else {
+    mCallback->Error(ToGMPErr(rv));
+  }
+}
+
+bool
+WidevineVideoDecoder::ReturnOutput(WidevineVideoFrame& aCDMFrame)
+{
+  GMPVideoFrame* f = nullptr;
+  auto err = mVideoHost->CreateFrame(kGMPI420VideoFrame, &f);
+  if (GMP_FAILED(err) || !f) {
+    Log("Failed to create i420 frame!\n");
+    return false;
+  }
+  auto gmpFrame = static_cast<GMPVideoi420Frame*>(f);
+  Size size = aCDMFrame.Size();
+  const int32_t yStride = aCDMFrame.Stride(VideoFrame::kYPlane);
+  const int32_t uStride = aCDMFrame.Stride(VideoFrame::kUPlane);
+  const int32_t vStride = aCDMFrame.Stride(VideoFrame::kVPlane);
+  const int32_t halfHeight = size.height / 2;
+  err = gmpFrame->CreateEmptyFrame(size.width,
+                                   size.height,
+                                   yStride,
+                                   uStride,
+                                   vStride);
+  ENSURE_GMP_SUCCESS(err, false);
+
+  err = gmpFrame->SetWidth(size.width);
+  ENSURE_GMP_SUCCESS(err, false);
+
+  err = gmpFrame->SetHeight(size.height);
+  ENSURE_GMP_SUCCESS(err, false);
+
+  Buffer* buffer = aCDMFrame.FrameBuffer();
+  uint8_t* outBuffer = gmpFrame->Buffer(kGMPYPlane);
+  ENSURE_TRUE(outBuffer != nullptr, false);
+  MOZ_ASSERT(gmpFrame->AllocatedSize(kGMPYPlane) >= yStride*size.height);
+  memcpy(outBuffer,
+         buffer->Data() + aCDMFrame.PlaneOffset(VideoFrame::kYPlane),
+         yStride * size.height);
+
+  outBuffer = gmpFrame->Buffer(kGMPUPlane);
+  ENSURE_TRUE(outBuffer != nullptr, false);
+  MOZ_ASSERT(gmpFrame->AllocatedSize(kGMPUPlane) >= uStride * halfHeight);
+  memcpy(outBuffer,
+         buffer->Data() + aCDMFrame.PlaneOffset(VideoFrame::kUPlane),
+         uStride * halfHeight);
+
+  outBuffer = gmpFrame->Buffer(kGMPVPlane);
+  ENSURE_TRUE(outBuffer != nullptr, false);
+  MOZ_ASSERT(gmpFrame->AllocatedSize(kGMPVPlane) >= vStride * halfHeight);
+  memcpy(outBuffer,
+         buffer->Data() + aCDMFrame.PlaneOffset(VideoFrame::kVPlane),
+         vStride * halfHeight);
+
+  gmpFrame->SetTimestamp(aCDMFrame.Timestamp());
+
+  auto d = mFrameDurations.find(aCDMFrame.Timestamp());
+  if (d != mFrameDurations.end()) {
+    gmpFrame->SetDuration(d->second);
+    mFrameDurations.erase(d);
+  }
+
+  mCallback->Decoded(gmpFrame);
+
+  return true;
+}
+
+void
+WidevineVideoDecoder::Reset()
+{
+  Log("WidevineVideoDecoder::Reset() mSentInput=%d", mSentInput);
+  if (mSentInput) {
+    CDM()->ResetDecoder(kStreamTypeVideo);
+  }
+  mFrameDurations.clear();
+  mCallback->ResetComplete();
+  mSentInput = false;
+}
+
+void
+WidevineVideoDecoder::Drain()
+{
+  Log("WidevineVideoDecoder::Drain()");
+
+  Status rv = kSuccess;
+  while (rv == kSuccess) {
+    WidevineVideoFrame frame;
+    InputBuffer sample;
+    Status rv = CDM()->DecryptAndDecodeFrame(sample, &frame);
+    Log("WidevineVideoDecoder::Drain();  DecryptAndDecodeFrame() rv=%d", rv);
+    if (frame.Format() == kUnknownVideoFormat) {
+      break;
+    }
+    if (rv == kSuccess) {
+      if (!ReturnOutput(frame)) {
+        Log("WidevineVideoDecoder::Decode() Failed in ReturnOutput()");
+      }
+    }
+  }
+
+  CDM()->ResetDecoder(kStreamTypeVideo);
+  mCallback->DrainComplete();
+}
+
+void
+WidevineVideoDecoder::DecodingComplete()
+{
+  Log("WidevineVideoDecoder::DecodingComplete()");
+  if (mCDM) {
+    CDM()->DeinitializeDecoder(kStreamTypeVideo);
+    mCDM = nullptr;
+  }
+  Release();
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WidevineVideoDecoder_h_
+#define WidevineVideoDecoder_h_
+
+#include "stddef.h"
+#include "content_decryption_module.h"
+#include "gmp-api/gmp-video-decode.h"
+#include "gmp-api/gmp-video-host.h"
+#include "MediaData.h"
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+#include "WidevineDecryptor.h"
+#include "WidevineVideoFrame.h"
+#include <map>
+
+namespace mozilla {
+
+class WidevineVideoDecoder : public GMPVideoDecoder {
+public:
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WidevineVideoDecoder)
+
+  WidevineVideoDecoder(GMPVideoHost* aVideoHost,
+                       RefPtr<CDMWrapper> aCDM);
+  void InitDecode(const GMPVideoCodec& aCodecSettings,
+                  const uint8_t* aCodecSpecific,
+                  uint32_t aCodecSpecificLength,
+                  GMPVideoDecoderCallback* aCallback,
+                  int32_t aCoreCount) override;
+  void Decode(GMPVideoEncodedFrame* aInputFrame,
+              bool aMissingFrames,
+              const uint8_t* aCodecSpecificInfo,
+              uint32_t aCodecSpecificInfoLength,
+              int64_t aRenderTimeMs = -1) override;
+  void Reset() override;
+  void Drain() override;
+  void DecodingComplete() override;
+
+private:
+
+  ~WidevineVideoDecoder();
+
+  cdm::ContentDecryptionModule_8* CDM() { return mCDM->GetCDM(); }
+
+  bool ReturnOutput(WidevineVideoFrame& aFrame);
+
+  GMPVideoHost* mVideoHost;
+  RefPtr<CDMWrapper> mCDM;
+  RefPtr<MediaByteBuffer> mExtraData;
+  RefPtr<MediaByteBuffer> mAnnexB;
+  GMPVideoDecoderCallback* mCallback;
+  std::map<uint64_t, uint64_t> mFrameDurations;
+  bool mSentInput;
+};
+
+} // namespace mozilla
+
+#endif // WidevineVideoDecoder_h_
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/widevine-adapter/WidevineVideoFrame.cpp
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WidevineVideoFrame.h"
+
+#include "WidevineUtils.h"
+
+using namespace cdm;
+
+namespace mozilla {
+
+WidevineVideoFrame::WidevineVideoFrame()
+  : mFormat(kUnknownVideoFormat)
+  , mSize(0,0)
+  , mBuffer(nullptr)
+  , mTimestamp(0)
+{
+  Log("WidevineVideoFrame::WidevineVideoFrame() this=%p", this);
+  memset(mPlaneOffsets, 0, sizeof(mPlaneOffsets));
+  memset(mPlaneStrides, 0, sizeof(mPlaneStrides));
+}
+
+WidevineVideoFrame::~WidevineVideoFrame()
+{
+  if (mBuffer) {
+    mBuffer->Destroy();
+    mBuffer = nullptr;
+  }
+}
+
+void
+WidevineVideoFrame::SetFormat(cdm::VideoFormat aFormat)
+{
+  Log("WidevineVideoFrame::SetFormat(%d) this=%p", aFormat, this);
+  mFormat = aFormat;
+}
+
+cdm::VideoFormat
+WidevineVideoFrame::Format() const
+{
+  return mFormat;
+}
+
+void
+WidevineVideoFrame::SetSize(cdm::Size aSize)
+{
+  Log("WidevineVideoFrame::SetSize(%d,%d) this=%p", aSize.width, aSize.height, this);
+  mSize.width = aSize.width;
+  mSize.height = aSize.height;
+}
+
+cdm::Size
+WidevineVideoFrame::Size() const
+{
+  return mSize;
+}
+
+void
+WidevineVideoFrame::SetFrameBuffer(cdm::Buffer* aFrameBuffer)
+{
+  Log("WidevineVideoFrame::SetFrameBuffer(%p) this=%p", aFrameBuffer, this);
+  MOZ_ASSERT(!mBuffer);
+  mBuffer = aFrameBuffer;
+}
+
+cdm::Buffer*
+WidevineVideoFrame::FrameBuffer()
+{
+  return mBuffer;
+}
+
+void
+WidevineVideoFrame::SetPlaneOffset(cdm::VideoFrame::VideoPlane aPlane, uint32_t aOffset)
+{
+  Log("WidevineVideoFrame::SetPlaneOffset(%d, %d) this=%p", aPlane, aOffset, this);
+  mPlaneOffsets[aPlane] = aOffset;
+}
+
+uint32_t
+WidevineVideoFrame::PlaneOffset(cdm::VideoFrame::VideoPlane aPlane)
+{
+  return mPlaneOffsets[aPlane];
+}
+
+void
+WidevineVideoFrame::SetStride(cdm::VideoFrame::VideoPlane aPlane, uint32_t aStride)
+{
+  Log("WidevineVideoFrame::SetStride(%d, %d) this=%p", aPlane, aStride, this);
+  mPlaneStrides[aPlane] = aStride;
+}
+
+uint32_t
+WidevineVideoFrame::Stride(cdm::VideoFrame::VideoPlane aPlane)
+{
+  return mPlaneStrides[aPlane];
+}
+
+void
+WidevineVideoFrame::SetTimestamp(int64_t timestamp)
+{
+  Log("WidevineVideoFrame::SetTimestamp(%lld) this=%p", timestamp, this);
+  mTimestamp = timestamp;
+}
+
+int64_t
+WidevineVideoFrame::Timestamp() const
+{
+  return mTimestamp;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/widevine-adapter/WidevineVideoFrame.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WidevineVideoFrame_h_
+#define WidevineVideoFrame_h_
+
+#include "stddef.h"
+#include "content_decryption_module.h"
+#include <vector>
+
+namespace mozilla {
+
+class WidevineVideoFrame : public cdm::VideoFrame {
+public:
+  WidevineVideoFrame();
+  ~WidevineVideoFrame();
+
+  void SetFormat(cdm::VideoFormat aFormat) override;
+  cdm::VideoFormat Format() const override;
+
+  void SetSize(cdm::Size aSize) override;
+  cdm::Size Size() const override;
+
+  void SetFrameBuffer(cdm::Buffer* aFrameBuffer) override;
+  cdm::Buffer* FrameBuffer() override;
+
+  void SetPlaneOffset(cdm::VideoFrame::VideoPlane aPlane, uint32_t aOffset) override;
+  uint32_t PlaneOffset(cdm::VideoFrame::VideoPlane aPlane) override;
+
+  void SetStride(cdm::VideoFrame::VideoPlane aPlane, uint32_t aStride) override;
+  uint32_t Stride(cdm::VideoFrame::VideoPlane aPlane) override;
+
+  void SetTimestamp(int64_t aTimestamp) override;
+  int64_t Timestamp() const override;
+
+protected:
+  cdm::VideoFormat mFormat;
+  cdm::Size mSize;
+  cdm::Buffer* mBuffer;
+  uint32_t mPlaneOffsets[kMaxPlanes];
+  uint32_t mPlaneStrides[kMaxPlanes];
+  int64_t mTimestamp;
+};
+
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/widevine-adapter/content_decryption_module.h
@@ -0,0 +1,1199 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CDM_CONTENT_DECRYPTION_MODULE_H_
+#define CDM_CONTENT_DECRYPTION_MODULE_H_
+
+#if defined(_MSC_VER)
+typedef unsigned char uint8_t;
+typedef unsigned int uint32_t;
+typedef int int32_t;
+typedef __int64 int64_t;
+#else
+#include <stdint.h>
+#endif
+
+// Define CDM_EXPORT so that functionality implemented by the CDM module
+// can be exported to consumers.
+#if defined(WIN32)
+
+#if defined(CDM_IMPLEMENTATION)
+#define CDM_EXPORT __declspec(dllexport)
+#else
+#define CDM_EXPORT __declspec(dllimport)
+#endif  // defined(CDM_IMPLEMENTATION)
+
+#else  // defined(WIN32)
+
+#if defined(CDM_IMPLEMENTATION)
+#define CDM_EXPORT __attribute__((visibility("default")))
+#else
+#define CDM_EXPORT
+#endif
+
+#endif  // defined(WIN32)
+
+// The version number must be rolled when the exported functions are updated!
+// If the CDM and the adapter use different versions of these functions, the
+// adapter will fail to load or crash!
+#define CDM_MODULE_VERSION 4
+
+// Build the versioned entrypoint name.
+// The extra macros are necessary to expand version to an actual value.
+#define INITIALIZE_CDM_MODULE \
+  BUILD_ENTRYPOINT(InitializeCdmModule, CDM_MODULE_VERSION)
+#define BUILD_ENTRYPOINT(name, version) \
+  BUILD_ENTRYPOINT_NO_EXPANSION(name, version)
+#define BUILD_ENTRYPOINT_NO_EXPANSION(name, version) name##_##version
+
+extern "C" {
+CDM_EXPORT void INITIALIZE_CDM_MODULE();
+
+CDM_EXPORT void DeinitializeCdmModule();
+
+// Returns a pointer to the requested CDM Host interface upon success.
+// Returns NULL if the requested CDM Host interface is not supported.
+// The caller should cast the returned pointer to the type matching
+// |host_interface_version|.
+typedef void* (*GetCdmHostFunc)(int host_interface_version, void* user_data);
+
+// Returns a pointer to the requested CDM upon success.
+// Returns NULL if an error occurs or the requested |cdm_interface_version| or
+// |key_system| is not supported or another error occurs.
+// The caller should cast the returned pointer to the type matching
+// |cdm_interface_version|.
+// Caller retains ownership of arguments and must call Destroy() on the returned
+// object.
+CDM_EXPORT void* CreateCdmInstance(
+    int cdm_interface_version,
+    const char* key_system, uint32_t key_system_size,
+    GetCdmHostFunc get_cdm_host_func, void* user_data);
+
+CDM_EXPORT const char* GetCdmVersion();
+}
+
+namespace cdm {
+
+class AudioFrames;
+class DecryptedBlock;
+class VideoFrame;
+
+class Host_7;
+class Host_8;
+
+enum Status {
+  kSuccess = 0,
+  kNeedMoreData,  // Decoder needs more data to produce a decoded frame/sample.
+  kNoKey,  // The required decryption key is not available.
+  kSessionError,  // Session management error.
+  kDecryptError,  // Decryption failed.
+  kDecodeError,  // Error decoding audio or video.
+  kDeferredInitialization  // Decoder is not ready for initialization.
+};
+
+// This must at least contain the exceptions defined in the spec:
+// https://w3c.github.io/encrypted-media/#exceptions
+// The following starts with the list of DOM4 exceptions from:
+// http://www.w3.org/TR/dom/#domexception
+// Some DOM4 exceptions are not included as they are not expected to be used.
+enum Error {
+  kNotSupportedError = 9,
+  kInvalidStateError = 11,
+  kInvalidAccessError = 15,
+  kQuotaExceededError = 22,
+
+  // Additional exceptions that do not have assigned codes.
+  // There are other non-EME-specific values, not included in this list.
+  kUnknownError = 30,
+
+  // Additional values from previous EME versions. They currently have no
+  // matching DOMException.
+  kClientError = 100,
+  kOutputError = 101
+};
+
+// Time is defined as the number of seconds since the
+// Epoch (00:00:00 UTC, January 1, 1970).
+typedef double Time;
+
+// An input buffer can be split into several continuous subsamples.
+// A SubsampleEntry specifies the number of clear and cipher bytes in each
+// subsample. For example, the following buffer has three subsamples:
+//
+// |<----- subsample1 ----->|<----- subsample2 ----->|<----- subsample3 ----->|
+// |   clear1   |  cipher1  |  clear2  |   cipher2   | clear3 |    cipher3    |
+//
+// For decryption, all of the cipher bytes in a buffer should be concatenated
+// (in the subsample order) into a single logical stream. The clear bytes should
+// not be considered as part of decryption.
+//
+// Stream to decrypt:   |  cipher1  |   cipher2   |    cipher3    |
+// Decrypted stream:    | decrypted1|  decrypted2 |   decrypted3  |
+//
+// After decryption, the decrypted bytes should be copied over the position
+// of the corresponding cipher bytes in the original buffer to form the output
+// buffer. Following the above example, the decrypted buffer should be:
+//
+// |<----- subsample1 ----->|<----- subsample2 ----->|<----- subsample3 ----->|
+// |   clear1   | decrypted1|  clear2  |  decrypted2 | clear3 |   decrypted3  |
+//
+struct SubsampleEntry {
+  SubsampleEntry(uint32_t clear_bytes, uint32_t cipher_bytes)
+      : clear_bytes(clear_bytes), cipher_bytes(cipher_bytes) {}
+
+  uint32_t clear_bytes;
+  uint32_t cipher_bytes;
+};
+
+// Represents an input buffer to be decrypted (and possibly decoded). It does
+// not own any pointers in this struct. If |iv_size| = 0, the data is
+// unencrypted.
+struct InputBuffer {
+  InputBuffer()
+      : data(NULL),
+        data_size(0),
+        key_id(NULL),
+        key_id_size(0),
+        iv(NULL),
+        iv_size(0),
+        subsamples(NULL),
+        num_subsamples(0),
+        timestamp(0) {}
+
+  const uint8_t* data;  // Pointer to the beginning of the input data.
+  uint32_t data_size;  // Size (in bytes) of |data|.
+
+  const uint8_t* key_id;  // Key ID to identify the decryption key.
+  uint32_t key_id_size;  // Size (in bytes) of |key_id|.
+
+  const uint8_t* iv;  // Initialization vector.
+  uint32_t iv_size;  // Size (in bytes) of |iv|.
+
+  const struct SubsampleEntry* subsamples;
+  uint32_t num_subsamples;  // Number of subsamples in |subsamples|.
+
+  int64_t timestamp;  // Presentation timestamp in microseconds.
+};
+
+struct AudioDecoderConfig {
+  enum AudioCodec {
+    kUnknownAudioCodec = 0,
+    kCodecVorbis,
+    kCodecAac
+  };
+
+  AudioDecoderConfig()
+      : codec(kUnknownAudioCodec),
+        channel_count(0),
+        bits_per_channel(0),
+        samples_per_second(0),
+        extra_data(NULL),
+        extra_data_size(0) {}
+
+  AudioCodec codec;
+  int32_t channel_count;
+  int32_t bits_per_channel;
+  int32_t samples_per_second;
+
+  // Optional byte data required to initialize audio decoders, such as the
+  // vorbis setup header.
+  uint8_t* extra_data;
+  uint32_t extra_data_size;
+};
+
+// Supported sample formats for AudioFrames.
+enum AudioFormat {
+  kUnknownAudioFormat = 0,  // Unknown format value. Used for error reporting.
+  kAudioFormatU8,  // Interleaved unsigned 8-bit w/ bias of 128.
+  kAudioFormatS16,  // Interleaved signed 16-bit.
+  kAudioFormatS32,  // Interleaved signed 32-bit.
+  kAudioFormatF32,  // Interleaved float 32-bit.
+  kAudioFormatPlanarS16,  // Signed 16-bit planar.
+  kAudioFormatPlanarF32,  // Float 32-bit planar.
+};
+
+// Surface formats based on FOURCC labels, see: http://www.fourcc.org/yuv.php
+enum VideoFormat {
+  kUnknownVideoFormat = 0,  // Unknown format value. Used for error reporting.
+  kYv12,  // 12bpp YVU planar 1x1 Y, 2x2 VU samples.
+  kI420  // 12bpp YVU planar 1x1 Y, 2x2 UV samples.
+};
+
+struct Size {
+  Size() : width(0), height(0) {}
+  Size(int32_t width, int32_t height) : width(width), height(height) {}
+
+  int32_t width;
+  int32_t height;
+};
+
+struct VideoDecoderConfig {
+  enum VideoCodec {
+    kUnknownVideoCodec = 0,
+    kCodecVp8,
+    kCodecH264,
+    kCodecVp9
+  };
+
+  enum VideoCodecProfile {
+    kUnknownVideoCodecProfile = 0,
+    kProfileNotNeeded,
+    kH264ProfileBaseline,
+    kH264ProfileMain,
+    kH264ProfileExtended,
+    kH264ProfileHigh,
+    kH264ProfileHigh10,
+    kH264ProfileHigh422,
+    kH264ProfileHigh444Predictive
+  };
+
+  VideoDecoderConfig()
+      : codec(kUnknownVideoCodec),
+        profile(kUnknownVideoCodecProfile),
+        format(kUnknownVideoFormat),
+        extra_data(NULL),
+        extra_data_size(0) {}
+
+  VideoCodec codec;
+  VideoCodecProfile profile;
+  VideoFormat format;
+
+  // Width and height of video frame immediately post-decode. Not all pixels
+  // in this region are valid.
+  Size coded_size;
+
+  // Optional byte data required to initialize video decoders, such as H.264
+  // AAVC data.
+  uint8_t* extra_data;
+  uint32_t extra_data_size;
+};
+
+enum StreamType {
+  kStreamTypeAudio = 0,
+  kStreamTypeVideo = 1
+};
+
+// Structure provided to ContentDecryptionModule::OnPlatformChallengeResponse()
+// after a platform challenge was initiated via Host::SendPlatformChallenge().
+// All values will be NULL / zero in the event of a challenge failure.
+struct PlatformChallengeResponse {
+  // |challenge| provided during Host::SendPlatformChallenge() combined with
+  // nonce data and signed with the platform's private key.
+  const uint8_t* signed_data;
+  uint32_t signed_data_length;
+
+  // RSASSA-PKCS1-v1_5-SHA256 signature of the |signed_data| block.
+  const uint8_t* signed_data_signature;
+  uint32_t signed_data_signature_length;
+
+  // X.509 device specific certificate for the |service_id| requested.
+  const uint8_t* platform_key_certificate;
+  uint32_t platform_key_certificate_length;
+};
+
+// Used when passing arrays of binary data. Does not own the referenced data.
+struct BinaryData {
+  BinaryData() : data(NULL), length(0) {}
+  const uint8_t* data;
+  uint32_t length;
+};
+
+// The current status of the associated key. The valid types are defined in the
+// spec: https://w3c.github.io/encrypted-media/#idl-def-MediaKeyStatus
+enum KeyStatus {
+  kUsable = 0,
+  kInternalError = 1,
+  kExpired = 2,
+  kOutputRestricted = 3,
+  kOutputDownscaled = 4,
+  kStatusPending = 5,
+  kReleased = 6
+};
+
+// Used when passing arrays of key information. Does not own the referenced
+// data. |system_code| is an additional error code for unusable keys and
+// should be 0 when |status| == kUsable.
+struct KeyInformation {
+  KeyInformation()
+      : key_id(NULL), key_id_size(0), status(kInternalError), system_code(0) {}
+  const uint8_t* key_id;
+  uint32_t key_id_size;
+  KeyStatus status;
+  uint32_t system_code;
+};
+
+// Supported output protection methods for use with EnableOutputProtection() and
+// returned by OnQueryOutputProtectionStatus().
+enum OutputProtectionMethods {
+  kProtectionNone = 0,
+  kProtectionHDCP = 1 << 0
+};
+
+// Connected output link types returned by OnQueryOutputProtectionStatus().
+enum OutputLinkTypes {
+  kLinkTypeNone = 0,
+  kLinkTypeUnknown = 1 << 0,
+  kLinkTypeInternal = 1 << 1,
+  kLinkTypeVGA = 1 << 2,
+  kLinkTypeHDMI = 1 << 3,
+  kLinkTypeDVI = 1 << 4,
+  kLinkTypeDisplayPort = 1 << 5,
+  kLinkTypeNetwork = 1 << 6
+};
+
+// Result of the QueryOutputProtectionStatus() call.
+enum QueryResult {
+  kQuerySucceeded = 0,
+  kQueryFailed
+};
+
+// The Initialization Data Type. The valid types are defined in the spec:
+// http://w3c.github.io/encrypted-media/initdata-format-registry.html#registry
+enum InitDataType {
+  kCenc = 0,
+  kKeyIds = 1,
+  kWebM = 2
+};
+
+// The type of session to create. The valid types are defined in the spec:
+// https://w3c.github.io/encrypted-media/#idl-def-SessionType
+enum SessionType {
+  kTemporary = 0,
+  kPersistentLicense = 1,
+  kPersistentKeyRelease = 2
+};
+
+// The type of the message event.  The valid types are defined in the spec:
+// https://w3c.github.io/encrypted-media/#idl-def-MediaKeyMessageType
+enum MessageType {
+  kLicenseRequest = 0,
+  kLicenseRenewal = 1,
+  kLicenseRelease = 2
+};
+
+// FileIO interface provides a way for the CDM to store data in a file in
+// persistent storage. This interface aims only at providing basic read/write
+// capabilities and should not be used as a full fledged file IO API.
+// Each CDM and origin (e.g. HTTPS, "foo.example.com", 443) combination has
+// its own persistent storage. All instances of a given CDM associated with a
+// given origin share the same persistent storage.
+// Note to implementors of this interface:
+// Per-origin storage and the ability for users to clear it are important.
+// See http://www.w3.org/TR/encrypted-media/#privacy-storedinfo.
+class FileIO {
+ public:
+  // Opens the file with |file_name| for read and write.
+  // FileIOClient::OnOpenComplete() will be called after the opening
+  // operation finishes.
+  // - When the file is opened by a CDM instance, it will be classified as "in
+  //   use". In this case other CDM instances in the same domain may receive
+  //   kInUse status when trying to open it.
+  // - |file_name| must not contain forward slash ('/') or backslash ('\'), and
+  //   must not start with an underscore ('_').
+  virtual void Open(const char* file_name, uint32_t file_name_size) = 0;
+
+  // Reads the contents of the file. FileIOClient::OnReadComplete() will be
+  // called with the read status. Read() should not be called if a previous
+  // Read() or Write() call is still pending; otherwise OnReadComplete() will
+  // be called with kInUse.
+  virtual void Read() = 0;
+
+  // Writes |data_size| bytes of |data| into the file.
+  // FileIOClient::OnWriteComplete() will be called with the write status.
+  // All existing contents in the file will be overwritten. Calling Write() with
+  // NULL |data| will clear all contents in the file. Write() should not be
+  // called if a previous Write() or Read() call is still pending; otherwise
+  // OnWriteComplete() will be called with kInUse.
+  virtual void Write(const uint8_t* data, uint32_t data_size) = 0;
+
+  // Closes the file if opened, destroys this FileIO object and releases any
+  // resources allocated. The CDM must call this method when it finished using
+  // this object. A FileIO object must not be used after Close() is called.
+  virtual void Close() = 0;
+
+ protected:
+  FileIO() {}
+  virtual ~FileIO() {}
+};
+
+// Responses to FileIO calls. All responses will be called asynchronously.
+// When kError is returned, the FileIO object could be in an error state. All
+// following calls (other than Close()) could return kError. The CDM should
+// still call Close() to destroy the FileIO object.
+class FileIOClient {
+ public:
+  enum Status {
+    kSuccess = 0,
+    kInUse,
+    kError
+  };
+
+  // Response to a FileIO::Open() call with the open |status|.
+  virtual void OnOpenComplete(Status status) = 0;
+
+  // Response to a FileIO::Read() call to provide |data_size| bytes of |data|
+  // read from the file.
+  // - kSuccess indicates that all contents of the file has been successfully
+  //   read. In this case, 0 |data_size| means that the file is empty.
+  // - kInUse indicates that there are other read/write operations pending.
+  // - kError indicates read failure, e.g. the storage is not open or cannot be
+  //   fully read.
+  virtual void OnReadComplete(Status status,
+                              const uint8_t* data, uint32_t data_size) = 0;
+
+  // Response to a FileIO::Write() call.
+  // - kSuccess indicates that all the data has been written into the file
+  //   successfully.
+  // - kInUse indicates that there are other read/write operations pending.
+  // - kError indicates write failure, e.g. the storage is not open or cannot be
+  //   fully written. Upon write failure, the contents of the file should be
+  //   regarded as corrupt and should not used.
+  virtual void OnWriteComplete(Status status) = 0;
+
+ protected:
+  FileIOClient() {}
+  virtual ~FileIOClient() {}
+};
+
+// ContentDecryptionModule interface that all CDMs need to implement.
+// The interface is versioned for backward compatibility.
+// Note: ContentDecryptionModule implementations must use the allocator
+// provided in CreateCdmInstance() to allocate any Buffer that needs to
+// be passed back to the caller. Implementations must call Buffer::Destroy()
+// when a Buffer is created that will never be returned to the caller.
+class ContentDecryptionModule_7 {
+ public:
+  static const int kVersion = 7;
+  typedef Host_7 Host;
+
+  // SetServerCertificate(), CreateSessionAndGenerateRequest(), LoadSession(),
+  // UpdateSession(), CloseSession(), and RemoveSession() all accept a
+  // |promise_id|, which must be passed to the completion Host method
+  // (e.g. Host::OnResolveNewSessionPromise()).
+
+  // Provides a server certificate to be used to encrypt messages to the
+  // license server. The CDM must respond by calling either
+  // Host::OnResolvePromise() or Host::OnRejectPromise().
+  virtual void SetServerCertificate(uint32_t promise_id,
+                                    const uint8_t* server_certificate_data,
+                                    uint32_t server_certificate_data_size) = 0;
+
+  // Creates a session given |session_type|, |init_data_type|, and |init_data|.
+  // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
+  // or Host::OnRejectPromise().
+  virtual void CreateSessionAndGenerateRequest(uint32_t promise_id,
+                                               SessionType session_type,
+                                               const char* init_data_type,
+                                               uint32_t init_data_type_size,
+                                               const uint8_t* init_data,
+                                               uint32_t init_data_size) = 0;
+
+  // Loads the session of type |session_type| specified by |session_id|.
+  // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
+  // or Host::OnRejectPromise(). If the session is not found, call
+  // Host::OnResolveNewSessionPromise() with session_id = NULL.
+  virtual void LoadSession(uint32_t promise_id,
+                           SessionType session_type,
+                           const char* session_id,
+                           uint32_t session_id_size) = 0;
+
+  // Updates the session with |response|. The CDM must respond by calling
+  // either Host::OnResolvePromise() or Host::OnRejectPromise().
+  virtual void UpdateSession(uint32_t promise_id,
+                             const char* session_id,
+                             uint32_t session_id_size,
+                             const uint8_t* response,
+                             uint32_t response_size) = 0;
+
+  // Requests that the CDM close the session. The CDM must respond by calling
+  // either Host::OnResolvePromise() or Host::OnRejectPromise() when the request
+  // has been processed. This may be before the session is closed. Once the
+  // session is closed, Host::OnSessionClosed() must also be called.
+  virtual void CloseSession(uint32_t promise_id,
+                            const char* session_id,
+                            uint32_t session_id_size) = 0;
+
+  // Removes any stored session data associated with this session. Will only be
+  // called for persistent sessions. The CDM must respond by calling either
+  // Host::OnResolvePromise() or Host::OnRejectPromise() when the request has
+  // been processed.
+  virtual void RemoveSession(uint32_t promise_id,
+                             const char* session_id,
+                             uint32_t session_id_size) = 0;
+
+  // Performs scheduled operation with |context| when the timer fires.
+  virtual void TimerExpired(void* context) = 0;
+
+  // Decrypts the |encrypted_buffer|.
+  //
+  // Returns kSuccess if decryption succeeded, in which case the callee
+  // should have filled the |decrypted_buffer| and passed the ownership of
+  // |data| in |decrypted_buffer| to the caller.
+  // Returns kNoKey if the CDM did not have the necessary decryption key
+  // to decrypt.
+  // Returns kDecryptError if any other error happened.
+  // If the return value is not kSuccess, |decrypted_buffer| should be ignored
+  // by the caller.
+  virtual Status Decrypt(const InputBuffer& encrypted_buffer,
+                         DecryptedBlock* decrypted_buffer) = 0;
+
+  // Initializes the CDM audio decoder with |audio_decoder_config|. This
+  // function must be called before DecryptAndDecodeSamples() is called.
+  //
+  // Returns kSuccess if the |audio_decoder_config| is supported and the CDM
+  // audio decoder is successfully initialized.
+  // Returns kSessionError if |audio_decoder_config| is not supported. The CDM
+  // may still be able to do Decrypt().
+  // Returns kDeferredInitialization if the CDM is not ready to initialize the
+  // decoder at this time. Must call Host::OnDeferredInitializationDone() once
+  // initialization is complete.
+  virtual Status InitializeAudioDecoder(
+      const AudioDecoderConfig& audio_decoder_config) = 0;
+
+  // Initializes the CDM video decoder with |video_decoder_config|. This
+  // function must be called before DecryptAndDecodeFrame() is called.
+  //
+  // Returns kSuccess if the |video_decoder_config| is supported and the CDM
+  // video decoder is successfully initialized.
+  // Returns kSessionError if |video_decoder_config| is not supported. The CDM
+  // may still be able to do Decrypt().
+  // Returns kDeferredInitialization if the CDM is not ready to initialize the
+  // decoder at this time. Must call Host::OnDeferredInitializationDone() once
+  // initialization is complete.
+  virtual Status InitializeVideoDecoder(
+      const VideoDecoderConfig& video_decoder_config) = 0;
+
+  // De-initializes the CDM decoder and sets it to an uninitialized state. The
+  // caller can initialize the decoder again after this call to re-initialize
+  // it. This can be used to reconfigure the decoder if the configuration
+  // changes.
+  virtual void DeinitializeDecoder(StreamType decoder_type) = 0;
+
+  // Resets the CDM decoder to an initialized clean state. All internal buffers
+  // MUST be flushed.
+  virtual void ResetDecoder(StreamType decoder_type) = 0;
+
+  // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into a
+  // |video_frame|. Upon end-of-stream, the caller should call this function
+  // repeatedly with empty |encrypted_buffer| (|data| == NULL) until only empty
+  // |video_frame| (|format| == kEmptyVideoFrame) is produced.
+  //
+  // Returns kSuccess if decryption and decoding both succeeded, in which case
+  // the callee will have filled the |video_frame| and passed the ownership of
+  // |frame_buffer| in |video_frame| to the caller.
+  // Returns kNoKey if the CDM did not have the necessary decryption key
+  // to decrypt.
+  // Returns kNeedMoreData if more data was needed by the decoder to generate
+  // a decoded frame (e.g. during initialization and end-of-stream).
+  // Returns kDecryptError if any decryption error happened.
+  // Returns kDecodeError if any decoding error happened.
+  // If the return value is not kSuccess, |video_frame| should be ignored by
+  // the caller.
+  virtual Status DecryptAndDecodeFrame(const InputBuffer& encrypted_buffer,
+                                       VideoFrame* video_frame) = 0;
+
+  // Decrypts the |encrypted_buffer| and decodes the decrypted buffer into
+  // |audio_frames|. Upon end-of-stream, the caller should call this function
+  // repeatedly with empty |encrypted_buffer| (|data| == NULL) until only empty
+  // |audio_frames| is produced.
+  //
+  // Returns kSuccess if decryption and decoding both succeeded, in which case
+  // the callee will have filled |audio_frames| and passed the ownership of
+  // |data| in |audio_frames| to the caller.
+  // Returns kNoKey if the CDM did not have the necessary decryption key
+  // to decrypt.
+  // Returns kNeedMoreData if more data was needed by the decoder to generate
+  // audio samples (e.g. during initialization and end-of-stream).
+  // Returns kDecryptError if any decryption error happened.
+  // Returns kDecodeError if any decoding error happened.
+  // If the return value is not kSuccess, |audio_frames| should be ignored by
+  // the caller.
+  virtual Status DecryptAndDecodeSamples(const InputBuffer& encrypted_buffer,
+                                         AudioFrames* audio_frames) = 0;
+
+  // Called by the host after a platform challenge was initiated via
+  // Host::SendPlatformChallenge().
+  virtual void OnPlatformChallengeResponse(
+      const PlatformChallengeResponse& response) = 0;
+
+  // Called by the host after a call to Host::QueryOutputProtectionStatus(). The
+  // |link_mask| is a bit mask of OutputLinkTypes and |output_protection_mask|
+  // is a bit mask of OutputProtectionMethods. If |result| is kQueryFailed,
+  // then |link_mask| and |output_protection_mask| are undefined and should
+  // be ignored.
+  virtual void OnQueryOutputProtectionStatus(
+      QueryResult result,
+      uint32_t link_mask,
+      uint32_t output_protection_mask) = 0;
+
+  // Destroys the object in the same context as it was created.
+  virtual void Destroy() = 0;
+
+ protected:
+  ContentDecryptionModule_7() {}
+  virtual ~ContentDecryptionModule_7() {}
+};
+
+// ContentDecryptionModule interface that all CDMs need to implement.
+// The interface is versioned for backward compatibility.
+// Note: ContentDecryptionModule implementations must use the allocator
+// provided in CreateCdmInstance() to allocate any Buffer that needs to
+// be passed back to the caller. Implementations must call Buffer::Destroy()
+// when a Buffer is created that will never be returned to the caller.
+class ContentDecryptionModule_8 {
+ public:
+  static const int kVersion = 8;
+  typedef Host_8 Host;
+
+  // Initializes the CDM instance, providing information about permitted
+  // functionalities.
+  // If |allow_distinctive_identifier| is false, messages from the CDM,
+  // such as message events, must not contain a Distinctive Identifier,
+  // even in an encrypted form.
+  // If |allow_persistent_state| is false, the CDM must not attempt to
+  // persist state. Calls to CreateFileIO() will fail.
+  virtual void Initialize(bool allow_distinctive_identifier,
+                          bool allow_persistent_state) = 0;
+
+  // SetServerCertificate(), CreateSessionAndGenerateRequest(), LoadSession(),
+  // UpdateSession(), CloseSession(), and RemoveSession() all accept a
+  // |promise_id|, which must be passed to the completion Host method
+  // (e.g. Host::OnResolveNewSessionPromise()).
+
+  // Provides a server certificate to be used to encrypt messages to the
+  // license server. The CDM must respond by calling either
+  // Host::OnResolvePromise() or Host::OnRejectPromise().
+  virtual void SetServerCertificate(uint32_t promise_id,
+                                    const uint8_t* server_certificate_data,
+                                    uint32_t server_certificate_data_size) = 0;
+
+  // Creates a session given |session_type|, |init_data_type|, and |init_data|.
+  // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
+  // or Host::OnRejectPromise().
+  virtual void CreateSessionAndGenerateRequest(uint32_t promise_id,
+                                               SessionType session_type,
+                                               InitDataType init_data_type,
+                                               const uint8_t* init_data,
+                                               uint32_t init_data_size) = 0;
+
+  // Loads the session of type |session_type| specified by |session_id|.
+  // The CDM must respond by calling either Host::OnResolveNewSessionPromise()
+  // or Host::OnRejectPromise(). If the session is not found, call
+  // Host::OnResolveNewSessionPromise() with session_id = NULL.
+  virtual void LoadSession(uint32_t promise_id,
+                           SessionType session_type,
+                           const char* session_id,
+                           uint32_t session_id_size) = 0;
+
+  // Updates the session with |response|. The CDM must respond by calling
+  // either Host::OnResolvePromise() or Host::OnRejectPromise().
+  virtual void UpdateSession(uint32_t promise_id,
+                             const char* session_id,
+                             uint32_t session_id_size,
+                             const uint8_t* response,
+                             uint32_t response_size) = 0;
+
+  // Requests that the CDM close the session. The CDM must respond by calling
+  // either Host::OnResolvePromise() or Host::OnRejectPromise() when the request
+  // has been processed. This may be before the session is closed. Once the
+  // session is closed, Host::OnS