Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 12 Apr 2016 13:55:15 +0200
changeset 330740 c6e90a3857c019e418c113f31dec86ca43e60243
parent 330739 80fa4e428fb2d5e2e40040d4795d97b0b21d6d6b (current diff)
parent 330652 49d7fb650c9dde7cf6e4b2c7aa578a4a11e83f83 (diff)
child 330741 4cab5471b2fa42dd7d0560f8f35dcc4e82a9414e
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)
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to fx-team
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
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::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