Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2016-04-12 13:55:15 +02:00
Родитель 353d6b0d9a 128f40b9a8
Коммит bf1832d88a
1251 изменённых файлов: 87720 добавлений и 10376 удалений

Просмотреть файл

@ -2,35 +2,21 @@
* 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 "EmbeddedObjCollector.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()
EmbeddedObjCollector::Count()
{
EnsureNGetIndex(nullptr);
return mObjects.Length();
}
Accessible*
AccCollector::GetAccessibleAt(uint32_t aIndex)
EmbeddedObjCollector::GetAccessibleAt(uint32_t aIndex)
{
Accessible* accessible = mObjects.SafeElementAt(aIndex, nullptr);
if (accessible)
@ -39,26 +25,13 @@ AccCollector::GetAccessibleAt(uint32_t aIndex)
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)
EmbeddedObjCollector::EnsureNGetObject(uint32_t aIndex)
{
uint32_t childCount = mRoot->ChildCount();
while (mRootChildIdx < childCount) {
Accessible* child = mRoot->GetChildAt(mRootChildIdx++);
if (!(mFilterFunc(child) & filters::eMatch))
if (child->IsText())
continue;
AppendObject(child);
@ -70,12 +43,12 @@ AccCollector::EnsureNGetObject(uint32_t aIndex)
}
int32_t
AccCollector::EnsureNGetIndex(Accessible* aAccessible)
EmbeddedObjCollector::EnsureNGetIndex(Accessible* aAccessible)
{
uint32_t childCount = mRoot->ChildCount();
while (mRootChildIdx < childCount) {
Accessible* child = mRoot->GetChildAt(mRootChildIdx++);
if (!(mFilterFunc(child) & filters::eMatch))
if (child->IsText())
continue;
AppendObject(child);
@ -86,16 +59,6 @@ AccCollector::EnsureNGetIndex(Accessible* aAccessible)
return -1;
}
void
AccCollector::AppendObject(Accessible* aAccessible)
{
mObjects.AppendElement(aAccessible);
}
////////////////////////////////////////////////////////////////////////////////
// EmbeddedObjCollector
////////////////////////////////////////////////////////////////////////////////
int32_t
EmbeddedObjCollector::GetIndexAt(Accessible* aAccessible)
{
@ -106,8 +69,7 @@ EmbeddedObjCollector::GetIndexAt(Accessible* aAccessible)
if (aAccessible->mInt.mIndexOfEmbeddedChild != -1)
return aAccessible->mInt.mIndexOfEmbeddedChild;
return mFilterFunc(aAccessible) & filters::eMatch ?
EnsureNGetIndex(aAccessible) : -1;
return !aAccessible->IsText() ? EnsureNGetIndex(aAccessible) : -1;
}
void

Просмотреть файл

@ -2,10 +2,8 @@
* 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"
#ifndef mozilla_a11y_EmbeddedObjCollector_h__
#define mozilla_a11y_EmbeddedObjCollector_h__
#include "nsTArray.h"
@ -15,14 +13,18 @@ namespace a11y {
class Accessible;
/**
* Collect accessible children complying with filter function. Provides quick
* access to accessible by index.
* Collect embedded objects. Provide quick access to accessible by index and
* vice versa.
*/
class AccCollector
class EmbeddedObjCollector final
{
public:
AccCollector(Accessible* aRoot, filters::FilterFuncPtr aFilterFunc);
virtual ~AccCollector();
~EmbeddedObjCollector() { }
/**
* Return index of the given accessible within the collection.
*/
int32_t GetIndexAt(Accessible* aAccessible);
/**
* Return accessible count within the collection.
@ -34,11 +36,6 @@ public:
*/
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.
@ -50,43 +47,20 @@ protected:
*/
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.
*/
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;
void AppendObject(Accessible* aAccessible);
friend class Accessible;
Accessible* mRoot;
uint32_t mRootChildIdx;
nsTArray<Accessible*> mObjects;
};
} // namespace a11y

Просмотреть файл

@ -49,9 +49,3 @@ filters::GetCell(Accessible* aAccessible)
{
return aAccessible->IsTableCell() ? eMatch : eSkipSubtree;
}
uint32_t
filters::GetEmbeddedObject(Accessible* aAccessible)
{
return aAccessible->IsText() ? eSkipSubtree : eMatch | eSkipSubtree;
}

Просмотреть файл

@ -43,12 +43,6 @@ 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

Просмотреть файл

@ -828,7 +828,7 @@ 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()) {

Просмотреть файл

@ -26,7 +26,6 @@ if CONFIG['MOZ_DEBUG']:
]
UNIFIED_SOURCES += [
'AccCollector.cpp',
'AccEvent.cpp',
'AccGroupInfo.cpp',
'AccIterator.cpp',
@ -34,6 +33,7 @@ UNIFIED_SOURCES += [
'ARIAStateMap.cpp',
'Asserts.cpp',
'DocManager.cpp',
'EmbeddedObjCollector.cpp',
'EventQueue.cpp',
'EventTree.cpp',
'Filters.cpp',

Просмотреть файл

@ -901,7 +901,7 @@ RuleCache::ApplyFilter(Accessible* aAccessible, uint16_t* aResult)
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;
}

Просмотреть файл

@ -7,7 +7,7 @@
#include "nsIXBLAccessible.h"
#include "AccCollector.h"
#include "EmbeddedObjCollector.h"
#include "AccGroupInfo.h"
#include "AccIterator.h"
#include "nsAccUtils.h"
@ -1205,8 +1205,7 @@ Accessible::State()
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;
}

Просмотреть файл

@ -1059,6 +1059,9 @@ pref("layout.accessiblecaret.bar.enabled", true);
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);

Просмотреть файл

@ -1549,11 +1549,18 @@ pref("media.gmp.decoder.h264", 2);
// 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)

Просмотреть файл

@ -2311,28 +2311,6 @@
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()
@ -2350,6 +2328,35 @@
}
}
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

Просмотреть файл

@ -438,6 +438,7 @@ run-if = e10s
[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]

Просмотреть файл

@ -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;
});

Просмотреть файл

@ -19,5 +19,10 @@ 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

Просмотреть файл

@ -32,10 +32,10 @@ export MOZ_TELEMETRY_REPORTING=1
# 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

Просмотреть файл

@ -30,10 +30,10 @@ 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

Просмотреть файл

@ -510,6 +510,10 @@ a {
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;

Просмотреть файл

@ -1,5 +1,5 @@
/* -*- 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/. */
@ -1341,8 +1341,10 @@ Messages.Extended.prototype = Heritage.extend(Messages.Simple.prototype,
* 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;
@ -1365,7 +1367,13 @@ Messages.JavaScriptEvalOutput = function(evalResponse, errorMessage)
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;

Просмотреть файл

@ -1,5 +1,5 @@
/* -*- 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/. */
@ -308,6 +308,22 @@ JSTerm.prototype = {
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") {
@ -356,7 +372,7 @@ JSTerm.prototype = {
return;
}
let msg = new Messages.JavaScriptEvalOutput(response, errorMessage);
let msg = new Messages.JavaScriptEvalOutput(response, errorMessage, errorDocLink);
this.hud.output.addMessage(msg);
if (callback) {

Просмотреть файл

@ -166,4 +166,26 @@ function* testJSTerm(hud) {
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}`);
}
}

Просмотреть файл

@ -17,6 +17,7 @@ Cu.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderMo
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",
@ -242,6 +243,8 @@ function WebConsoleFrame(webConsoleOwner) {
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;
@ -1494,6 +1497,11 @@ WebConsoleFrame.prototype = {
// 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;
}

Просмотреть файл

@ -13,6 +13,7 @@ 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);
@ -873,7 +874,7 @@ WebConsoleActor.prototype =
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;
@ -889,6 +890,12 @@ WebConsoleActor.prototype =
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) {}
}
}
@ -910,6 +917,7 @@ WebConsoleActor.prototype =
timestamp: timestamp,
exception: errorGrip,
exceptionMessage: this._createStringGrip(errorMessage),
exceptionDocURL: errorDocURL,
helperResult: helperResult,
};
},

Просмотреть файл

@ -407,14 +407,23 @@ private:
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;

Просмотреть файл

@ -1018,6 +1018,11 @@ EmptyBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
{
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();
}
@ -1025,6 +1030,11 @@ 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);

Просмотреть файл

@ -754,7 +754,17 @@ public:
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;

Просмотреть файл

@ -16,8 +16,8 @@
#include <algorithm>
#include "nsPIDOMWindow.h"
using namespace mozilla;
using namespace mozilla::dom;
namespace mozilla {
namespace dom {
class MultipartBlobImpl final : public BlobImplBase
{
@ -104,11 +104,6 @@ public:
mName = aName;
}
void SetFromNsIFile(bool aValue)
{
mIsFromNsIFile = aValue;
}
virtual bool MayBeClonedToOtherThreads() const override;
protected:
@ -137,4 +132,7 @@ protected:
bool mIsFromNsIFile;
};
} // dom namespace
} // mozilla namespace
#endif // mozilla_dom_MultipartBlobImpl_h

Просмотреть файл

@ -454,7 +454,6 @@ ResponsiveImageSelector::ComputeFinalWidthForCurrentViewport(double *aWidth)
mSizeValues[i]);
}
MOZ_ASSERT(effectiveWidth >= 0);
*aWidth = nsPresContext::AppUnitsToDoubleCSSPixels(std::max(effectiveWidth, 0));
return true;
}

Просмотреть файл

@ -12566,49 +12566,37 @@ nsDocument::ShouldLockPointer(Element* aElement, Element* aCurrentLock,
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;
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
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()");
return false;
}
nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
nsIPresShell* shell = GetShell();
if (!shell) {
NS_WARNING("SetPointerLock(): Unable to find presContext->PresShell()");
NS_WARNING("SetPointerLock(): No PresShell");
return false;
}
nsPresContext* presContext = shell->GetPresContext();
if (!presContext) {
NS_WARNING("SetPointerLock(): Unable to get PresContext");
return false;
}
nsCOMPtr<nsIWidget> widget;
nsIFrame* rootFrame = shell->GetRootFrame();
if (!rootFrame) {
NS_WARNING("SetPointerLock(): Unable to get root frame");
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;
}
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;
}
// Hide the cursor and set pointer lock for future mouse events

Просмотреть файл

@ -1,5 +1,7 @@
[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
@ -26,3 +28,4 @@ skip-if = true # Intermittent failures - bug 987493. Restore the skip-if above o
[browser_use_counters.js]
[browser_bug1238440.js]
skip-if = e10s
[browser_bug1011748.js]

Просмотреть файл

@ -0,0 +1,31 @@
const gHttpTestRoot = "http://example.com/browser/dom/base/test/";
add_task(function* () {
var statusTexts = [];
var xhr = new XMLHttpRequest();
var observer = {
observe: function (aSubject, aTopic, aData) {
try {
var channel = aSubject.QueryInterface(Ci.nsIHttpChannel);
channel.getResponseHeader("Location");
} catch (e) {
return;
}
statusTexts.push(xhr.statusText);
}
};
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();
});
Services.obs.removeObserver(observer, "http-on-examine-response");
});

Просмотреть файл

@ -673,17 +673,12 @@ skip-if = buildapp == 'b2g'
[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]
@ -739,9 +734,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #bug 901343, specialpowers.wr
[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]
@ -857,9 +850,6 @@ 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]

Просмотреть файл

@ -1,57 +0,0 @@
<!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">
/** Test for Bug 1011748 **/
"use strict";
var observer = {
observe: function (aSubject, aTopic, aData) {
try {
var channel = aSubject.QueryInterface(SpecialPowers.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();
});
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>

Просмотреть файл

@ -83,7 +83,6 @@ support-files =
browserElement_XFrameOptionsAllowFrom.js
browserElement_XFrameOptionsDeny.js
browserElement_XFrameOptionsSameOrigin.js
browserElement_XFrameOptionsSameOrigin.js
browserElement_GetContentDimensions.js
browserElement_AudioChannel.js
browserElement_AudioChannel_nested.js

Просмотреть файл

@ -2438,7 +2438,7 @@ CanvasRenderingContext2D::ParseFilter(const nsAString& aString,
return false;
}
aFilterChain = sc->StyleSVGReset()->mFilters;
aFilterChain = sc->StyleEffects()->mFilters;
return true;
}

Просмотреть файл

@ -756,7 +756,13 @@ TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec, const char* f
// 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,

Просмотреть файл

@ -1909,16 +1909,24 @@ ContentEventHandler::ConvertToRootRelativeOffset(nsIFrame* aFrame,
{
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;
}

Просмотреть файл

@ -264,8 +264,8 @@ protected:
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

Просмотреть файл

@ -4339,10 +4339,6 @@ EventStateManager::SetPointerLock(nsIWidget* aWidget,
// NOTE: aElement will be nullptr when unlocking.
sIsPointerLocked = !!aElement;
if (!aWidget) {
return;
}
// Reset mouse wheel transaction
WheelTransaction::EndTransaction();
@ -4351,6 +4347,8 @@ EventStateManager::SetPointerLock(nsIWidget* aWidget,
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;
@ -4358,8 +4356,8 @@ EventStateManager::SetPointerLock(nsIWidget* aWidget,
// 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);
@ -4374,8 +4372,10 @@ EventStateManager::SetPointerLock(nsIWidget* aWidget,
// 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);

Просмотреть файл

@ -294,6 +294,15 @@ public:
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.
@ -560,15 +569,6 @@ protected:
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];

Просмотреть файл

@ -1352,8 +1352,8 @@ function doTestZoomedScroll(aCallback)
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); });
}

Просмотреть файл

@ -493,7 +493,7 @@ HTMLTableCellElement::MapAttributesIntoRule(const nsMappedAttributes* aAttribute
}
}
}
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

Просмотреть файл

@ -111,7 +111,7 @@ HTMLTableColElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes
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

Просмотреть файл

@ -286,7 +286,7 @@ HTMLTableRowElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes
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

Просмотреть файл

@ -186,7 +186,7 @@ HTMLTableSectionElement::MapAttributesIntoRule(const nsMappedAttributes* aAttrib
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

Просмотреть файл

@ -0,0 +1 @@
<img srcset="data:,a 2400w" sizes="(min-width: 1px) calc(300px - 100vw)">

Просмотреть файл

@ -76,4 +76,4 @@ load 1032654.html
pref(dom.image.srcset.enabled,true) load 1141260.html
load 1228876.html
load 1230110.html
load 1237633.html

Просмотреть файл

@ -1431,12 +1431,10 @@ 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) {
@ -1445,8 +1443,6 @@ nsGenericHTMLElement::MapImageAlignAttributeInto(const nsMappedAttributes* aAttr
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) {
@ -1460,7 +1456,6 @@ nsGenericHTMLElement::MapImageAlignAttributeInto(const nsMappedAttributes* aAttr
}
}
}
}
}
void

Просмотреть файл

@ -8,9 +8,18 @@
<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");
@ -77,6 +86,15 @@ function enterFullscreen() {
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();
}

Просмотреть файл

@ -10453,7 +10453,7 @@ DatabaseConnection::Close()
AssertIsOnConnectionThread();
MOZ_ASSERT(mStorageConnection);
MOZ_ASSERT(!mDEBUGSavepointCount);
MOZ_RELEASE_ASSERT(!mInWriteTransaction);
MOZ_ASSERT(!mInWriteTransaction);
PROFILER_LABEL("IndexedDB",
"DatabaseConnection::Close",
@ -12139,7 +12139,7 @@ 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));
@ -12313,7 +12313,7 @@ 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);
@ -12340,7 +12340,7 @@ 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);
@ -13965,7 +13965,7 @@ Database::RecvPBackgroundIDBTransactionConstructor(
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

Просмотреть файл

@ -657,58 +657,6 @@ private:
}
};
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;

Просмотреть файл

@ -106,12 +106,6 @@ 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
@ -460,121 +454,6 @@ TabParent::IsVisible() const
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()
{

Просмотреть файл

@ -544,11 +544,6 @@ public:
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,
@ -592,10 +587,6 @@ protected:
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;

Просмотреть файл

@ -29,3 +29,6 @@ openH264_description2=This plugin is automatically installed by Mozilla to compl
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.

Просмотреть файл

@ -17,7 +17,15 @@ 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.
@ -41,7 +49,13 @@ public:
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);
@ -115,6 +129,7 @@ private:
}
MediaQueue<AudioData> &mQueue;
size_t mSamplesPadding;
};
} // namespace mozilla

Просмотреть файл

@ -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

151
dom/media/AudioConverter.h Normal file
Просмотреть файл

@ -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 */

Просмотреть файл

@ -18,6 +18,7 @@
#include "CubebUtils.h"
#include "nsPrintfCString.h"
#include "gfxPrefs.h"
#include "AudioConverter.h"
namespace mozilla {
@ -352,6 +353,9 @@ AudioStream::Init(uint32_t aNumChannels, uint32_t aRate,
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);
}
@ -557,10 +561,10 @@ AudioStream::Downmix(Chunk* aChunk)
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) {

Просмотреть файл

@ -29,6 +29,8 @@ struct CubebDestroyPolicy
class AudioStream;
class FrameHistory;
class AudioConfig;
class AudioConverter;
class AudioClock
{
@ -367,10 +369,12 @@ private:
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

Просмотреть файл

@ -347,7 +347,7 @@ MP3TrackDemuxer::Duration() const {
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 {

Просмотреть файл

@ -12,7 +12,6 @@
#endif
#include "VideoUtils.h"
#include "ImageContainer.h"
#include "mozilla/UniquePtrExtensions.h"
#ifdef MOZ_WIDGET_GONK
#include <cutils/properties.h>
@ -47,7 +46,8 @@ AudioData::EnsureAudioBuffer()
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);
}
@ -487,35 +487,17 @@ VideoData::Create(const VideoInfo& aInfo,
}
#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>
@ -530,46 +512,12 @@ MediaRawData::Clone() const
s->mExtraData = mExtraData;
s->mCryptoInternal = mCryptoInternal;
s->mTrackInfo = mTrackInfo;
if (mSize) {
if (!s->EnsureCapacity(mSize)) {
if (!s->mBuffer.Append(mBuffer.Data(), mBuffer.Length())) {
return nullptr;
}
memcpy(s->mData, mData, mSize);
s->mSize = mSize;
}
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()
{
}
@ -578,7 +526,7 @@ size_t
MediaRawData::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t size = aMallocSizeOf(this);
size += aMallocSizeOf(mBuffer.get());
size += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
return size;
}
@ -594,69 +542,34 @@ MediaRawDataWriter::MediaRawDataWriter(MediaRawData* 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

Просмотреть файл

@ -14,7 +14,10 @@
#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 {
@ -26,6 +29,237 @@ class ImageContainer;
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:
@ -124,7 +358,7 @@ public:
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)
@ -159,7 +393,7 @@ public:
// mChannels channels, each with mFrames frames
RefPtr<SharedBuffer> mAudioBuffer;
// mFrames frames, each with mChannels values
UniquePtr<AudioDataValue[]> mAudioData;
AlignedAudioBuffer mAudioData;
protected:
~AudioData() {}
@ -338,12 +572,11 @@ public:
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.
@ -396,12 +629,12 @@ public:
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;
@ -421,16 +654,7 @@ protected:
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
};

Просмотреть файл

@ -736,7 +736,7 @@ 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,

Просмотреть файл

@ -295,11 +295,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
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;
@ -313,22 +308,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
// 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()
@ -1072,14 +1051,35 @@ bool MediaDecoderStateMachine::IsPlaying() const
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;
@ -2511,7 +2511,10 @@ MediaDecoderStateMachine::DropAudioUpToSeekTarget(MediaData* aSample)
}
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));

Просмотреть файл

@ -142,7 +142,7 @@ public:
MediaDecoderReader* aReader,
bool aRealTime = false);
nsresult Init();
nsresult Init(MediaDecoder* aDecoder);
// Enumeration for the valid decoding states
enum State {

190
dom/media/MediaInfo.cpp Normal file
Просмотреть файл

@ -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

Просмотреть файл

@ -474,6 +474,153 @@ 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

Просмотреть файл

@ -140,60 +140,6 @@ media::TimeIntervals GetEstimatedBufferedTimeRanges(mozilla::MediaResource* aStr
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)
{

Просмотреть файл

@ -163,13 +163,6 @@ static const int32_t MAX_VIDEO_HEIGHT = 4608;
// 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,

Просмотреть файл

@ -83,6 +83,7 @@ ParseKeySystem(const nsAString& aExpectedKeySystem,
static const char16_t* sKeySystems[] = {
MOZ_UTF16("org.w3.clearkey"),
MOZ_UTF16("com.adobe.primetime"),
MOZ_UTF16("com.widevine.alpha"),
};
bool
@ -140,6 +141,9 @@ KeySystemToGMPName(const nsAString& aKeySystem)
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();
}

Просмотреть файл

@ -33,9 +33,6 @@
#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
@ -306,6 +303,23 @@ MediaKeySystemAccess::GetKeySystemStatus(const nsAString& aKeySystem,
}
#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;
}
@ -319,16 +333,7 @@ GMPDecryptsAndDecodesAAC(mozIGeckoMediaPluginService* aGMPS,
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
@ -341,23 +346,12 @@ GMPDecryptsAndDecodesH264(mozIGeckoMediaPluginService* aGMPS,
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,
@ -367,20 +361,11 @@ GMPDecryptsAndGeckoDecodesH264(mozIGeckoMediaPluginService* aGMPService,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
MOZ_ASSERT(IsH264ContentType(aContentType));
return
(!HaveGMPFor(aGMPService,
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);
NS_LITERAL_CSTRING("h264")) &&
MP4Decoder::CanHandleMediaType(aContentType);
}
static bool
@ -392,20 +377,20 @@ GMPDecryptsAndGeckoDecodesAAC(mozIGeckoMediaPluginService* aGMPService,
NS_ConvertUTF16toUTF8(aKeySystem),
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
MOZ_ASSERT(IsAACContentType(aContentType));
return
(!HaveGMPFor(aGMPService,
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())
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
@ -457,6 +442,20 @@ IsSupported(mozIGeckoMediaPluginService* aGMPService,
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,
@ -468,13 +467,7 @@ GetSupportedConfig(mozIGeckoMediaPluginService* aGMPService,
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);
}
}
@ -511,6 +504,17 @@ GetSupportedConfig(mozIGeckoMediaPluginService* aGMPService,
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;

Просмотреть файл

@ -4,6 +4,9 @@
# 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',

Просмотреть файл

@ -21,6 +21,9 @@
#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;
@ -345,7 +348,7 @@ GMPChild::GetUTF8LibPath(nsACString& aOutLibPath)
}
bool
GMPChild::AnswerStartPlugin()
GMPChild::AnswerStartPlugin(const nsString& aAdapter)
{
LOGD("%s", __FUNCTION__);
@ -378,11 +381,18 @@ GMPChild::AnswerStartPlugin()
}
#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;

Просмотреть файл

@ -53,7 +53,7 @@ private:
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;

Просмотреть файл

@ -169,8 +169,9 @@ GMPDecryptorParent::Decrypt(uint32_t aId,
}
// Caller should ensure parameters passed in are valid.
MOZ_ASSERT(!aBuffer.IsEmpty() && aCrypto.mValid);
MOZ_ASSERT(!aBuffer.IsEmpty());
if (aCrypto.mValid) {
GMPDecryptionData data(aCrypto.mKeyId,
aCrypto.mIV,
aCrypto.mPlainSizes,
@ -178,6 +179,10 @@ GMPDecryptorParent::Decrypt(uint32_t aId,
aCrypto.mSessionIds);
Unused << SendDecrypt(aId, aBuffer, data);
} else {
GMPDecryptionData data;
Unused << SendDecrypt(aId, aBuffer, data);
}
}
bool

Просмотреть файл

@ -53,6 +53,7 @@ class GMPLoaderImpl : public GMPLoader {
public:
explicit GMPLoaderImpl(SandboxStarter* aStarter)
: mSandboxStarter(aStarter)
, mAdapter(nullptr)
{}
virtual ~GMPLoaderImpl() {}
@ -60,7 +61,8 @@ public:
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,
@ -73,15 +75,77 @@ public:
#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
@ -175,7 +239,8 @@ 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
@ -259,29 +324,27 @@ GMPLoaderImpl::Load(const char* aUTF8LibPath,
libSpec.value.pathname = aUTF8LibPath;
libSpec.type = PR_LibSpec_Pathname;
#endif
mLib = PR_LoadLibraryWithFlags(libSpec, 0);
if (!mLib) {
PRLibrary* lib = PR_LoadLibraryWithFlags(libSpec, 0);
if (!lib) {
return false;
}
GMPInitFunc initFunc = reinterpret_cast<GMPInitFunc>(PR_FindFunctionSymbol(mLib, "GMPInit"));
if (!initFunc) {
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;
}
if (initFunc(aPlatformAPI) != GMPNoErr) {
return false;
}
// 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);
GMPSetNodeIdFunc setNodeIdFunc = reinterpret_cast<GMPSetNodeIdFunc>(PR_FindFunctionSymbol(mLib, "GMPSetNodeId"));
if (setNodeIdFunc) {
setNodeIdFunc(nodeId.c_str(), nodeId.size());
}
mGetAPIFunc = reinterpret_cast<GMPGetAPIFunc>(PR_FindFunctionSymbol(mLib, "GMPGetAPI"));
if (!mGetAPIFunc) {
return false;
}
mAdapter->GMPInit(aPlatformAPI);
return true;
}
@ -291,20 +354,14 @@ 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();
}
}

Просмотреть файл

@ -8,6 +8,7 @@
#define GMP_LOADER_H__
#include <stdint.h>
#include "prlink.h"
#include "gmp-entrypoints.h"
#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
@ -29,6 +30,22 @@ public:
#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).
//
@ -45,18 +62,24 @@ public:
//
// 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;

Просмотреть файл

@ -35,6 +35,15 @@ using CrashReporter::GetIDFromMinidump;
#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
@ -80,10 +89,24 @@ 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);
@ -98,12 +121,12 @@ GMPParent::Init(GeckoMediaPluginServiceParent* aService, nsIFile* aPluginDir)
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());
@ -175,7 +198,7 @@ GMPParent::LoadProcess()
#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;
@ -546,10 +569,10 @@ 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;
@ -752,7 +775,7 @@ ReadInfoField(GMPInfoFileParser& aParser, const nsCString& aKey, nsACString& aOu
return true;
}
nsresult
RefPtr<GenericPromise>
GMPParent::ReadGMPMetaData()
{
MOZ_ASSERT(mDirectory, "Plugin directory cannot be NULL!");
@ -761,13 +784,34 @@ GMPParent::ReadGMPMetaData()
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;
@ -775,7 +819,7 @@ GMPParent::ReadGMPMetaData()
!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
@ -793,37 +837,36 @@ GMPParent::ReadGMPMetaData()
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)
@ -831,36 +874,104 @@ GMPParent::ReadGMPMetaData()
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 NS_OK;
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__);
}
// 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
{

Просмотреть файл

@ -21,6 +21,7 @@
#include "nsString.h"
#include "nsTArray.h"
#include "nsIFile.h"
#include "mozilla/MozPromise.h"
class nsIThread;
@ -41,6 +42,16 @@ 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;
};
@ -75,7 +86,7 @@ public:
GMPParent();
nsresult Init(GeckoMediaPluginServiceParent* aService, nsIFile* aPluginDir);
RefPtr<GenericPromise> Init(GeckoMediaPluginServiceParent* aService, nsIFile* aPluginDir);
nsresult CloneFrom(const GMPParent* aOther);
void Crash();
@ -151,9 +162,15 @@ public:
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);
@ -196,8 +213,9 @@ private:
#ifdef XP_WIN
nsCString mLibs;
#endif
nsString mAdapter;
uint32_t mPluginId;
nsTArray<nsAutoPtr<GMPCapability>> mCapabilities;
nsTArray<GMPCapability> mCapabilities;
GMPProcessParent* mProcess;
bool mDeleteProcessOnlyOnUnload;
bool mAbnormalShutdownInProgress;

Просмотреть файл

@ -357,6 +357,8 @@ GeckoMediaPluginService::GetThread(nsIThread** aThread)
return rv;
}
mAbstractGMPThread = AbstractThread::CreateXPCOMThreadWrapper(mGMPThread, false);
// Tell the thread to initialize plugins
InitializePlugins();
}
@ -367,6 +369,12 @@ GeckoMediaPluginService::GetThread(nsIThread** aThread)
return NS_OK;
}
RefPtr<AbstractThread>
GeckoMediaPluginService::GetAbstractGMPThread()
{
return mAbstractGMPThread;
}
class GetGMPContentParentForAudioDecoderDone : public GetGMPContentParentCallback
{
public:

Просмотреть файл

@ -19,6 +19,7 @@
#include "nsPIDOMWindow.h"
#include "nsIDocument.h"
#include "nsIWeakReference.h"
#include "mozilla/AbstractThread.h"
template <class> struct already_AddRefed;
@ -74,6 +75,8 @@ public:
void AddPluginCrashedEventTarget(const uint32_t aPluginId,
nsPIDOMWindowInner* aParentWindow);
RefPtr<AbstractThread> GetAbstractGMPThread();
protected:
GeckoMediaPluginService();
virtual ~GeckoMediaPluginService();
@ -92,6 +95,7 @@ protected:
Mutex mMutex; // Protects mGMPThread and mGMPThreadShutdown and some members
// in derived classes.
nsCOMPtr<nsIThread> mGMPThread;
RefPtr<AbstractThread> mAbstractGMPThread;
bool mGMPThreadShutdown;
bool mShuttingDownOnGMPThread;

Просмотреть файл

@ -91,6 +91,8 @@ GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent()
#endif
, mScannedPluginOnDisk(false)
, mWaitingForPluginsSyncShutdown(false)
, mInitPromiseMonitor("GeckoMediaPluginServiceParent::mInitPromiseMonitor")
, mLoadPluginsFromDiskComplete(false)
{
MOZ_ASSERT(NS_IsMainThread());
if (!sHaveSetGMPServiceParentPrefCaches) {
@ -101,6 +103,7 @@ GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent()
Preferences::AddBoolVarCache(&sAllowInsecureGMP,
"media.gmp.insecure.allow", false);
}
mInitPromise.SetMonitor(&mInitPromiseMonitor);
}
GeckoMediaPluginServiceParent::~GeckoMediaPluginServiceParent()
@ -508,30 +511,72 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
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()));
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) {
return false;
NS_WARNING("GeckoMediaPluginServiceParent::GetContentParentFrom failed");
callback->Done(nullptr);
return;
}
return gmp->GetGMPContentParent(Move(aCallback));
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
@ -702,36 +747,39 @@ GeckoMediaPluginServiceParent::CrashPlugins()
}
}
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 {
@ -758,13 +806,9 @@ private:
NS_IMETHODIMP
GeckoMediaPluginServiceParent::PathRunnable::Run()
{
if (mOperation == ADD) {
mService->AddOnGMPThread(mPath);
} else {
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.
@ -778,12 +822,41 @@ GeckoMediaPluginServiceParent::PathRunnable::Run()
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
@ -983,8 +1056,8 @@ GeckoMediaPluginServiceParent::ClonePlugin(const GMPParent* aOriginal)
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()));
@ -992,22 +1065,31 @@ GeckoMediaPluginServiceParent::AddOnGMPThread(const nsAString& aDirectory)
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__);
}
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(mMutex);
mPlugins.AppendElement(gmp);
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
@ -1245,13 +1327,18 @@ GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
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))) {

Просмотреть файл

@ -13,6 +13,7 @@
#include "nsDataHashtable.h"
#include "mozilla/Atomics.h"
#include "nsThreadUtils.h"
#include "mozilla/MozPromise.h"
template <class> struct already_AddRefed;
@ -55,6 +56,8 @@ public:
#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;
@ -80,10 +83,8 @@ private:
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);
@ -105,6 +106,8 @@ protected:
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,
@ -119,7 +122,6 @@ private:
{
public:
enum EOperation {
ADD,
REMOVE,
REMOVE_AND_DELETE_FROM_DISK,
};
@ -193,6 +195,12 @@ private:
// 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);

Просмотреть файл

@ -138,7 +138,7 @@ ReadIntoArray(nsIFile* aFile,
return (bytesRead == length);
}
static bool
bool
ReadIntoString(nsIFile* aFile,
nsCString& aOutDst,
size_t aMaxLength)

Просмотреть файл

@ -75,6 +75,11 @@ ReadIntoArray(nsIFile* aFile,
nsTArray<uint8_t>& aOutDst,
size_t aMaxLength);
bool
ReadIntoString(nsIFile* aFile,
nsCString& aOutDst,
size_t aMaxLength);
} // namespace mozilla
#endif

Просмотреть файл

@ -34,7 +34,7 @@ parent:
child:
async BeginAsyncShutdown();
async CrashPluginNow();
intr StartPlugin();
intr StartPlugin(nsString adapter);
async SetNodeId(nsCString nodeId);
async PreloadLibs(nsCString libs);
async CloseActive();

Просмотреть файл

@ -4,6 +4,9 @@
# 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 += [
@ -114,6 +117,11 @@ if CONFIG['OS_TARGET'] in ('WINNT', 'Darwin'):
'rlz',
]
if 'widevine' in CONFIG['MOZ_EME_MODULES']:
DIRS += [
'widevine-adapter',
]
IPDL_SOURCES += [
'GMPTypes.ipdlh',
'PGMP.ipdl',

Просмотреть файл

@ -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

Просмотреть файл

@ -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_

Просмотреть файл

@ -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

Просмотреть файл

@ -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_

Просмотреть файл

@ -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

Просмотреть файл

@ -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_

Просмотреть файл

@ -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

Просмотреть файл

@ -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_

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,19 @@
# -*- 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/.
SOURCES += [
'WidevineAdapter.cpp',
'WidevineDecryptor.cpp',
'WidevineUtils.cpp',
'WidevineVideoDecoder.cpp',
'WidevineVideoFrame.cpp',
]
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'/dom/media/gmp',
]

Просмотреть файл

@ -29,6 +29,7 @@ public:
private:
void MarkFinished()
{
MOZ_ASSERT(NS_IsMainThread());
mFinished = true;
}

Просмотреть файл

@ -228,14 +228,29 @@ void
GMPRemoveTest::Setup()
{
GeneratePlugin();
EXPECT_OK(GetServiceParent()->RemovePluginDirectory(mOriginalPath));
GetService()->GetThread(getter_AddRefs(mGMPThread));
GetServiceParent()->AddPluginDirectory(mTmpPath);
// Spin the event loop until the GMP service has had a chance to complete
// adding GMPs from MOZ_GMP_PATH. Otherwise, the RemovePluginDirectory()
// below may complete before we're finished adding GMPs from MOZ_GMP_PATH,
// and we'll end up not removing the GMP, and the test will fail.
RefPtr<AbstractThread> thread(GetServiceParent()->GetAbstractGMPThread());
GMPTestMonitor* mon = &mTestMonitor;
GetServiceParent()->EnsureInitialized()->Then(thread, __func__,
[mon]() { mon->SetFinished(); },
[mon]() { mon->SetFinished(); }
);
mTestMonitor.AwaitFinished();
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
obs->AddObserver(this, GMP_DELETED_TOPIC, false /* strong ref */);
EXPECT_OK(GetServiceParent()->RemovePluginDirectory(mOriginalPath));
GetService()->GetThread(getter_AddRefs(mGMPThread));
GetServiceParent()->AsyncAddPluginDirectory(mTmpPath)->Then(thread, __func__,
[mon]() { mon->SetFinished(); },
[mon]() { mon->SetFinished(); }
);
mTestMonitor.AwaitFinished();
}
bool

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше