mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-07 18:04:46 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
7419b36815
@ -74,7 +74,7 @@ XPCOMUtils.defineLazyGetter(this, "standaloneStylesheets", () => {
|
||||
});
|
||||
|
||||
class BasePopup {
|
||||
constructor(extension, viewNode, popupURL, browserStyle, fixedWidth = false) {
|
||||
constructor(extension, viewNode, popupURL, browserStyle, fixedWidth = false, blockParser = false) {
|
||||
this.extension = extension;
|
||||
this.popupURL = popupURL;
|
||||
this.viewNode = viewNode;
|
||||
@ -82,6 +82,7 @@ class BasePopup {
|
||||
this.window = viewNode.ownerGlobal;
|
||||
this.destroyed = false;
|
||||
this.fixedWidth = fixedWidth;
|
||||
this.blockParser = blockParser;
|
||||
|
||||
extension.callOnClose(this);
|
||||
|
||||
@ -117,9 +118,14 @@ class BasePopup {
|
||||
|
||||
this.destroyed = true;
|
||||
this.browserLoadedDeferred.reject(new Error("Popup destroyed"));
|
||||
|
||||
BasePopup.instances.get(this.window).delete(this.extension);
|
||||
|
||||
return this.browserReady.then(() => {
|
||||
this.destroyBrowser(this.browser, true);
|
||||
this.browser.remove();
|
||||
if (this.browser) {
|
||||
this.destroyBrowser(this.browser, true);
|
||||
this.browser.remove();
|
||||
}
|
||||
|
||||
if (this.viewNode) {
|
||||
this.viewNode.removeEventListener(this.DESTROY_EVENT, this);
|
||||
@ -131,8 +137,6 @@ class BasePopup {
|
||||
this.panel.style.removeProperty("--panel-arrow-image-vertical");
|
||||
}
|
||||
|
||||
BasePopup.instances.get(this.window).delete(this.extension);
|
||||
|
||||
this.browser = null;
|
||||
this.viewNode = null;
|
||||
});
|
||||
@ -287,6 +291,7 @@ class BasePopup {
|
||||
|
||||
mm.sendAsyncMessage("Extension:InitBrowser", {
|
||||
allowScriptsToClose: true,
|
||||
blockParser: this.blockParser,
|
||||
fixedWidth: this.fixedWidth,
|
||||
maxWidth: 800,
|
||||
maxHeight: 600,
|
||||
@ -297,6 +302,12 @@ class BasePopup {
|
||||
});
|
||||
}
|
||||
|
||||
unblockParser() {
|
||||
this.browserReady.then(browser => {
|
||||
this.browser.messageManager.sendAsyncMessage("Extension:UnblockParser");
|
||||
});
|
||||
}
|
||||
|
||||
resizeBrowser({width, height, detail}) {
|
||||
if (this.fixedWidth) {
|
||||
// Figure out how much extra space we have on the side of the panel
|
||||
@ -401,7 +412,7 @@ class PanelPopup extends BasePopup {
|
||||
}
|
||||
|
||||
class ViewPopup extends BasePopup {
|
||||
constructor(extension, window, popupURL, browserStyle, fixedWidth) {
|
||||
constructor(extension, window, popupURL, browserStyle, fixedWidth, blockParser) {
|
||||
let document = window.document;
|
||||
|
||||
// Create a temporary panel to hold the browser while it pre-loads its
|
||||
@ -411,7 +422,7 @@ class ViewPopup extends BasePopup {
|
||||
panel.setAttribute("type", "arrow");
|
||||
document.getElementById("mainPopupSet").appendChild(panel);
|
||||
|
||||
super(extension, panel, popupURL, browserStyle, fixedWidth);
|
||||
super(extension, panel, popupURL, browserStyle, fixedWidth, blockParser);
|
||||
|
||||
this.ignoreResizes = true;
|
||||
|
||||
|
@ -122,6 +122,8 @@ BrowserAction.prototype = {
|
||||
node.setAttribute("constrain-size", "true");
|
||||
|
||||
node.onmousedown = event => this.handleEvent(event);
|
||||
node.onmouseover = event => this.handleEvent(event);
|
||||
node.onmouseout = event => this.handleEvent(event);
|
||||
|
||||
this.updateButton(node, this.defaults);
|
||||
},
|
||||
@ -209,10 +211,10 @@ BrowserAction.prototype = {
|
||||
let popupURL = this.getProperty(tab, "popup");
|
||||
let enabled = this.getProperty(tab, "enabled");
|
||||
|
||||
if (popupURL && enabled) {
|
||||
if (popupURL && enabled && (this.pendingPopup || !ViewPopup.for(this.extension, window))) {
|
||||
// Add permission for the active tab so it will exist for the popup.
|
||||
// Store the tab to revoke the permission during clearPopup.
|
||||
if (!this.pendingPopup && !this.tabManager.hasActiveTabPermission(tab)) {
|
||||
if (!this.tabManager.hasActiveTabPermission(tab)) {
|
||||
this.tabManager.addActiveTabPermission(tab);
|
||||
this.tabToRevokeDuringClearPopup = tab;
|
||||
}
|
||||
@ -242,6 +244,26 @@ BrowserAction.prototype = {
|
||||
}
|
||||
break;
|
||||
|
||||
case "mouseover": {
|
||||
// Begin pre-loading the browser for the popup, so it's more likely to
|
||||
// be ready by the time we get a complete click.
|
||||
let tab = window.gBrowser.selectedTab;
|
||||
let popupURL = this.getProperty(tab, "popup");
|
||||
let enabled = this.getProperty(tab, "enabled");
|
||||
|
||||
if (popupURL && enabled && (this.pendingPopup || !ViewPopup.for(this.extension, window))) {
|
||||
this.pendingPopup = this.getPopup(window, popupURL, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case "mouseout":
|
||||
if (this.pendingPopup) {
|
||||
this.clearPopup();
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case "popupshowing":
|
||||
const menu = event.target;
|
||||
const trigger = menu.triggerNode;
|
||||
@ -271,22 +293,28 @@ BrowserAction.prototype = {
|
||||
* The browser window in which to create the popup.
|
||||
* @param {string} popupURL
|
||||
* The URL to load into the popup.
|
||||
* @param {boolean} [blockParser = false]
|
||||
* True if the HTML parser should initially be blocked.
|
||||
* @returns {ViewPopup}
|
||||
*/
|
||||
getPopup(window, popupURL) {
|
||||
getPopup(window, popupURL, blockParser = false) {
|
||||
this.clearPopupTimeout();
|
||||
let {pendingPopup} = this;
|
||||
this.pendingPopup = null;
|
||||
|
||||
if (pendingPopup) {
|
||||
if (pendingPopup.window === window && pendingPopup.popupURL === popupURL) {
|
||||
if (!this.blockParser) {
|
||||
pendingPopup.unblockParser();
|
||||
}
|
||||
|
||||
return pendingPopup;
|
||||
}
|
||||
pendingPopup.destroy();
|
||||
}
|
||||
|
||||
let fixedWidth = this.widget.areaType == CustomizableUI.TYPE_MENU_PANEL;
|
||||
return new ViewPopup(this.extension, window, popupURL, this.browserStyle, fixedWidth);
|
||||
return new ViewPopup(this.extension, window, popupURL, this.browserStyle, fixedWidth, blockParser);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -7,6 +7,9 @@ let scriptPage = url => `<html><head><meta charset="utf-8"><script src="${url}">
|
||||
add_task(function* testBrowserActionClickCanceled() {
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
|
||||
|
||||
// Make sure the mouse isn't hovering over the browserAction widget.
|
||||
EventUtils.synthesizeMouseAtCenter(gURLBar, {type: "mouseover"}, window);
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"browser_action": {
|
||||
@ -82,6 +85,9 @@ add_task(function* testBrowserActionClickCanceled() {
|
||||
});
|
||||
|
||||
add_task(function* testBrowserActionDisabled() {
|
||||
// Make sure the mouse isn't hovering over the browserAction widget.
|
||||
EventUtils.synthesizeMouseAtCenter(gURLBar, {type: "mouseover"}, window);
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"browser_action": {
|
||||
@ -186,6 +192,9 @@ add_task(function* testBrowserActionTabPopulation() {
|
||||
yield BrowserTestUtils.loadURI(win.gBrowser.selectedBrowser, "http://example.com/");
|
||||
yield BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
|
||||
|
||||
// Make sure the mouse isn't hovering over the browserAction widget.
|
||||
EventUtils.synthesizeMouseAtCenter(win.gURLBar, {type: "mouseover"}, win);
|
||||
|
||||
yield extension.startup();
|
||||
|
||||
let widget = getBrowserActionWidget(extension).forWindow(win);
|
||||
|
@ -3,6 +3,9 @@
|
||||
"use strict";
|
||||
|
||||
function* testExecuteBrowserActionWithOptions(options = {}) {
|
||||
// Make sure the mouse isn't hovering over the browserAction widget.
|
||||
EventUtils.synthesizeMouseAtCenter(gURLBar, {type: "mouseover"}, window);
|
||||
|
||||
let extensionOptions = {};
|
||||
|
||||
extensionOptions.manifest = {
|
||||
|
@ -3,6 +3,9 @@
|
||||
"use strict";
|
||||
|
||||
add_task(function* testIncognitoViews() {
|
||||
// Make sure the mouse isn't hovering over the browserAction widget.
|
||||
EventUtils.synthesizeMouseAtCenter(gURLBar, {type: "mouseover"}, window);
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": ["tabs"],
|
||||
|
@ -229,7 +229,9 @@ ContentPrincipal::GetOriginInternal(nsACString& aOrigin)
|
||||
// of the spec, and the beginning of the origin attributes) is not present
|
||||
// in the origin string
|
||||
nsCOMPtr<nsIStandardURL> standardURL = do_QueryInterface(origin);
|
||||
NS_ENSURE_TRUE(standardURL, NS_ERROR_FAILURE);
|
||||
if (!standardURL) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
rv = origin->GetAsciiSpec(aOrigin);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -6,9 +6,13 @@
|
||||
|
||||
const { addons, createClass, DOM: dom, PropTypes } =
|
||||
require("devtools/client/shared/vendor/react");
|
||||
const { throttle } = require("devtools/client/inspector/shared/utils");
|
||||
|
||||
const Types = require("../types");
|
||||
|
||||
// The delay prior to executing the grid cell highlighting.
|
||||
const GRID_CELL_MOUSEOVER_TIMEOUT = 150;
|
||||
|
||||
// Move SVG grid to the right 100 units, so that it is not flushed against the edge of
|
||||
// layout border
|
||||
const TRANSLATE_X = -100;
|
||||
@ -35,6 +39,13 @@ module.exports = createClass({
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount() {
|
||||
// Throttle the grid highlighting of grid cells. It makes the UX smoother by not
|
||||
// lagging the grid cell highlighting if a lot of grid cells are mouseover in a
|
||||
// quick succession.
|
||||
this.highlightCell = throttle(this.highlightCell, GRID_CELL_MOUSEOVER_TIMEOUT);
|
||||
},
|
||||
|
||||
componentWillReceiveProps({ grids }) {
|
||||
this.setState({
|
||||
selectedGrids: grids.filter(grid => grid.highlighted),
|
||||
@ -66,6 +77,31 @@ module.exports = createClass({
|
||||
return gridArea.name;
|
||||
},
|
||||
|
||||
highlightCell({ target }) {
|
||||
const {
|
||||
grids,
|
||||
onShowGridAreaHighlight,
|
||||
onShowGridCellHighlight,
|
||||
} = this.props;
|
||||
const name = target.getAttribute("data-grid-area-name");
|
||||
const id = target.getAttribute("data-grid-id");
|
||||
const fragmentIndex = target.getAttribute("data-grid-fragment-index");
|
||||
const color = target.getAttribute("stroke");
|
||||
const rowNumber = target.getAttribute("data-grid-row");
|
||||
const columnNumber = target.getAttribute("data-grid-column");
|
||||
|
||||
target.setAttribute("fill", color);
|
||||
|
||||
if (name) {
|
||||
onShowGridAreaHighlight(grids[id].nodeFront, name, color);
|
||||
}
|
||||
|
||||
if (fragmentIndex && rowNumber && columnNumber) {
|
||||
onShowGridCellHighlight(grids[id].nodeFront, fragmentIndex,
|
||||
rowNumber, columnNumber);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the grid outline for the given grid container object.
|
||||
*
|
||||
@ -186,29 +222,9 @@ module.exports = createClass({
|
||||
onShowGridCellHighlight(grids[id].nodeFront);
|
||||
},
|
||||
|
||||
onMouseOverCell({ target }) {
|
||||
const {
|
||||
grids,
|
||||
onShowGridAreaHighlight,
|
||||
onShowGridCellHighlight,
|
||||
} = this.props;
|
||||
const name = target.getAttribute("data-grid-area-name");
|
||||
const id = target.getAttribute("data-grid-id");
|
||||
const fragmentIndex = target.getAttribute("data-grid-fragment-index");
|
||||
const color = target.getAttribute("stroke");
|
||||
const rowNumber = target.getAttribute("data-grid-row");
|
||||
const columnNumber = target.getAttribute("data-grid-column");
|
||||
|
||||
target.setAttribute("fill", color);
|
||||
|
||||
if (name) {
|
||||
onShowGridAreaHighlight(grids[id].nodeFront, name, color);
|
||||
}
|
||||
|
||||
if (fragmentIndex && rowNumber && columnNumber) {
|
||||
onShowGridCellHighlight(grids[id].nodeFront, fragmentIndex,
|
||||
rowNumber, columnNumber);
|
||||
}
|
||||
onMouseOverCell(event) {
|
||||
event.persist();
|
||||
this.highlightCell(event);
|
||||
},
|
||||
|
||||
render() {
|
||||
|
@ -122,6 +122,7 @@ const char* mozilla::dom::ContentPrefs::gInitPrefs[] = {
|
||||
"media.decoder.recycle.enabled",
|
||||
"media.dormant-on-pause-timeout-ms",
|
||||
"media.eme.audio.blank",
|
||||
"media.eme.chromium-api.enabled",
|
||||
"media.eme.enabled",
|
||||
"media.eme.video.blank",
|
||||
"media.ffmpeg.enabled",
|
||||
|
@ -142,6 +142,7 @@ private:
|
||||
DECL_MEDIA_PREF("media.gmp.decoder.h264", GMPH264Preferred, uint32_t, 0);
|
||||
DECL_MEDIA_PREF("media.eme.audio.blank", EMEBlankAudio, bool, false);
|
||||
DECL_MEDIA_PREF("media.eme.video.blank", EMEBlankVideo, bool, false);
|
||||
DECL_MEDIA_PREF("media.eme.chromium-api.enabled", EMEChromiumAPIEnabled, bool, false);
|
||||
|
||||
// MediaDecoderStateMachine
|
||||
DECL_MEDIA_PREF("media.suspend-bkgnd-video.enabled", MDSMSuspendBackgroundVideoEnabled, bool, false);
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
namespace mozilla {
|
||||
class MediaRawData;
|
||||
class ChromiumCDMProxy;
|
||||
|
||||
enum DecryptStatus {
|
||||
Ok = 0,
|
||||
@ -114,6 +115,7 @@ public:
|
||||
// Uses the CDM to load a presistent session stored on disk.
|
||||
// Calls MediaKeys::OnSessionActivated() when session is loaded.
|
||||
virtual void LoadSession(PromiseId aPromiseId,
|
||||
dom::MediaKeySessionType aSessionType,
|
||||
const nsAString& aSessionId) = 0;
|
||||
|
||||
// Main thread only.
|
||||
@ -223,6 +225,8 @@ public:
|
||||
|
||||
virtual uint32_t GetDecryptorId() { return 0; }
|
||||
|
||||
virtual ChromiumCDMProxy* AsChromiumCDMProxy() { return nullptr; }
|
||||
|
||||
protected:
|
||||
virtual ~CDMProxy() {}
|
||||
|
||||
|
@ -36,7 +36,7 @@ DetailedPromise::~DetailedPromise()
|
||||
// GetPromiseState() == PromiseState::Rejected. But by now we've been
|
||||
// unlinked, so don't have a reference to our actual JS Promise object
|
||||
// anymore.
|
||||
MaybeReportTelemetry(Failed);
|
||||
MaybeReportTelemetry(kFailed);
|
||||
}
|
||||
|
||||
void
|
||||
@ -46,7 +46,7 @@ DetailedPromise::MaybeReject(nsresult aArg, const nsACString& aReason)
|
||||
static_cast<uint32_t>(aArg), PromiseFlatCString(aReason).get());
|
||||
EME_LOG("%s", msg.get());
|
||||
|
||||
MaybeReportTelemetry(Failed);
|
||||
MaybeReportTelemetry(kFailed);
|
||||
|
||||
LogToBrowserConsole(NS_ConvertUTF8toUTF16(msg));
|
||||
|
||||
@ -84,7 +84,7 @@ DetailedPromise::Create(nsIGlobalObject* aGlobal,
|
||||
}
|
||||
|
||||
void
|
||||
DetailedPromise::MaybeReportTelemetry(Status aStatus)
|
||||
DetailedPromise::MaybeReportTelemetry(eStatus aStatus)
|
||||
{
|
||||
if (mResponded) {
|
||||
return;
|
||||
@ -95,8 +95,8 @@ DetailedPromise::MaybeReportTelemetry(Status aStatus)
|
||||
}
|
||||
uint32_t latency = (TimeStamp::Now() - mStartTime).ToMilliseconds();
|
||||
EME_LOG("%s %s latency %ums reported via telemetry", mName.get(),
|
||||
((aStatus == Succeeded) ? "succcess" : "failure"), latency);
|
||||
Telemetry::HistogramID tid = (aStatus == Succeeded) ? mSuccessLatencyProbe.Value()
|
||||
((aStatus == kSucceeded) ? "succcess" : "failure"), latency);
|
||||
Telemetry::HistogramID tid = (aStatus == kSucceeded) ? mSuccessLatencyProbe.Value()
|
||||
: mFailureLatencyProbe.Value();
|
||||
Telemetry::Accumulate(tid, latency);
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ public:
|
||||
void MaybeResolve(const T& aArg)
|
||||
{
|
||||
EME_LOG("%s promise resolved", mName.get());
|
||||
MaybeReportTelemetry(Succeeded);
|
||||
MaybeReportTelemetry(eStatus::kSucceeded);
|
||||
Promise::MaybeResolve<T>(aArg);
|
||||
}
|
||||
|
||||
@ -57,8 +57,8 @@ private:
|
||||
Telemetry::HistogramID aFailureLatencyProbe);
|
||||
virtual ~DetailedPromise();
|
||||
|
||||
enum Status { Succeeded, Failed };
|
||||
void MaybeReportTelemetry(Status aStatus);
|
||||
enum eStatus { kSucceeded, kFailed };
|
||||
void MaybeReportTelemetry(eStatus aStatus);
|
||||
|
||||
nsCString mName;
|
||||
bool mResponded;
|
||||
|
@ -396,7 +396,7 @@ MediaKeySession::Load(const nsAString& aSessionId, ErrorResult& aRv)
|
||||
SetSessionId(aSessionId);
|
||||
|
||||
PromiseId pid = mKeys->StorePromise(promise);
|
||||
mKeys->GetCDMProxy()->LoadSession(pid, aSessionId);
|
||||
mKeys->GetCDMProxy()->LoadSession(pid, mSessionType, aSessionId);
|
||||
|
||||
EME_LOG("MediaKeySession[%p,'%s'] Load() sent to CDM, promiseId=%d",
|
||||
this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);
|
||||
|
@ -99,8 +99,11 @@ MediaKeySystemAccess::CreateMediaKeys(ErrorResult& aRv)
|
||||
static bool
|
||||
HavePluginForKeySystem(const nsCString& aKeySystem)
|
||||
{
|
||||
bool havePlugin = HaveGMPFor(NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
|
||||
{ aKeySystem });
|
||||
nsCString api = MediaPrefs::EMEChromiumAPIEnabled()
|
||||
? NS_LITERAL_CSTRING(CHROMIUM_CDM_API)
|
||||
: NS_LITERAL_CSTRING(GMP_API_DECRYPTOR);
|
||||
|
||||
bool havePlugin = HaveGMPFor(api, { aKeySystem });
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// Check if we can use MediaDrm for this keysystem.
|
||||
if (!havePlugin) {
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "mozilla/dom/MediaKeySystemAccess.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "ChromiumCDMProxy.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -343,12 +344,23 @@ MediaKeys::CreateCDMProxy(nsIEventTarget* aMainThread)
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
proxy = new GMPCDMProxy(this,
|
||||
mKeySystem,
|
||||
new MediaKeysGMPCrashHelper(this),
|
||||
mConfig.mDistinctiveIdentifier == MediaKeysRequirement::Required,
|
||||
mConfig.mPersistentState == MediaKeysRequirement::Required,
|
||||
aMainThread);
|
||||
if (MediaPrefs::EMEChromiumAPIEnabled()) {
|
||||
proxy = new ChromiumCDMProxy(
|
||||
this,
|
||||
mKeySystem,
|
||||
new MediaKeysGMPCrashHelper(this),
|
||||
mConfig.mDistinctiveIdentifier == MediaKeysRequirement::Required,
|
||||
mConfig.mPersistentState == MediaKeysRequirement::Required,
|
||||
aMainThread);
|
||||
} else {
|
||||
proxy = new GMPCDMProxy(
|
||||
this,
|
||||
mKeySystem,
|
||||
new MediaKeysGMPCrashHelper(this),
|
||||
mConfig.mDistinctiveIdentifier == MediaKeysRequirement::Required,
|
||||
mConfig.mPersistentState == MediaKeysRequirement::Required,
|
||||
aMainThread);
|
||||
}
|
||||
}
|
||||
return proxy.forget();
|
||||
}
|
||||
|
@ -101,6 +101,7 @@ MediaDrmCDMProxy::CreateSession(uint32_t aCreateSessionToken,
|
||||
|
||||
void
|
||||
MediaDrmCDMProxy::LoadSession(PromiseId aPromiseId,
|
||||
dom::MediaKeySessionType aSessionType,
|
||||
const nsAString& aSessionId)
|
||||
{
|
||||
// TODO: Implement LoadSession.
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "mozilla/CDMCaps.h"
|
||||
#include "mozilla/dom/MediaKeys.h"
|
||||
#include "mozilla/dom/MediaKeySession.h"
|
||||
#include "mozilla/MediaDrmProxySupport.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
@ -47,6 +48,7 @@ public:
|
||||
nsTArray<uint8_t>& aInitData) override;
|
||||
|
||||
void LoadSession(PromiseId aPromiseId,
|
||||
dom::MediaKeySessionType aSessionType,
|
||||
const nsAString& aSessionId) override;
|
||||
|
||||
void SetServerCertificate(PromiseId aPromiseId,
|
||||
|
133
dom/media/gmp/ChromiumCDMAdapter.cpp
Normal file
133
dom/media/gmp/ChromiumCDMAdapter.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
/* -*- 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 "ChromiumCDMAdapter.h"
|
||||
#include "content_decryption_module.h"
|
||||
#include "VideoUtils.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 "WidevineUtils.h"
|
||||
#include "GMPLog.h"
|
||||
|
||||
// Declared in WidevineAdapter.cpp.
|
||||
extern const GMPPlatformAPI* sPlatform;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void
|
||||
ChromiumCDMAdapter::SetAdaptee(PRLibrary* aLib)
|
||||
{
|
||||
mLib = aLib;
|
||||
}
|
||||
|
||||
void*
|
||||
ChromiumCdmHost(int aHostInterfaceVersion, void* aUserData)
|
||||
{
|
||||
CDM_LOG("ChromiumCdmHostFunc(%d, %p)", aHostInterfaceVersion, aUserData);
|
||||
if (aHostInterfaceVersion != cdm::Host_8::kVersion) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<cdm::Host_8*>(aUserData);
|
||||
}
|
||||
|
||||
#define STRINGIFY(s) _STRINGIFY(s)
|
||||
#define _STRINGIFY(s) #s
|
||||
|
||||
GMPErr
|
||||
ChromiumCDMAdapter::GMPInit(const GMPPlatformAPI* aPlatformAPI)
|
||||
{
|
||||
CDM_LOG("ChromiumCDMAdapter::GMPInit");
|
||||
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;
|
||||
}
|
||||
|
||||
CDM_LOG(STRINGIFY(INITIALIZE_CDM_MODULE)"()");
|
||||
init();
|
||||
|
||||
return GMPNoErr;
|
||||
}
|
||||
|
||||
GMPErr
|
||||
ChromiumCDMAdapter::GMPGetAPI(const char* aAPIName,
|
||||
void* aHostAPI,
|
||||
void** aPluginAPI,
|
||||
uint32_t aDecryptorId)
|
||||
{
|
||||
CDM_LOG("ChromiumCDMAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p",
|
||||
aAPIName,
|
||||
aHostAPI,
|
||||
aPluginAPI,
|
||||
aDecryptorId,
|
||||
this);
|
||||
if (!strcmp(aAPIName, CHROMIUM_CDM_API)) {
|
||||
auto create = reinterpret_cast<decltype(::CreateCdmInstance)*>(
|
||||
PR_FindFunctionSymbol(mLib, "CreateCdmInstance"));
|
||||
if (!create) {
|
||||
CDM_LOG("ChromiumCDMAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p "
|
||||
"FAILED to find CreateCdmInstance",
|
||||
aAPIName,
|
||||
aHostAPI,
|
||||
aPluginAPI,
|
||||
aDecryptorId,
|
||||
this);
|
||||
return GMPGenericErr;
|
||||
}
|
||||
|
||||
auto cdm = reinterpret_cast<cdm::ContentDecryptionModule*>(
|
||||
create(cdm::ContentDecryptionModule::kVersion,
|
||||
kEMEKeySystemWidevine.get(),
|
||||
kEMEKeySystemWidevine.Length(),
|
||||
&ChromiumCdmHost,
|
||||
aHostAPI));
|
||||
if (!cdm) {
|
||||
CDM_LOG("ChromiumCDMAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p "
|
||||
"FAILED to create cdm",
|
||||
aAPIName,
|
||||
aHostAPI,
|
||||
aPluginAPI,
|
||||
aDecryptorId,
|
||||
this);
|
||||
return GMPGenericErr;
|
||||
}
|
||||
CDM_LOG("cdm: 0x%p", cdm);
|
||||
*aPluginAPI = cdm;
|
||||
}
|
||||
return *aPluginAPI ? GMPNoErr : GMPNotImplementedErr;
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMAdapter::GMPShutdown()
|
||||
{
|
||||
CDM_LOG("ChromiumCDMAdapter::GMPShutdown()");
|
||||
|
||||
decltype(::DeinitializeCdmModule)* deinit;
|
||||
deinit = (decltype(deinit))(PR_FindFunctionSymbol(mLib, "DeinitializeCdmModule"));
|
||||
if (deinit) {
|
||||
CDM_LOG("DeinitializeCdmModule()");
|
||||
deinit();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
ChromiumCDMAdapter::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
|
41
dom/media/gmp/ChromiumCDMAdapter.h
Normal file
41
dom/media/gmp/ChromiumCDMAdapter.h
Normal file
@ -0,0 +1,41 @@
|
||||
/* -*- 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 ChromiumAdapter_h_
|
||||
#define ChromiumAdapter_h_
|
||||
|
||||
#include "GMPLoader.h"
|
||||
#include "prlink.h"
|
||||
#include "GMPUtils.h"
|
||||
|
||||
struct GMPPlatformAPI;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ChromiumCDMAdapter : 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,
|
||||
uint32_t aDecryptorId) override;
|
||||
void GMPShutdown() override;
|
||||
|
||||
static bool Supports(int32_t aModuleVersion,
|
||||
int32_t aInterfaceVersion,
|
||||
int32_t aHostVersion);
|
||||
|
||||
private:
|
||||
PRLibrary* mLib = nullptr;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ChromiumAdapter_h_
|
620
dom/media/gmp/ChromiumCDMChild.cpp
Normal file
620
dom/media/gmp/ChromiumCDMChild.cpp
Normal file
@ -0,0 +1,620 @@
|
||||
/* -*- 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 "ChromiumCDMChild.h"
|
||||
#include "GMPContentChild.h"
|
||||
#include "WidevineUtils.h"
|
||||
#include "WidevineFileIO.h"
|
||||
#include "WidevineVideoFrame.h"
|
||||
#include "GMPLog.h"
|
||||
#include "GMPPlatform.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "base/time.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
||||
ChromiumCDMChild::ChromiumCDMChild(GMPContentChild* aPlugin)
|
||||
: mPlugin(aPlugin)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMChild::Init(cdm::ContentDecryptionModule_8* aCDM)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
mCDM = aCDM;
|
||||
MOZ_ASSERT(mCDM);
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMChild::TimerExpired(void* aContext)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::TimerExpired(context=0x%p)", aContext);
|
||||
if (mCDM) {
|
||||
mCDM->TimerExpired(aContext);
|
||||
}
|
||||
}
|
||||
|
||||
cdm::Buffer*
|
||||
ChromiumCDMChild::Allocate(uint32_t aCapacity)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::Allocate(capacity=%" PRIu32 ")", aCapacity);
|
||||
return new WidevineBuffer(aCapacity);
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMChild::SetTimer(int64_t aDelayMs, void* aContext)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::SetTimer(delay=%" PRId64 ", context=0x%p)",
|
||||
aDelayMs,
|
||||
aContext);
|
||||
RefPtr<ChromiumCDMChild> self(this);
|
||||
SetTimerOnMainThread(NewGMPTask([self, aContext]() {
|
||||
self->TimerExpired(aContext);
|
||||
}), aDelayMs);
|
||||
}
|
||||
|
||||
cdm::Time
|
||||
ChromiumCDMChild::GetCurrentWallTime()
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
return base::Time::Now().ToDoubleT();
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMChild::OnResolveNewSessionPromise(uint32_t aPromiseId,
|
||||
const char* aSessionId,
|
||||
uint32_t aSessionIdSize)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::OnResolveNewSessionPromise(pid=%" PRIu32
|
||||
", sid=%s)",
|
||||
aPromiseId,
|
||||
aSessionId);
|
||||
|
||||
if (mLoadSessionPromiseIds.Contains(aPromiseId)) {
|
||||
// As laid out in the Chromium CDM API, if the CDM fails to load
|
||||
// a session it calls OnResolveNewSessionPromise with nullptr as the sessionId.
|
||||
// We can safely assume this means that we have failed to load a session
|
||||
// as the other methods specify calling 'OnRejectPromise' when they fail.
|
||||
bool loadSuccessful = aSessionId != nullptr;
|
||||
GMP_LOG("ChromiumCDMChild::OnResolveNewSessionPromise(pid=%u, sid=%s) "
|
||||
"resolving %s load session ",
|
||||
aPromiseId,
|
||||
aSessionId,
|
||||
(loadSuccessful ? "successful" : "failed"));
|
||||
Unused << SendResolveLoadSessionPromise(aPromiseId, loadSuccessful);
|
||||
mLoadSessionPromiseIds.RemoveElement(aPromiseId);
|
||||
return;
|
||||
}
|
||||
|
||||
Unused << SendOnResolveNewSessionPromise(aPromiseId,
|
||||
nsCString(aSessionId, aSessionIdSize));
|
||||
}
|
||||
|
||||
void ChromiumCDMChild::OnResolvePromise(uint32_t aPromiseId)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::OnResolvePromise(pid=%" PRIu32 ")", aPromiseId);
|
||||
Unused << SendOnResolvePromise(aPromiseId);
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMChild::OnRejectPromise(uint32_t aPromiseId,
|
||||
cdm::Error aError,
|
||||
uint32_t aSystemCode,
|
||||
const char* aErrorMessage,
|
||||
uint32_t aErrorMessageSize)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::OnRejectPromise(pid=%" PRIu32 ", err=%" PRIu32
|
||||
" code=%" PRIu32 ", msg='%s')",
|
||||
aPromiseId,
|
||||
aError,
|
||||
aSystemCode,
|
||||
aErrorMessage);
|
||||
Unused << SendOnRejectPromise(aPromiseId,
|
||||
static_cast<uint32_t>(aError),
|
||||
aSystemCode,
|
||||
nsCString(aErrorMessage, aErrorMessageSize));
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMChild::OnSessionMessage(const char* aSessionId,
|
||||
uint32_t aSessionIdSize,
|
||||
cdm::MessageType aMessageType,
|
||||
const char* aMessage,
|
||||
uint32_t aMessageSize,
|
||||
const char* aLegacyDestinationUrl,
|
||||
uint32_t aLegacyDestinationUrlLength)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::OnSessionMessage(sid=%s, type=%" PRIu32
|
||||
" size=%" PRIu32 ")",
|
||||
aSessionId,
|
||||
aMessageType,
|
||||
aMessageSize);
|
||||
nsTArray<uint8_t> message;
|
||||
message.AppendElements(aMessage, aMessageSize);
|
||||
Unused << SendOnSessionMessage(nsCString(aSessionId, aSessionIdSize),
|
||||
static_cast<uint32_t>(aMessageType),
|
||||
message);
|
||||
}
|
||||
|
||||
static nsCString
|
||||
ToString(const cdm::KeyInformation* aKeysInfo, uint32_t aKeysInfoCount)
|
||||
{
|
||||
nsCString str;
|
||||
for (uint32_t i = 0; i < aKeysInfoCount; i++) {
|
||||
nsCString keyId;
|
||||
const cdm::KeyInformation& key = aKeysInfo[i];
|
||||
for (size_t k = 0; k < key.key_id_size; k++) {
|
||||
keyId.Append(nsPrintfCString("%hhX", key.key_id[k]));
|
||||
}
|
||||
if (!str.IsEmpty()) {
|
||||
str.AppendLiteral(",");
|
||||
}
|
||||
str.Append(keyId);
|
||||
str.AppendLiteral("=");
|
||||
str.AppendInt(key.status);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMChild::OnSessionKeysChange(const char *aSessionId,
|
||||
uint32_t aSessionIdSize,
|
||||
bool aHasAdditionalUsableKey,
|
||||
const cdm::KeyInformation* aKeysInfo,
|
||||
uint32_t aKeysInfoCount)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::OnSessionKeysChange(sid=%s) keys={%s}",
|
||||
aSessionId,
|
||||
ToString(aKeysInfo, aKeysInfoCount).get());
|
||||
|
||||
nsTArray<CDMKeyInformation> keys;
|
||||
keys.SetCapacity(aKeysInfoCount);
|
||||
for (uint32_t i = 0; i < aKeysInfoCount; i++) {
|
||||
const cdm::KeyInformation& key = aKeysInfo[i];
|
||||
nsTArray<uint8_t> kid;
|
||||
kid.AppendElements(key.key_id, key.key_id_size);
|
||||
keys.AppendElement(CDMKeyInformation(kid, key.status, key.system_code));
|
||||
}
|
||||
Unused << SendOnSessionKeysChange(nsCString(aSessionId, aSessionIdSize),
|
||||
keys);
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMChild::OnExpirationChange(const char* aSessionId,
|
||||
uint32_t aSessionIdSize,
|
||||
cdm::Time aNewExpiryTime)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::OnExpirationChange(sid=%s, time=%lf)",
|
||||
aSessionId,
|
||||
aNewExpiryTime);
|
||||
Unused << SendOnExpirationChange(nsCString(aSessionId, aSessionIdSize),
|
||||
aNewExpiryTime);
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMChild::OnSessionClosed(const char* aSessionId,
|
||||
uint32_t aSessionIdSize)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::OnSessionClosed(sid=%s)", aSessionId);
|
||||
Unused << SendOnSessionClosed(nsCString(aSessionId, aSessionIdSize));
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMChild::OnLegacySessionError(const char* aSessionId,
|
||||
uint32_t aSessionIdLength,
|
||||
cdm::Error aError,
|
||||
uint32_t aSystemCode,
|
||||
const char* aErrorMessage,
|
||||
uint32_t aErrorMessageLength)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::OnLegacySessionError(sid=%s, error=%" PRIu32
|
||||
" msg='%s')",
|
||||
aSessionId,
|
||||
aError,
|
||||
aErrorMessage);
|
||||
Unused << SendOnLegacySessionError(
|
||||
nsCString(aSessionId, aSessionIdLength),
|
||||
static_cast<uint32_t>(aError),
|
||||
aSystemCode,
|
||||
nsCString(aErrorMessage, aErrorMessageLength));
|
||||
}
|
||||
|
||||
cdm::FileIO*
|
||||
ChromiumCDMChild::CreateFileIO(cdm::FileIOClient * aClient)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::CreateFileIO()");
|
||||
if (!mPersistentStateAllowed) {
|
||||
return nullptr;
|
||||
}
|
||||
return new WidevineFileIO(aClient);
|
||||
}
|
||||
|
||||
bool
|
||||
ChromiumCDMChild::IsOnMessageLoopThread()
|
||||
{
|
||||
return mPlugin && mPlugin->GMPMessageLoop() == MessageLoop::current();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ChromiumCDMChild::RecvInit(const bool& aAllowDistinctiveIdentifier,
|
||||
const bool& aAllowPersistentState)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::RecvInit(distinctiveId=%d, persistentState=%d)",
|
||||
aAllowDistinctiveIdentifier,
|
||||
aAllowPersistentState);
|
||||
mPersistentStateAllowed = aAllowPersistentState;
|
||||
if (mCDM) {
|
||||
mCDM->Initialize(aAllowDistinctiveIdentifier, aAllowPersistentState);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ChromiumCDMChild::RecvSetServerCertificate(const uint32_t& aPromiseId,
|
||||
nsTArray<uint8_t>&& aServerCert)
|
||||
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::RecvSetServerCertificate() certlen=%zu",
|
||||
aServerCert.Length());
|
||||
if (mCDM) {
|
||||
mCDM->SetServerCertificate(aPromiseId,
|
||||
aServerCert.Elements(),
|
||||
aServerCert.Length());
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ChromiumCDMChild::RecvCreateSessionAndGenerateRequest(
|
||||
const uint32_t& aPromiseId,
|
||||
const uint32_t& aSessionType,
|
||||
const uint32_t& aInitDataType,
|
||||
nsTArray<uint8_t>&& aInitData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::RecvCreateSessionAndGenerateRequest("
|
||||
"pid=%" PRIu32 ", sessionType=%" PRIu32 ", initDataType=%" PRIu32
|
||||
") initDataLen=%zu",
|
||||
aPromiseId,
|
||||
aSessionType,
|
||||
aInitDataType,
|
||||
aInitData.Length());
|
||||
MOZ_ASSERT(aSessionType <= cdm::SessionType::kPersistentKeyRelease);
|
||||
MOZ_ASSERT(aInitDataType <= cdm::InitDataType::kWebM);
|
||||
if (mCDM) {
|
||||
mCDM->CreateSessionAndGenerateRequest(aPromiseId,
|
||||
static_cast<cdm::SessionType>(aSessionType),
|
||||
static_cast<cdm::InitDataType>(aInitDataType),
|
||||
aInitData.Elements(),
|
||||
aInitData.Length());
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ChromiumCDMChild::RecvLoadSession(const uint32_t& aPromiseId,
|
||||
const uint32_t& aSessionType,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMChild::RecvLoadSession(pid=%u, type=%u, sessionId=%s)",
|
||||
aPromiseId,
|
||||
aSessionType,
|
||||
aSessionId.get());
|
||||
if (mCDM) {
|
||||
mLoadSessionPromiseIds.AppendElement(aPromiseId);
|
||||
mCDM->LoadSession(aPromiseId,
|
||||
static_cast<cdm::SessionType>(aSessionType),
|
||||
aSessionId.get(),
|
||||
aSessionId.Length());
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ChromiumCDMChild::RecvUpdateSession(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId,
|
||||
nsTArray<uint8_t>&& aResponse)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::RecvUpdateSession(pid=%" PRIu32
|
||||
", sid=%s) responseLen=%zu",
|
||||
aPromiseId,
|
||||
aSessionId.get(),
|
||||
aResponse.Length());
|
||||
if (mCDM) {
|
||||
mCDM->UpdateSession(aPromiseId,
|
||||
aSessionId.get(),
|
||||
aSessionId.Length(),
|
||||
aResponse.Elements(),
|
||||
aResponse.Length());
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ChromiumCDMChild::RecvCloseSession(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::RecvCloseSession(pid=%" PRIu32 ", sid=%s)",
|
||||
aPromiseId,
|
||||
aSessionId.get());
|
||||
if (mCDM) {
|
||||
mCDM->CloseSession(aPromiseId, aSessionId.get(), aSessionId.Length());
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ChromiumCDMChild::RecvRemoveSession(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::RecvRemoveSession(pid=%" PRIu32 ", sid=%s)",
|
||||
aPromiseId,
|
||||
aSessionId.get());
|
||||
if (mCDM) {
|
||||
mCDM->RemoveSession(aPromiseId, aSessionId.get(), aSessionId.Length());
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMChild::DecryptFailed(uint32_t aId, cdm::Status aStatus)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
Unused << SendDecrypted(aId, aStatus, nsTArray<uint8_t>());
|
||||
}
|
||||
|
||||
static void
|
||||
InitInputBuffer(const CDMInputBuffer& aBuffer,
|
||||
nsTArray<cdm::SubsampleEntry>& aSubSamples,
|
||||
cdm::InputBuffer& aInputBuffer)
|
||||
{
|
||||
aInputBuffer.data = aBuffer.mData().Elements();
|
||||
aInputBuffer.data_size = aBuffer.mData().Length();
|
||||
|
||||
if (aBuffer.mIsEncrypted()) {
|
||||
aInputBuffer.key_id = aBuffer.mKeyId().Elements();
|
||||
aInputBuffer.key_id_size = aBuffer.mKeyId().Length();
|
||||
|
||||
aInputBuffer.iv = aBuffer.mIV().Elements();
|
||||
aInputBuffer.iv_size = aBuffer.mIV().Length();
|
||||
|
||||
aSubSamples.SetCapacity(aBuffer.mClearBytes().Length());
|
||||
for (size_t i = 0; i < aBuffer.mCipherBytes().Length(); i++) {
|
||||
aSubSamples.AppendElement(cdm::SubsampleEntry(aBuffer.mClearBytes()[i],
|
||||
aBuffer.mCipherBytes()[i]));
|
||||
}
|
||||
aInputBuffer.subsamples = aSubSamples.Elements();
|
||||
aInputBuffer.num_subsamples = aSubSamples.Length();
|
||||
}
|
||||
aInputBuffer.timestamp = aBuffer.mTimestamp();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ChromiumCDMChild::RecvDecrypt(const uint32_t& aId,
|
||||
const CDMInputBuffer& aBuffer)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::RecvDecrypt()");
|
||||
if (!mCDM) {
|
||||
GMP_LOG("ChromiumCDMChild::RecvDecrypt() no CDM");
|
||||
DecryptFailed(aId, cdm::kDecryptError);
|
||||
return IPC_OK();
|
||||
}
|
||||
if (aBuffer.mClearBytes().Length() != aBuffer.mCipherBytes().Length()) {
|
||||
GMP_LOG("ChromiumCDMChild::RecvDecrypt() clear/cipher bytes length doesn't "
|
||||
"match");
|
||||
DecryptFailed(aId, cdm::kDecryptError);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
cdm::InputBuffer input;
|
||||
nsTArray<cdm::SubsampleEntry> subsamples;
|
||||
InitInputBuffer(aBuffer, subsamples, input);
|
||||
|
||||
WidevineDecryptedBlock output;
|
||||
cdm::Status status = mCDM->Decrypt(input, &output);
|
||||
|
||||
if (status != cdm::kSuccess) {
|
||||
DecryptFailed(aId, status);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
if (!output.DecryptedBuffer() ||
|
||||
output.DecryptedBuffer()->Size() != aBuffer.mData().Length()) {
|
||||
// The sizes of the input and output should exactly match.
|
||||
DecryptFailed(aId, cdm::kDecryptError);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> buf =
|
||||
static_cast<WidevineBuffer*>(output.DecryptedBuffer())->ExtractBuffer();
|
||||
Unused << SendDecrypted(aId, cdm::kSuccess, buf);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ChromiumCDMChild::RecvInitializeVideoDecoder(
|
||||
const CDMVideoDecoderConfig& aConfig)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
MOZ_ASSERT(!mDecoderInitialized);
|
||||
cdm::VideoDecoderConfig config;
|
||||
config.codec =
|
||||
static_cast<cdm::VideoDecoderConfig::VideoCodec>(aConfig.mCodec());
|
||||
config.profile =
|
||||
static_cast<cdm::VideoDecoderConfig::VideoCodecProfile>(aConfig.mProfile());
|
||||
config.format = static_cast<cdm::VideoFormat>(aConfig.mFormat());
|
||||
config.coded_size =
|
||||
mCodedSize = { aConfig.mImageWidth(), aConfig.mImageHeight() };
|
||||
nsTArray<uint8_t> extraData(aConfig.mExtraData());
|
||||
config.extra_data = extraData.Elements();
|
||||
config.extra_data_size = extraData.Length();
|
||||
cdm::Status status = mCDM->InitializeVideoDecoder(config);
|
||||
GMP_LOG("ChromiumCDMChild::RecvInitializeVideoDecoder() status=%u", status);
|
||||
Unused << SendOnDecoderInitDone(status);
|
||||
mDecoderInitialized = status == cdm::kSuccess;
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ChromiumCDMChild::RecvDeinitializeVideoDecoder()
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::RecvDeinitializeVideoDecoder()");
|
||||
MOZ_ASSERT(mDecoderInitialized);
|
||||
if (mDecoderInitialized) {
|
||||
mDecoderInitialized = false;
|
||||
mCDM->DeinitializeDecoder(cdm::kStreamTypeVideo);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ChromiumCDMChild::RecvResetVideoDecoder()
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::RecvResetVideoDecoder()");
|
||||
if (mDecoderInitialized) {
|
||||
mCDM->ResetDecoder(cdm::kStreamTypeVideo);
|
||||
}
|
||||
Unused << SendResetVideoDecoderComplete();
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ChromiumCDMChild::RecvDecryptAndDecodeFrame(const CDMInputBuffer& aBuffer)
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::RecvDecryptAndDecodeFrame()");
|
||||
MOZ_ASSERT(mDecoderInitialized);
|
||||
|
||||
// The output frame may not have the same timestamp as the frame we put in.
|
||||
// We may need to input a number of frames before we receive output. The
|
||||
// CDM's decoder reorders to ensure frames output are in presentation order.
|
||||
// So we need to store the durations of the frames input, and retrieve them
|
||||
// on output.
|
||||
mFrameDurations.Insert(aBuffer.mTimestamp(), aBuffer.mDuration());
|
||||
|
||||
cdm::InputBuffer input;
|
||||
nsTArray<cdm::SubsampleEntry> subsamples;
|
||||
InitInputBuffer(aBuffer, subsamples, input);
|
||||
|
||||
WidevineVideoFrame frame;
|
||||
cdm::Status rv = mCDM->DecryptAndDecodeFrame(input, &frame);
|
||||
GMP_LOG("WidevineVideoDecoder::Decode(timestamp=%" PRId64 ") rv=%d",
|
||||
input.timestamp,
|
||||
rv);
|
||||
|
||||
switch (rv) {
|
||||
case cdm::kNoKey:
|
||||
GMP_LOG("NoKey for sample at time=%" PRId64 "!", input.timestamp);
|
||||
// Somehow our key became unusable. Typically this would happen when
|
||||
// a stream requires output protection, and the configuration changed
|
||||
// such that output protection is no longer available. For example, a
|
||||
// non-compliant monitor was attached. The JS player should notice the
|
||||
// key status changing to "output-restricted", and is supposed to switch
|
||||
// to a stream that doesn't require OP. In order to keep the playback
|
||||
// pipeline rolling, just output a black frame. See bug 1343140.
|
||||
frame.InitToBlack(mCodedSize.width, mCodedSize.height, input.timestamp);
|
||||
MOZ_FALLTHROUGH;
|
||||
case cdm::kSuccess:
|
||||
ReturnOutput(frame);
|
||||
break;
|
||||
case cdm::kNeedMoreData:
|
||||
Unused << SendDecoded(gmp::CDMVideoFrame());
|
||||
break;
|
||||
default:
|
||||
Unused << SendDecodeFailed(rv);
|
||||
break;
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMChild::ReturnOutput(WidevineVideoFrame& aFrame)
|
||||
{
|
||||
// TODO: WidevineBuffers should hold a shmem instead of a array, and we can
|
||||
// send the handle instead of copying the array here.
|
||||
gmp::CDMVideoFrame output;
|
||||
output.mFormat() = static_cast<cdm::VideoFormat>(aFrame.Format());
|
||||
output.mImageWidth() = aFrame.Size().width;
|
||||
output.mImageHeight() = aFrame.Size().height;
|
||||
output.mData() = Move(
|
||||
reinterpret_cast<WidevineBuffer*>(aFrame.FrameBuffer())->ExtractBuffer());
|
||||
output.mYPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kYPlane),
|
||||
aFrame.Stride(cdm::VideoFrame::kYPlane) };
|
||||
output.mUPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kUPlane),
|
||||
aFrame.Stride(cdm::VideoFrame::kUPlane) };
|
||||
output.mVPlane() = { aFrame.PlaneOffset(cdm::VideoFrame::kVPlane),
|
||||
aFrame.Stride(cdm::VideoFrame::kVPlane) };
|
||||
output.mTimestamp() = aFrame.Timestamp();
|
||||
|
||||
uint64_t duration = 0;
|
||||
if (mFrameDurations.Find(aFrame.Timestamp(), duration)) {
|
||||
output.mDuration() = duration;
|
||||
}
|
||||
|
||||
Unused << SendDecoded(output);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ChromiumCDMChild::RecvDrain()
|
||||
{
|
||||
WidevineVideoFrame frame;
|
||||
cdm::InputBuffer sample;
|
||||
cdm::Status rv = mCDM->DecryptAndDecodeFrame(sample, &frame);
|
||||
CDM_LOG("ChromiumCDMChild::RecvDrain(); DecryptAndDecodeFrame() rv=%d", rv);
|
||||
if (rv == cdm::kSuccess) {
|
||||
MOZ_ASSERT(frame.Format() != cdm::kUnknownVideoFormat);
|
||||
ReturnOutput(frame);
|
||||
} else {
|
||||
Unused << SendDrainComplete();
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
ChromiumCDMChild::RecvDestroy()
|
||||
{
|
||||
MOZ_ASSERT(IsOnMessageLoopThread());
|
||||
GMP_LOG("ChromiumCDMChild::RecvDestroy()");
|
||||
|
||||
MOZ_ASSERT(!mDecoderInitialized);
|
||||
|
||||
if (mCDM) {
|
||||
mCDM->Destroy();
|
||||
mCDM = nullptr;
|
||||
}
|
||||
|
||||
Unused << Send__delete__(this);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
131
dom/media/gmp/ChromiumCDMChild.h
Normal file
131
dom/media/gmp/ChromiumCDMChild.h
Normal file
@ -0,0 +1,131 @@
|
||||
/* -*- 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 ChromiumCDMChild_h_
|
||||
#define ChromiumCDMChild_h_
|
||||
|
||||
#include "content_decryption_module.h"
|
||||
#include "mozilla/gmp/PChromiumCDMChild.h"
|
||||
#include "SimpleMap.h"
|
||||
#include "WidevineVideoFrame.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
||||
class GMPContentChild;
|
||||
|
||||
class ChromiumCDMChild : public PChromiumCDMChild
|
||||
, public cdm::Host_8
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ChromiumCDMChild);
|
||||
|
||||
explicit ChromiumCDMChild(GMPContentChild* aPlugin);
|
||||
|
||||
void Init(cdm::ContentDecryptionModule_8* aCDM);
|
||||
|
||||
void TimerExpired(void* aContext);
|
||||
|
||||
// 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 aSessionIdLength,
|
||||
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;
|
||||
protected:
|
||||
~ChromiumCDMChild() {}
|
||||
|
||||
bool IsOnMessageLoopThread();
|
||||
|
||||
ipc::IPCResult RecvInit(const bool& aAllowDistinctiveIdentifier,
|
||||
const bool& aAllowPersistentState) override;
|
||||
ipc::IPCResult RecvSetServerCertificate(
|
||||
const uint32_t& aPromiseId,
|
||||
nsTArray<uint8_t>&& aServerCert) override;
|
||||
ipc::IPCResult RecvCreateSessionAndGenerateRequest(
|
||||
const uint32_t& aPromiseId,
|
||||
const uint32_t& aSessionType,
|
||||
const uint32_t& aInitDataType,
|
||||
nsTArray<uint8_t>&& aInitData) override;
|
||||
ipc::IPCResult RecvLoadSession(const uint32_t& aPromiseId,
|
||||
const uint32_t& aSessionType,
|
||||
const nsCString& aSessionId) override;
|
||||
ipc::IPCResult RecvUpdateSession(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId,
|
||||
nsTArray<uint8_t>&& aResponse) override;
|
||||
ipc::IPCResult RecvCloseSession(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId) override;
|
||||
ipc::IPCResult RecvRemoveSession(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId) override;
|
||||
ipc::IPCResult RecvDecrypt(const uint32_t& aId,
|
||||
const CDMInputBuffer& aBuffer) override;
|
||||
ipc::IPCResult RecvInitializeVideoDecoder(
|
||||
const CDMVideoDecoderConfig& aConfig) override;
|
||||
ipc::IPCResult RecvDeinitializeVideoDecoder() override;
|
||||
ipc::IPCResult RecvResetVideoDecoder() override;
|
||||
ipc::IPCResult RecvDecryptAndDecodeFrame(
|
||||
const CDMInputBuffer& aBuffer) override;
|
||||
ipc::IPCResult RecvDrain() override;
|
||||
ipc::IPCResult RecvDestroy() override;
|
||||
|
||||
void DecryptFailed(uint32_t aId, cdm::Status aStatus);
|
||||
void ReturnOutput(WidevineVideoFrame& aFrame);
|
||||
|
||||
GMPContentChild* mPlugin = nullptr;
|
||||
cdm::ContentDecryptionModule_8* mCDM = nullptr;
|
||||
|
||||
typedef SimpleMap<uint64_t> DurationMap;
|
||||
DurationMap mFrameDurations;
|
||||
nsTArray<uint32_t> mLoadSessionPromiseIds;
|
||||
|
||||
cdm::Size mCodedSize;
|
||||
|
||||
bool mDecoderInitialized = false;
|
||||
bool mPersistentStateAllowed = false;
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ChromiumCDMChild_h_
|
850
dom/media/gmp/ChromiumCDMParent.cpp
Normal file
850
dom/media/gmp/ChromiumCDMParent.cpp
Normal file
@ -0,0 +1,850 @@
|
||||
/* -*- 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 "ChromiumCDMParent.h"
|
||||
#include "mozilla/gmp/GMPTypes.h"
|
||||
#include "GMPContentChild.h"
|
||||
#include "GMPContentParent.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "ChromiumCDMProxy.h"
|
||||
#include "mozilla/dom/MediaKeyMessageEventBinding.h"
|
||||
#include "content_decryption_module.h"
|
||||
#include "GMPLog.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
||||
ChromiumCDMParent::ChromiumCDMParent(GMPContentParent* aContentParent,
|
||||
uint32_t aPluginId)
|
||||
: mPluginId(aPluginId)
|
||||
, mContentParent(aContentParent)
|
||||
{
|
||||
GMP_LOG(
|
||||
"ChromiumCDMParent::ChromiumCDMParent(this=%p, contentParent=%p, id=%u)",
|
||||
this,
|
||||
aContentParent,
|
||||
aPluginId);
|
||||
}
|
||||
|
||||
bool
|
||||
ChromiumCDMParent::Init(ChromiumCDMProxy* aProxy,
|
||||
bool aAllowDistinctiveIdentifier,
|
||||
bool aAllowPersistentState)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::Init(this=%p)", this);
|
||||
if (!aProxy) {
|
||||
return false;
|
||||
}
|
||||
mProxy = aProxy;
|
||||
return SendInit(aAllowDistinctiveIdentifier, aAllowPersistentState);
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMParent::CreateSession(uint32_t aCreateSessionToken,
|
||||
uint32_t aSessionType,
|
||||
uint32_t aInitDataType,
|
||||
uint32_t aPromiseId,
|
||||
const nsTArray<uint8_t>& aInitData)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::CreateSession(this=%p)", this);
|
||||
if (mIsShutdown) {
|
||||
RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("CDM is shutdown."));
|
||||
return;
|
||||
}
|
||||
if (!SendCreateSessionAndGenerateRequest(
|
||||
aPromiseId, aSessionType, aInitDataType, aInitData)) {
|
||||
RejectPromise(
|
||||
aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Failed to send generateRequest to CDM process."));
|
||||
return;
|
||||
}
|
||||
mPromiseToCreateSessionToken.Put(aPromiseId, aCreateSessionToken);
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMParent::LoadSession(uint32_t aPromiseId,
|
||||
uint32_t aSessionType,
|
||||
nsString aSessionId)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::LoadSession(this=%p, pid=%u, type=%u, sid=%s)",
|
||||
this,
|
||||
aPromiseId,
|
||||
aSessionType,
|
||||
NS_ConvertUTF16toUTF8(aSessionId).get());
|
||||
if (mIsShutdown) {
|
||||
RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("CDM is shutdown."));
|
||||
return;
|
||||
}
|
||||
if (!SendLoadSession(
|
||||
aPromiseId, aSessionType, NS_ConvertUTF16toUTF8(aSessionId))) {
|
||||
RejectPromise(
|
||||
aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Failed to send loadSession to CDM process."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMParent::SetServerCertificate(uint32_t aPromiseId,
|
||||
const nsTArray<uint8_t>& aCert)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::SetServerCertificate(this=%p)", this);
|
||||
if (mIsShutdown) {
|
||||
RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("CDM is shutdown."));
|
||||
return;
|
||||
}
|
||||
if (!SendSetServerCertificate(aPromiseId, aCert)) {
|
||||
RejectPromise(
|
||||
aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Failed to send setServerCertificate to CDM process"));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMParent::UpdateSession(const nsCString& aSessionId,
|
||||
uint32_t aPromiseId,
|
||||
const nsTArray<uint8_t>& aResponse)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::UpdateSession(this=%p)", this);
|
||||
if (mIsShutdown) {
|
||||
RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("CDM is shutdown."));
|
||||
return;
|
||||
}
|
||||
if (!SendUpdateSession(aPromiseId, aSessionId, aResponse)) {
|
||||
RejectPromise(
|
||||
aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Failed to send updateSession to CDM process"));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMParent::CloseSession(const nsCString& aSessionId,
|
||||
uint32_t aPromiseId)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::CloseSession(this=%p)", this);
|
||||
if (mIsShutdown) {
|
||||
RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("CDM is shutdown."));
|
||||
return;
|
||||
}
|
||||
if (!SendCloseSession(aPromiseId, aSessionId)) {
|
||||
RejectPromise(
|
||||
aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Failed to send closeSession to CDM process"));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMParent::RemoveSession(const nsCString& aSessionId,
|
||||
uint32_t aPromiseId)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::RemoveSession(this=%p)", this);
|
||||
if (mIsShutdown) {
|
||||
RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("CDM is shutdown."));
|
||||
return;
|
||||
}
|
||||
if (!SendRemoveSession(aPromiseId, aSessionId)) {
|
||||
RejectPromise(
|
||||
aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Failed to send removeSession to CDM process"));
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
InitCDMInputBuffer(gmp::CDMInputBuffer& aBuffer, MediaRawData* aSample)
|
||||
{
|
||||
const CryptoSample& crypto = aSample->mCrypto;
|
||||
if (crypto.mEncryptedSizes.Length() != crypto.mPlainSizes.Length()) {
|
||||
GMP_LOG("InitCDMInputBuffer clear/cipher subsamples don't match");
|
||||
return false;
|
||||
}
|
||||
|
||||
nsTArray<uint8_t> data;
|
||||
data.AppendElements(aSample->Data(), aSample->Size());
|
||||
|
||||
aBuffer = gmp::CDMInputBuffer(data,
|
||||
crypto.mKeyId,
|
||||
crypto.mIV,
|
||||
aSample->mTime,
|
||||
aSample->mDuration,
|
||||
crypto.mPlainSizes,
|
||||
crypto.mEncryptedSizes,
|
||||
crypto.mValid);
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<DecryptPromise>
|
||||
ChromiumCDMParent::Decrypt(MediaRawData* aSample)
|
||||
{
|
||||
if (mIsShutdown) {
|
||||
return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample),
|
||||
__func__);
|
||||
}
|
||||
CDMInputBuffer buffer;
|
||||
if (!InitCDMInputBuffer(buffer, aSample)) {
|
||||
return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample),
|
||||
__func__);
|
||||
}
|
||||
RefPtr<DecryptJob> job = new DecryptJob(aSample);
|
||||
if (!SendDecrypt(job->mId, buffer)) {
|
||||
GMP_LOG(
|
||||
"ChromiumCDMParent::Decrypt(this=%p) failed to send decrypt message",
|
||||
this);
|
||||
return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample),
|
||||
__func__);
|
||||
}
|
||||
RefPtr<DecryptPromise> promise = job->Ensure();
|
||||
mDecrypts.AppendElement(job);
|
||||
return promise;
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::Recv__delete__()
|
||||
{
|
||||
MOZ_ASSERT(mIsShutdown);
|
||||
GMP_LOG("ChromiumCDMParent::Recv__delete__(this=%p)", this);
|
||||
if (mContentParent) {
|
||||
mContentParent->ChromiumCDMDestroyed(this);
|
||||
mContentParent = nullptr;
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvOnResolveNewSessionPromise(const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::RecvOnResolveNewSessionPromise(this=%p, pid=%u, "
|
||||
"sid=%s)",
|
||||
this,
|
||||
aPromiseId,
|
||||
aSessionId.get());
|
||||
if (!mProxy || mIsShutdown) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
Maybe<uint32_t> token = mPromiseToCreateSessionToken.GetAndRemove(aPromiseId);
|
||||
if (token.isNothing()) {
|
||||
RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Lost session token for new session."));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
RefPtr<Runnable> task =
|
||||
NewRunnableMethod<uint32_t, nsString>(mProxy,
|
||||
&ChromiumCDMProxy::OnSetSessionId,
|
||||
token.value(),
|
||||
NS_ConvertUTF8toUTF16(aSessionId));
|
||||
NS_DispatchToMainThread(task);
|
||||
|
||||
ResolvePromise(aPromiseId);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
|
||||
const bool& aSuccessful)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::RecvResolveLoadSessionPromise(this=%p, pid=%u, "
|
||||
"successful=%d)",
|
||||
this,
|
||||
aPromiseId,
|
||||
aSuccessful);
|
||||
if (!mProxy || mIsShutdown) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(NewRunnableMethod<uint32_t, bool>(
|
||||
mProxy,
|
||||
&ChromiumCDMProxy::OnResolveLoadSessionPromise,
|
||||
aPromiseId,
|
||||
aSuccessful));
|
||||
return IPC_OK();
|
||||
}
|
||||
void
|
||||
ChromiumCDMParent::ResolvePromise(uint32_t aPromiseId)
|
||||
{
|
||||
GMP_LOG(
|
||||
"ChromiumCDMParent::ResolvePromise(this=%p, pid=%u)", this, aPromiseId);
|
||||
|
||||
// Note: The MediaKeys rejects all pending DOM promises when it
|
||||
// initiates shutdown.
|
||||
if (!mProxy || mIsShutdown) {
|
||||
return;
|
||||
}
|
||||
NS_DispatchToMainThread(NewRunnableMethod<uint32_t>(
|
||||
mProxy, &ChromiumCDMProxy::ResolvePromise, aPromiseId));
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvOnResolvePromise(const uint32_t& aPromiseId)
|
||||
{
|
||||
ResolvePromise(aPromiseId);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
static nsresult
|
||||
ToNsresult(uint32_t aError)
|
||||
{
|
||||
switch (static_cast<cdm::Error>(aError)) {
|
||||
case cdm::kNotSupportedError:
|
||||
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
||||
case cdm::kInvalidStateError:
|
||||
return NS_ERROR_DOM_INVALID_STATE_ERR;
|
||||
case cdm::kInvalidAccessError:
|
||||
// Note: Chrome converts kInvalidAccessError to TypeError, since the
|
||||
// Chromium CDM API doesn't have a type error enum value. The EME spec
|
||||
// requires TypeError in some places, so we do the same conversion.
|
||||
// See bug 1313202.
|
||||
return NS_ERROR_DOM_TYPE_ERR;
|
||||
case cdm::kQuotaExceededError:
|
||||
return NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
|
||||
case cdm::kUnknownError:
|
||||
return NS_ERROR_DOM_UNKNOWN_ERR; // Note: Unique placeholder.
|
||||
case cdm::kClientError:
|
||||
return NS_ERROR_DOM_ABORT_ERR; // Note: Unique placeholder.
|
||||
case cdm::kOutputError:
|
||||
return NS_ERROR_DOM_SECURITY_ERR; // Note: Unique placeholder.
|
||||
};
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid cdm::Error enum value.");
|
||||
return NS_ERROR_DOM_TIMEOUT_ERR; // Note: Unique placeholder.
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMParent::RejectPromise(uint32_t aPromiseId,
|
||||
nsresult aError,
|
||||
const nsCString& aErrorMessage)
|
||||
{
|
||||
GMP_LOG(
|
||||
"ChromiumCDMParent::RejectPromise(this=%p, pid=%u)", this, aPromiseId);
|
||||
// Note: The MediaKeys rejects all pending DOM promises when it
|
||||
// initiates shutdown.
|
||||
if (!mProxy || mIsShutdown) {
|
||||
return;
|
||||
}
|
||||
NS_DispatchToMainThread(NewRunnableMethod<uint32_t, nsresult, nsCString>(
|
||||
mProxy,
|
||||
&ChromiumCDMProxy::RejectPromise,
|
||||
aPromiseId,
|
||||
aError,
|
||||
aErrorMessage));
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvOnRejectPromise(const uint32_t& aPromiseId,
|
||||
const uint32_t& aError,
|
||||
const uint32_t& aSystemCode,
|
||||
const nsCString& aErrorMessage)
|
||||
{
|
||||
RejectPromise(aPromiseId, ToNsresult(aError), aErrorMessage);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
static dom::MediaKeyMessageType
|
||||
ToDOMMessageType(uint32_t aMessageType)
|
||||
{
|
||||
switch (static_cast<cdm::MessageType>(aMessageType)) {
|
||||
case cdm::kLicenseRequest:
|
||||
return dom::MediaKeyMessageType::License_request;
|
||||
case cdm::kLicenseRenewal:
|
||||
return dom::MediaKeyMessageType::License_renewal;
|
||||
case cdm::kLicenseRelease:
|
||||
return dom::MediaKeyMessageType::License_release;
|
||||
}
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid cdm::MessageType enum value.");
|
||||
return dom::MediaKeyMessageType::License_request;
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvOnSessionMessage(const nsCString& aSessionId,
|
||||
const uint32_t& aMessageType,
|
||||
nsTArray<uint8_t>&& aMessage)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::RecvOnSessionMessage(this=%p, sid=%s)",
|
||||
this,
|
||||
aSessionId.get());
|
||||
if (!mProxy || mIsShutdown) {
|
||||
return IPC_OK();
|
||||
}
|
||||
RefPtr<CDMProxy> proxy = mProxy;
|
||||
nsString sid = NS_ConvertUTF8toUTF16(aSessionId);
|
||||
dom::MediaKeyMessageType messageType = ToDOMMessageType(aMessageType);
|
||||
nsTArray<uint8_t> msg(Move(aMessage));
|
||||
NS_DispatchToMainThread(
|
||||
NS_NewRunnableFunction([proxy, sid, messageType, msg]() mutable {
|
||||
proxy->OnSessionMessage(sid, messageType, msg);
|
||||
}));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
static dom::MediaKeyStatus
|
||||
ToDOMMediaKeyStatus(uint32_t aStatus)
|
||||
{
|
||||
switch (static_cast<cdm::KeyStatus>(aStatus)) {
|
||||
case cdm::kUsable:
|
||||
return dom::MediaKeyStatus::Usable;
|
||||
case cdm::kInternalError:
|
||||
return dom::MediaKeyStatus::Internal_error;
|
||||
case cdm::kExpired:
|
||||
return dom::MediaKeyStatus::Expired;
|
||||
case cdm::kOutputRestricted:
|
||||
return dom::MediaKeyStatus::Output_restricted;
|
||||
case cdm::kOutputDownscaled:
|
||||
return dom::MediaKeyStatus::Output_downscaled;
|
||||
case cdm::kStatusPending:
|
||||
return dom::MediaKeyStatus::Status_pending;
|
||||
case cdm::kReleased:
|
||||
return dom::MediaKeyStatus::Released;
|
||||
}
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid cdm::KeyStatus enum value.");
|
||||
return dom::MediaKeyStatus::Internal_error;
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvOnSessionKeysChange(
|
||||
const nsCString& aSessionId,
|
||||
nsTArray<CDMKeyInformation>&& aKeysInfo)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::RecvOnSessionKeysChange(this=%p)", this);
|
||||
if (!mProxy || mIsShutdown) {
|
||||
return IPC_OK();
|
||||
}
|
||||
bool keyStatusesChange = false;
|
||||
{
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
for (size_t i = 0; i < aKeysInfo.Length(); i++) {
|
||||
keyStatusesChange |=
|
||||
caps.SetKeyStatus(aKeysInfo[i].mKeyId(),
|
||||
NS_ConvertUTF8toUTF16(aSessionId),
|
||||
dom::Optional<dom::MediaKeyStatus>(
|
||||
ToDOMMediaKeyStatus(aKeysInfo[i].mStatus())));
|
||||
}
|
||||
}
|
||||
if (keyStatusesChange) {
|
||||
NS_DispatchToMainThread(
|
||||
NewRunnableMethod<nsString>(mProxy,
|
||||
&ChromiumCDMProxy::OnKeyStatusesChange,
|
||||
NS_ConvertUTF8toUTF16(aSessionId)));
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvOnExpirationChange(const nsCString& aSessionId,
|
||||
const double& aSecondsSinceEpoch)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::RecvOnExpirationChange(this=%p) time=%lf",
|
||||
this,
|
||||
aSecondsSinceEpoch);
|
||||
if (!mProxy || mIsShutdown) {
|
||||
return IPC_OK();
|
||||
}
|
||||
NS_DispatchToMainThread(NewRunnableMethod<nsString, UnixTime>(
|
||||
mProxy,
|
||||
&ChromiumCDMProxy::OnExpirationChange,
|
||||
NS_ConvertUTF8toUTF16(aSessionId),
|
||||
GMPTimestamp(aSecondsSinceEpoch * 1000)));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvOnSessionClosed(const nsCString& aSessionId)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::RecvOnSessionClosed(this=%p)", this);
|
||||
if (!mProxy || mIsShutdown) {
|
||||
return IPC_OK();
|
||||
}
|
||||
NS_DispatchToMainThread(
|
||||
NewRunnableMethod<nsString>(mProxy,
|
||||
&ChromiumCDMProxy::OnSessionClosed,
|
||||
NS_ConvertUTF8toUTF16(aSessionId)));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvOnLegacySessionError(const nsCString& aSessionId,
|
||||
const uint32_t& aError,
|
||||
const uint32_t& aSystemCode,
|
||||
const nsCString& aMessage)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::RecvOnLegacySessionError(this=%p)", this);
|
||||
if (!mProxy || mIsShutdown) {
|
||||
return IPC_OK();
|
||||
}
|
||||
NS_DispatchToMainThread(
|
||||
NewRunnableMethod<nsString, nsresult, uint32_t, nsString>(
|
||||
mProxy,
|
||||
&ChromiumCDMProxy::OnSessionError,
|
||||
NS_ConvertUTF8toUTF16(aSessionId),
|
||||
ToNsresult(aError),
|
||||
aSystemCode,
|
||||
NS_ConvertUTF8toUTF16(aMessage)));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
DecryptStatus
|
||||
ToDecryptStatus(uint32_t aError)
|
||||
{
|
||||
switch (static_cast<cdm::Status>(aError)) {
|
||||
case cdm::kSuccess:
|
||||
return DecryptStatus::Ok;
|
||||
case cdm::kNoKey:
|
||||
return DecryptStatus::NoKeyErr;
|
||||
default:
|
||||
return DecryptStatus::GenericErr;
|
||||
}
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvDecrypted(const uint32_t& aId,
|
||||
const uint32_t& aStatus,
|
||||
nsTArray<uint8_t>&& aData)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::RecvDecrypted(this=%p, id=%u, status=%u)",
|
||||
this,
|
||||
aId,
|
||||
aStatus);
|
||||
if (mIsShutdown) {
|
||||
MOZ_ASSERT(mDecrypts.IsEmpty());
|
||||
return IPC_OK();
|
||||
}
|
||||
for (size_t i = 0; i < mDecrypts.Length(); i++) {
|
||||
if (mDecrypts[i]->mId == aId) {
|
||||
mDecrypts[i]->PostResult(ToDecryptStatus(aStatus), aData);
|
||||
mDecrypts.RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvDecoded(const CDMVideoFrame& aFrame)
|
||||
{
|
||||
if (mIsShutdown || mDecodePromise.IsEmpty()) {
|
||||
return IPC_OK();
|
||||
}
|
||||
VideoData::YCbCrBuffer b;
|
||||
nsTArray<uint8_t> data;
|
||||
data = aFrame.mData();
|
||||
|
||||
if (data.IsEmpty()) {
|
||||
mDecodePromise.ResolveIfExists(nsTArray<RefPtr<MediaData>>(), __func__);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
b.mPlanes[0].mData = data.Elements();
|
||||
b.mPlanes[0].mWidth = aFrame.mImageWidth();
|
||||
b.mPlanes[0].mHeight = aFrame.mImageHeight();
|
||||
b.mPlanes[0].mStride = aFrame.mYPlane().mStride();
|
||||
b.mPlanes[0].mOffset = aFrame.mYPlane().mPlaneOffset();
|
||||
b.mPlanes[0].mSkip = 0;
|
||||
|
||||
b.mPlanes[1].mData = data.Elements();
|
||||
b.mPlanes[1].mWidth = (aFrame.mImageWidth() + 1) / 2;
|
||||
b.mPlanes[1].mHeight = (aFrame.mImageHeight() + 1) / 2;
|
||||
b.mPlanes[1].mStride = aFrame.mUPlane().mStride();
|
||||
b.mPlanes[1].mOffset = aFrame.mUPlane().mPlaneOffset();
|
||||
b.mPlanes[1].mSkip = 0;
|
||||
|
||||
b.mPlanes[2].mData = data.Elements();
|
||||
b.mPlanes[2].mWidth = (aFrame.mImageWidth() + 1) / 2;
|
||||
b.mPlanes[2].mHeight = (aFrame.mImageHeight() + 1) / 2;
|
||||
b.mPlanes[2].mStride = aFrame.mVPlane().mStride();
|
||||
b.mPlanes[2].mOffset = aFrame.mVPlane().mPlaneOffset();
|
||||
b.mPlanes[2].mSkip = 0;
|
||||
|
||||
gfx::IntRect pictureRegion(0, 0, aFrame.mImageWidth(), aFrame.mImageHeight());
|
||||
RefPtr<VideoData> v = VideoData::CreateAndCopyData(mVideoInfo,
|
||||
mImageContainer,
|
||||
mLastStreamOffset,
|
||||
aFrame.mTimestamp(),
|
||||
aFrame.mDuration(),
|
||||
b,
|
||||
false,
|
||||
-1,
|
||||
pictureRegion);
|
||||
|
||||
RefPtr<ChromiumCDMParent> self = this;
|
||||
if (v) {
|
||||
mDecodePromise.ResolveIfExists({ Move(v) }, __func__);
|
||||
} else {
|
||||
mDecodePromise.RejectIfExists(
|
||||
MediaResult(NS_ERROR_OUT_OF_MEMORY,
|
||||
RESULT_DETAIL("CallBack::CreateAndCopyData")),
|
||||
__func__);
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvDecodeFailed(const uint32_t& aStatus)
|
||||
{
|
||||
if (mIsShutdown) {
|
||||
MOZ_ASSERT(mDecodePromise.IsEmpty());
|
||||
return IPC_OK();
|
||||
}
|
||||
mDecodePromise.RejectIfExists(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
RESULT_DETAIL("ChromiumCDMParent::RecvDecodeFailed")),
|
||||
__func__);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvShutdown()
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::RecvShutdown(this=%p)", this);
|
||||
Shutdown();
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::ActorDestroy(this=%p, reason=%d)", this, aWhy);
|
||||
MOZ_ASSERT(!mActorDestroyed);
|
||||
mActorDestroyed = true;
|
||||
if (!mIsShutdown) {
|
||||
// Plugin crash.
|
||||
MOZ_ASSERT(aWhy == AbnormalShutdown);
|
||||
Shutdown();
|
||||
}
|
||||
MOZ_ASSERT(mIsShutdown);
|
||||
RefPtr<ChromiumCDMParent> kungFuDeathGrip(this);
|
||||
if (mContentParent) {
|
||||
mContentParent->ChromiumCDMDestroyed(this);
|
||||
mContentParent = nullptr;
|
||||
}
|
||||
bool abnormalShutdown = (aWhy == AbnormalShutdown);
|
||||
if (abnormalShutdown && mProxy) {
|
||||
RefPtr<Runnable> task =
|
||||
NewRunnableMethod(mProxy, &ChromiumCDMProxy::Terminated);
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
MaybeDisconnect(abnormalShutdown);
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder::InitPromise>
|
||||
ChromiumCDMParent::InitializeVideoDecoder(
|
||||
const gmp::CDMVideoDecoderConfig& aConfig,
|
||||
const VideoInfo& aInfo,
|
||||
RefPtr<layers::ImageContainer> aImageContainer)
|
||||
{
|
||||
if (mIsShutdown) {
|
||||
return MediaDataDecoder::InitPromise::CreateAndReject(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
RESULT_DETAIL("ChromiumCDMParent is shutdown")),
|
||||
__func__);
|
||||
}
|
||||
|
||||
if (!SendInitializeVideoDecoder(aConfig)) {
|
||||
return MediaDataDecoder::InitPromise::CreateAndReject(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
RESULT_DETAIL("Failed to send init video decoder to CDM")),
|
||||
__func__);
|
||||
}
|
||||
|
||||
mVideoDecoderInitialized = true;
|
||||
mImageContainer = aImageContainer;
|
||||
mVideoInfo = aInfo;
|
||||
|
||||
return mInitVideoDecoderPromise.Ensure(__func__);
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvOnDecoderInitDone(const uint32_t& aStatus)
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::RecvOnDecoderInitDone(this=%p, status=%u)",
|
||||
this,
|
||||
aStatus);
|
||||
if (mIsShutdown) {
|
||||
MOZ_ASSERT(mInitVideoDecoderPromise.IsEmpty());
|
||||
return IPC_OK();
|
||||
}
|
||||
if (aStatus == static_cast<uint32_t>(cdm::kSuccess)) {
|
||||
mInitVideoDecoderPromise.ResolveIfExists(TrackInfo::kVideoTrack, __func__);
|
||||
} else {
|
||||
mVideoDecoderInitialized = false;
|
||||
mInitVideoDecoderPromise.RejectIfExists(
|
||||
MediaResult(
|
||||
NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
RESULT_DETAIL("CDM init decode failed with %" PRIu32, aStatus)),
|
||||
__func__);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder::DecodePromise>
|
||||
ChromiumCDMParent::DecryptAndDecodeFrame(MediaRawData* aSample)
|
||||
{
|
||||
if (mIsShutdown) {
|
||||
return MediaDataDecoder::DecodePromise::CreateAndReject(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
RESULT_DETAIL("ChromiumCDMParent is shutdown")),
|
||||
__func__);
|
||||
}
|
||||
|
||||
CDMInputBuffer buffer;
|
||||
|
||||
if (!InitCDMInputBuffer(buffer, aSample)) {
|
||||
return MediaDataDecoder::DecodePromise::CreateAndReject(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, "Failed to init CDM buffer."),
|
||||
__func__);
|
||||
}
|
||||
|
||||
mLastStreamOffset = aSample->mOffset;
|
||||
|
||||
if (!SendDecryptAndDecodeFrame(buffer)) {
|
||||
GMP_LOG(
|
||||
"ChromiumCDMParent::Decrypt(this=%p) failed to send decrypt message.",
|
||||
this);
|
||||
return MediaDataDecoder::DecodePromise::CreateAndReject(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
"Failed to send decrypt to CDM process."),
|
||||
__func__);
|
||||
}
|
||||
|
||||
return mDecodePromise.Ensure(__func__);
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder::FlushPromise>
|
||||
ChromiumCDMParent::FlushVideoDecoder()
|
||||
{
|
||||
if (mIsShutdown) {
|
||||
return MediaDataDecoder::FlushPromise::CreateAndReject(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
RESULT_DETAIL("ChromiumCDMParent is shutdown")),
|
||||
__func__);
|
||||
}
|
||||
|
||||
mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
|
||||
if (!SendResetVideoDecoder()) {
|
||||
return MediaDataDecoder::FlushPromise::CreateAndReject(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, "Failed to send flush to CDM."),
|
||||
__func__);
|
||||
}
|
||||
return mFlushDecoderPromise.Ensure(__func__);
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvResetVideoDecoderComplete()
|
||||
{
|
||||
if (mIsShutdown) {
|
||||
MOZ_ASSERT(mFlushDecoderPromise.IsEmpty());
|
||||
return IPC_OK();
|
||||
}
|
||||
mFlushDecoderPromise.ResolveIfExists(true, __func__);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder::DecodePromise>
|
||||
ChromiumCDMParent::Drain()
|
||||
{
|
||||
MOZ_ASSERT(mDecodePromise.IsEmpty(), "Must wait for decoding to complete");
|
||||
if (mIsShutdown) {
|
||||
return MediaDataDecoder::DecodePromise::CreateAndReject(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
RESULT_DETAIL("ChromiumCDMParent is shutdown")),
|
||||
__func__);
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder::DecodePromise> p = mDecodePromise.Ensure(__func__);
|
||||
if (!SendDrain()) {
|
||||
mDecodePromise.Resolve(MediaDataDecoder::DecodedData(), __func__);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
ipc::IPCResult
|
||||
ChromiumCDMParent::RecvDrainComplete()
|
||||
{
|
||||
if (mIsShutdown) {
|
||||
MOZ_ASSERT(mDecodePromise.IsEmpty());
|
||||
return IPC_OK();
|
||||
}
|
||||
mDecodePromise.ResolveIfExists(MediaDataDecoder::DecodedData(), __func__);
|
||||
return IPC_OK();
|
||||
}
|
||||
RefPtr<ShutdownPromise>
|
||||
ChromiumCDMParent::ShutdownVideoDecoder()
|
||||
{
|
||||
if (mIsShutdown || !mVideoDecoderInitialized) {
|
||||
return ShutdownPromise::CreateAndResolve(true, __func__);
|
||||
}
|
||||
mInitVideoDecoderPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED,
|
||||
__func__);
|
||||
mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
|
||||
MOZ_ASSERT(mFlushDecoderPromise.IsEmpty());
|
||||
if (!SendDeinitializeVideoDecoder()) {
|
||||
return ShutdownPromise::CreateAndResolve(true, __func__);
|
||||
}
|
||||
mVideoDecoderInitialized = false;
|
||||
return ShutdownPromise::CreateAndResolve(true, __func__);
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMParent::Shutdown()
|
||||
{
|
||||
GMP_LOG("ChromiumCDMParent::Shutdown(this=%p)", this);
|
||||
|
||||
if (mIsShutdown) {
|
||||
return;
|
||||
}
|
||||
mIsShutdown = true;
|
||||
|
||||
for (RefPtr<DecryptJob>& decrypt : mDecrypts) {
|
||||
decrypt->PostResult(AbortedErr);
|
||||
}
|
||||
mDecrypts.Clear();
|
||||
|
||||
if (mVideoDecoderInitialized && !mActorDestroyed) {
|
||||
Unused << SendDeinitializeVideoDecoder();
|
||||
mVideoDecoderInitialized = false;
|
||||
}
|
||||
|
||||
// Note: MediaKeys rejects all outstanding promises when it initiates shutdown.
|
||||
mPromiseToCreateSessionToken.Clear();
|
||||
|
||||
mInitVideoDecoderPromise.RejectIfExists(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
RESULT_DETAIL("ChromiumCDMParent is shutdown")),
|
||||
__func__);
|
||||
mDecodePromise.RejectIfExists(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
RESULT_DETAIL("ChromiumCDMParent is shutdown")),
|
||||
__func__);
|
||||
mFlushDecoderPromise.RejectIfExists(
|
||||
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
|
||||
RESULT_DETAIL("ChromiumCDMParent is shutdown")),
|
||||
__func__);
|
||||
|
||||
if (!mActorDestroyed) {
|
||||
Unused << SendDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
156
dom/media/gmp/ChromiumCDMParent.h
Normal file
156
dom/media/gmp/ChromiumCDMParent.h
Normal file
@ -0,0 +1,156 @@
|
||||
/* -*- 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 ChromiumCDMParent_h_
|
||||
#define ChromiumCDMParent_h_
|
||||
|
||||
#include "DecryptJob.h"
|
||||
#include "GMPCrashHelper.h"
|
||||
#include "GMPCrashHelperHolder.h"
|
||||
#include "GMPMessageUtils.h"
|
||||
#include "mozilla/gmp/PChromiumCDMParent.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "ImageContainer.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MediaRawData;
|
||||
class ChromiumCDMProxy;
|
||||
|
||||
namespace gmp {
|
||||
|
||||
class GMPContentParent;
|
||||
|
||||
class ChromiumCDMParent final
|
||||
: public PChromiumCDMParent
|
||||
, public GMPCrashHelperHolder
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ChromiumCDMParent)
|
||||
|
||||
ChromiumCDMParent(GMPContentParent* aContentParent, uint32_t aPluginId);
|
||||
|
||||
uint32_t PluginId() const { return mPluginId; }
|
||||
|
||||
bool Init(ChromiumCDMProxy* aProxy,
|
||||
bool aAllowDistinctiveIdentifier,
|
||||
bool aAllowPersistentState);
|
||||
|
||||
void CreateSession(uint32_t aCreateSessionToken,
|
||||
uint32_t aSessionType,
|
||||
uint32_t aInitDataType,
|
||||
uint32_t aPromiseId,
|
||||
const nsTArray<uint8_t>& aInitData);
|
||||
|
||||
void LoadSession(uint32_t aPromiseId,
|
||||
uint32_t aSessionType,
|
||||
nsString aSessionId);
|
||||
|
||||
void SetServerCertificate(uint32_t aPromiseId,
|
||||
const nsTArray<uint8_t>& aCert);
|
||||
|
||||
void UpdateSession(const nsCString& aSessionId,
|
||||
uint32_t aPromiseId,
|
||||
const nsTArray<uint8_t>& aResponse);
|
||||
|
||||
void CloseSession(const nsCString& aSessionId, uint32_t aPromiseId);
|
||||
|
||||
void RemoveSession(const nsCString& aSessionId, uint32_t aPromiseId);
|
||||
|
||||
RefPtr<DecryptPromise> Decrypt(MediaRawData* aSample);
|
||||
|
||||
// TODO: Add functions for clients to send data to CDM, and
|
||||
// a Close() function.
|
||||
RefPtr<MediaDataDecoder::InitPromise> InitializeVideoDecoder(
|
||||
const gmp::CDMVideoDecoderConfig& aConfig,
|
||||
const VideoInfo& aInfo,
|
||||
RefPtr<layers::ImageContainer> aImageContainer);
|
||||
|
||||
RefPtr<MediaDataDecoder::DecodePromise> DecryptAndDecodeFrame(
|
||||
MediaRawData* aSample);
|
||||
|
||||
RefPtr<MediaDataDecoder::FlushPromise> FlushVideoDecoder();
|
||||
|
||||
RefPtr<MediaDataDecoder::DecodePromise> Drain();
|
||||
|
||||
RefPtr<ShutdownPromise> ShutdownVideoDecoder();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
protected:
|
||||
~ChromiumCDMParent() {}
|
||||
|
||||
ipc::IPCResult Recv__delete__() override;
|
||||
ipc::IPCResult RecvOnResolveNewSessionPromise(
|
||||
const uint32_t& aPromiseId,
|
||||
const nsCString& aSessionId) override;
|
||||
ipc::IPCResult RecvResolveLoadSessionPromise(
|
||||
const uint32_t& aPromiseId,
|
||||
const bool& aSuccessful) override;
|
||||
ipc::IPCResult RecvOnResolvePromise(const uint32_t& aPromiseId) override;
|
||||
ipc::IPCResult RecvOnRejectPromise(const uint32_t& aPromiseId,
|
||||
const uint32_t& aError,
|
||||
const uint32_t& aSystemCode,
|
||||
const nsCString& aErrorMessage) override;
|
||||
ipc::IPCResult RecvOnSessionMessage(const nsCString& aSessionId,
|
||||
const uint32_t& aMessageType,
|
||||
nsTArray<uint8_t>&& aMessage) override;
|
||||
ipc::IPCResult RecvOnSessionKeysChange(
|
||||
const nsCString& aSessionId,
|
||||
nsTArray<CDMKeyInformation>&& aKeysInfo) override;
|
||||
ipc::IPCResult RecvOnExpirationChange(
|
||||
const nsCString& aSessionId,
|
||||
const double& aSecondsSinceEpoch) override;
|
||||
ipc::IPCResult RecvOnSessionClosed(const nsCString& aSessionId) override;
|
||||
ipc::IPCResult RecvOnLegacySessionError(const nsCString& aSessionId,
|
||||
const uint32_t& aError,
|
||||
const uint32_t& aSystemCode,
|
||||
const nsCString& aMessage) override;
|
||||
ipc::IPCResult RecvDecrypted(const uint32_t& aId,
|
||||
const uint32_t& aStatus,
|
||||
nsTArray<uint8_t>&& aData) override;
|
||||
ipc::IPCResult RecvOnDecoderInitDone(const uint32_t& aStatus) override;
|
||||
ipc::IPCResult RecvDecoded(const CDMVideoFrame& aFrame) override;
|
||||
ipc::IPCResult RecvDecodeFailed(const uint32_t& aStatus) override;
|
||||
ipc::IPCResult RecvShutdown() override;
|
||||
ipc::IPCResult RecvResetVideoDecoderComplete() override;
|
||||
ipc::IPCResult RecvDrainComplete() override;
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
void RejectPromise(uint32_t aPromiseId,
|
||||
nsresult aError,
|
||||
const nsCString& aErrorMessage);
|
||||
|
||||
void ResolvePromise(uint32_t aPromiseId);
|
||||
|
||||
const uint32_t mPluginId;
|
||||
GMPContentParent* mContentParent;
|
||||
// Note: this pointer is a weak reference because otherwise it would cause
|
||||
// a cycle, as ChromiumCDMProxy has a strong reference to the
|
||||
// ChromiumCDMParent.
|
||||
ChromiumCDMProxy* mProxy = nullptr;
|
||||
nsDataHashtable<nsUint32HashKey, uint32_t> mPromiseToCreateSessionToken;
|
||||
nsTArray<RefPtr<DecryptJob>> mDecrypts;
|
||||
|
||||
MozPromiseHolder<MediaDataDecoder::InitPromise> mInitVideoDecoderPromise;
|
||||
MozPromiseHolder<MediaDataDecoder::DecodePromise> mDecodePromise;
|
||||
|
||||
RefPtr<layers::ImageContainer> mImageContainer;
|
||||
VideoInfo mVideoInfo;
|
||||
uint64_t mLastStreamOffset = 0;
|
||||
|
||||
MozPromiseHolder<MediaDataDecoder::FlushPromise> mFlushDecoderPromise;
|
||||
|
||||
bool mIsShutdown = false;
|
||||
bool mVideoDecoderInitialized = false;
|
||||
bool mActorDestroyed = false;
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // ChromiumCDMParent_h_
|
589
dom/media/gmp/ChromiumCDMProxy.cpp
Normal file
589
dom/media/gmp/ChromiumCDMProxy.cpp
Normal file
@ -0,0 +1,589 @@
|
||||
/* -*- 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 "ChromiumCDMProxy.h"
|
||||
#include "mozilla/dom/MediaKeySession.h"
|
||||
#include "GMPUtils.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "GMPService.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
ChromiumCDMProxy::ChromiumCDMProxy(dom::MediaKeys* aKeys,
|
||||
const nsAString& aKeySystem,
|
||||
GMPCrashHelper* aCrashHelper,
|
||||
bool aDistinctiveIdentifierRequired,
|
||||
bool aPersistentStateRequired,
|
||||
nsIEventTarget* aMainThread)
|
||||
: CDMProxy(aKeys,
|
||||
aKeySystem,
|
||||
aDistinctiveIdentifierRequired,
|
||||
aPersistentStateRequired,
|
||||
aMainThread)
|
||||
, mCrashHelper(aCrashHelper)
|
||||
, mCDMMutex("ChromiumCDMProxy")
|
||||
, mGMPThread(GetGMPAbstractThread())
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_COUNT_CTOR(ChromiumCDMProxy);
|
||||
}
|
||||
|
||||
ChromiumCDMProxy::~ChromiumCDMProxy()
|
||||
{
|
||||
MOZ_COUNT_DTOR(ChromiumCDMProxy);
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::Init(PromiseId aPromiseId,
|
||||
const nsAString& aOrigin,
|
||||
const nsAString& aTopLevelOrigin,
|
||||
const nsAString& aGMPName)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
|
||||
|
||||
EME_LOG(
|
||||
"ChromiumCDMProxy::Init (pid=%u, origin=%s, topLevelOrigin=%s, gmp=%s)",
|
||||
aPromiseId,
|
||||
NS_ConvertUTF16toUTF8(aOrigin).get(),
|
||||
NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
|
||||
NS_ConvertUTF16toUTF8(aGMPName).get());
|
||||
|
||||
if (!mGMPThread) {
|
||||
RejectPromise(
|
||||
aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Couldn't get GMP thread ChromiumCDMProxy::Init"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (aGMPName.IsEmpty()) {
|
||||
RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
nsPrintfCString("Unknown GMP for keysystem '%s'",
|
||||
NS_ConvertUTF16toUTF8(mKeySystem).get()));
|
||||
return;
|
||||
}
|
||||
|
||||
gmp::NodeId nodeId(aOrigin, aTopLevelOrigin, aGMPName);
|
||||
RefPtr<AbstractThread> thread = mGMPThread;
|
||||
RefPtr<GMPCrashHelper> helper(mCrashHelper);
|
||||
RefPtr<ChromiumCDMProxy> self(this);
|
||||
nsCString keySystem = NS_ConvertUTF16toUTF8(mKeySystem);
|
||||
RefPtr<Runnable> task(NS_NewRunnableFunction(
|
||||
[self, nodeId, helper, aPromiseId, thread, keySystem]() -> void {
|
||||
MOZ_ASSERT(self->IsOnOwnerThread());
|
||||
|
||||
RefPtr<gmp::GeckoMediaPluginService> service =
|
||||
gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
|
||||
if (!service) {
|
||||
self->RejectPromise(
|
||||
aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING(
|
||||
"Couldn't get GeckoMediaPluginService in ChromiumCDMProxy::Init"));
|
||||
return;
|
||||
}
|
||||
RefPtr<gmp::GetCDMParentPromise> promise =
|
||||
service->GetCDM(nodeId, { keySystem }, helper);
|
||||
promise->Then(
|
||||
thread,
|
||||
__func__,
|
||||
[self, aPromiseId](RefPtr<gmp::ChromiumCDMParent> cdm) {
|
||||
if (!cdm->Init(self,
|
||||
self->mDistinctiveIdentifierRequired,
|
||||
self->mPersistentStateRequired)) {
|
||||
self->RejectPromise(aPromiseId,
|
||||
NS_ERROR_FAILURE,
|
||||
NS_LITERAL_CSTRING("GetCDM failed."));
|
||||
return;
|
||||
}
|
||||
{
|
||||
MutexAutoLock lock(self->mCDMMutex);
|
||||
self->mCDM = cdm;
|
||||
}
|
||||
self->OnCDMCreated(aPromiseId);
|
||||
},
|
||||
[self, aPromiseId](nsresult rv) {
|
||||
self->RejectPromise(
|
||||
aPromiseId, NS_ERROR_FAILURE, NS_LITERAL_CSTRING("GetCDM failed."));
|
||||
});
|
||||
}));
|
||||
|
||||
mGMPThread->Dispatch(task.forget());
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::OnCDMCreated(uint32_t aPromiseId)
|
||||
{
|
||||
EME_LOG("ChromiumCDMProxy::OnCDMCreated(pid=%u) isMainThread=%d this=%p",
|
||||
aPromiseId,
|
||||
NS_IsMainThread(),
|
||||
this);
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
mMainThread->Dispatch(NewRunnableMethod<PromiseId>(
|
||||
this, &ChromiumCDMProxy::OnCDMCreated, aPromiseId),
|
||||
NS_DISPATCH_NORMAL);
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mKeys.IsNull()) {
|
||||
return;
|
||||
}
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
|
||||
// This should only be called once the CDM has been created.
|
||||
MOZ_ASSERT(cdm);
|
||||
if (cdm) {
|
||||
mKeys->OnCDMCreated(aPromiseId, cdm->PluginId());
|
||||
} else {
|
||||
// No CDM? Shouldn't be possible, but reject the promise anyway...
|
||||
mKeys->RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Null CDM in OnCDMCreated()"));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
ChromiumCDMProxy::IsOnOwnerThread()
|
||||
{
|
||||
return mGMPThread->IsCurrentThreadIn();
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint32_t
|
||||
ToCDMSessionType(dom::MediaKeySessionType aSessionType)
|
||||
{
|
||||
switch (aSessionType) {
|
||||
case dom::MediaKeySessionType::Temporary:
|
||||
return static_cast<uint32_t>(cdm::kTemporary);
|
||||
case dom::MediaKeySessionType::Persistent_license:
|
||||
return static_cast<uint32_t>(cdm::kPersistentLicense);
|
||||
default:
|
||||
return static_cast<uint32_t>(cdm::kTemporary);
|
||||
};
|
||||
};
|
||||
|
||||
static uint32_t
|
||||
ToCDMInitDataType(const nsAString& aInitDataType)
|
||||
{
|
||||
if (aInitDataType.EqualsLiteral("cenc")) {
|
||||
return static_cast<uint32_t>(cdm::kCenc);
|
||||
}
|
||||
if (aInitDataType.EqualsLiteral("webm")) {
|
||||
return static_cast<uint32_t>(cdm::kWebM);
|
||||
}
|
||||
if (aInitDataType.EqualsLiteral("keyids")) {
|
||||
return static_cast<uint32_t>(cdm::kKeyIds);
|
||||
}
|
||||
return static_cast<uint32_t>(cdm::kCenc);
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::CreateSession(uint32_t aCreateSessionToken,
|
||||
dom::MediaKeySessionType aSessionType,
|
||||
PromiseId aPromiseId,
|
||||
const nsAString& aInitDataType,
|
||||
nsTArray<uint8_t>& aInitData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
EME_LOG("ChromiumCDMProxy::CreateSession(token=%u, type=%d, pid=%u) "
|
||||
"initDataLen=%zu",
|
||||
aCreateSessionToken,
|
||||
(int)aSessionType,
|
||||
aPromiseId,
|
||||
aInitData.Length());
|
||||
|
||||
uint32_t sessionType = ToCDMSessionType(aSessionType);
|
||||
uint32_t initDataType = ToCDMInitDataType(aInitDataType);
|
||||
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
|
||||
if (!cdm) {
|
||||
RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Null CDM in CreateSession"));
|
||||
return;
|
||||
}
|
||||
|
||||
mGMPThread->Dispatch(
|
||||
NewRunnableMethod<uint32_t,
|
||||
uint32_t,
|
||||
uint32_t,
|
||||
uint32_t,
|
||||
nsTArray<uint8_t>>(cdm,
|
||||
&gmp::ChromiumCDMParent::CreateSession,
|
||||
aCreateSessionToken,
|
||||
sessionType,
|
||||
initDataType,
|
||||
aPromiseId,
|
||||
Move(aInitData)));
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::LoadSession(PromiseId aPromiseId,
|
||||
dom::MediaKeySessionType aSessionType,
|
||||
const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
|
||||
if (!cdm) {
|
||||
RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Null CDM in LoadSession"));
|
||||
return;
|
||||
}
|
||||
|
||||
mGMPThread->Dispatch(NewRunnableMethod<uint32_t, uint32_t, nsString>(
|
||||
cdm,
|
||||
&gmp::ChromiumCDMParent::LoadSession,
|
||||
aPromiseId,
|
||||
ToCDMSessionType(aSessionType),
|
||||
aSessionId));
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::SetServerCertificate(PromiseId aPromiseId,
|
||||
nsTArray<uint8_t>& aCert)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
EME_LOG("ChromiumCDMProxy::SetServerCertificate(pid=%u) certLen=%zu",
|
||||
aPromiseId,
|
||||
aCert.Length());
|
||||
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
|
||||
if (!cdm) {
|
||||
RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Null CDM in SetServerCertificate"));
|
||||
return;
|
||||
}
|
||||
|
||||
mGMPThread->Dispatch(NewRunnableMethod<uint32_t, nsTArray<uint8_t>>(
|
||||
cdm,
|
||||
&gmp::ChromiumCDMParent::SetServerCertificate,
|
||||
aPromiseId,
|
||||
Move(aCert)));
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::UpdateSession(const nsAString& aSessionId,
|
||||
PromiseId aPromiseId,
|
||||
nsTArray<uint8_t>& aResponse)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
EME_LOG("ChromiumCDMProxy::UpdateSession(sid='%s', pid=%u) responseLen=%zu",
|
||||
NS_ConvertUTF16toUTF8(aSessionId).get(),
|
||||
aPromiseId,
|
||||
aResponse.Length());
|
||||
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
|
||||
if (!cdm) {
|
||||
RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Null CDM in UpdateSession"));
|
||||
return;
|
||||
}
|
||||
mGMPThread->Dispatch(
|
||||
NewRunnableMethod<nsCString, uint32_t, nsTArray<uint8_t>>(
|
||||
cdm,
|
||||
&gmp::ChromiumCDMParent::UpdateSession,
|
||||
NS_ConvertUTF16toUTF8(aSessionId),
|
||||
aPromiseId,
|
||||
Move(aResponse)));
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::CloseSession(const nsAString& aSessionId,
|
||||
PromiseId aPromiseId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
EME_LOG("ChromiumCDMProxy::CloseSession(sid='%s', pid=%u)",
|
||||
NS_ConvertUTF16toUTF8(aSessionId).get(),
|
||||
aPromiseId);
|
||||
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
|
||||
if (!cdm) {
|
||||
RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Null CDM in CloseSession"));
|
||||
return;
|
||||
}
|
||||
mGMPThread->Dispatch(NewRunnableMethod<nsCString, uint32_t>(
|
||||
cdm,
|
||||
&gmp::ChromiumCDMParent::CloseSession,
|
||||
NS_ConvertUTF16toUTF8(aSessionId),
|
||||
aPromiseId));
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::RemoveSession(const nsAString& aSessionId,
|
||||
PromiseId aPromiseId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
EME_LOG("ChromiumCDMProxy::RemoveSession(sid='%s', pid=%u)",
|
||||
NS_ConvertUTF16toUTF8(aSessionId).get(),
|
||||
aPromiseId);
|
||||
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
|
||||
if (!cdm) {
|
||||
RejectPromise(aPromiseId,
|
||||
NS_ERROR_DOM_INVALID_STATE_ERR,
|
||||
NS_LITERAL_CSTRING("Null CDM in RemoveSession"));
|
||||
return;
|
||||
}
|
||||
mGMPThread->Dispatch(NewRunnableMethod<nsCString, uint32_t>(
|
||||
cdm,
|
||||
&gmp::ChromiumCDMParent::RemoveSession,
|
||||
NS_ConvertUTF16toUTF8(aSessionId),
|
||||
aPromiseId));
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
EME_LOG("ChromiumCDMProxy::Shutdown()");
|
||||
mKeys.Clear();
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm;
|
||||
{
|
||||
MutexAutoLock lock(mCDMMutex);
|
||||
cdm.swap(mCDM);
|
||||
}
|
||||
if (cdm) {
|
||||
nsCOMPtr<nsIRunnable> task =
|
||||
NewRunnableMethod(mCDM, &gmp::ChromiumCDMParent::Shutdown);
|
||||
mGMPThread->Dispatch(task.forget());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::RejectPromise(PromiseId aId,
|
||||
nsresult aCode,
|
||||
const nsCString& aReason)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIRunnable> task;
|
||||
task = NewRunnableMethod<PromiseId, nsresult, nsCString>(
|
||||
this, &ChromiumCDMProxy::RejectPromise, aId, aCode, aReason);
|
||||
NS_DispatchToMainThread(task);
|
||||
return;
|
||||
}
|
||||
EME_LOG("ChromiumCDMProxy::RejectPromise(pid=%u, code=0x%x, reason='%s')",
|
||||
aId,
|
||||
static_cast<uint32_t>(aCode),
|
||||
aReason.get());
|
||||
if (!mKeys.IsNull()) {
|
||||
mKeys->RejectPromise(aId, aCode, aReason);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::ResolvePromise(PromiseId aId)
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIRunnable> task;
|
||||
task = NewRunnableMethod<PromiseId>(
|
||||
this, &ChromiumCDMProxy::ResolvePromise, aId);
|
||||
NS_DispatchToMainThread(task);
|
||||
return;
|
||||
}
|
||||
|
||||
EME_LOG("ChromiumCDMProxy::ResolvePromise(pid=%u)", aId);
|
||||
if (!mKeys.IsNull()) {
|
||||
mKeys->ResolvePromise(aId);
|
||||
} else {
|
||||
NS_WARNING("ChromiumCDMProxy unable to resolve promise!");
|
||||
}
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
ChromiumCDMProxy::GetNodeId() const
|
||||
{
|
||||
return mNodeId;
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::OnSetSessionId(uint32_t aCreateSessionToken,
|
||||
const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
EME_LOG("ChromiumCDMProxy::OnSetSessionId(token=%u, sid='%s')",
|
||||
aCreateSessionToken,
|
||||
NS_ConvertUTF16toUTF8(aSessionId).get());
|
||||
|
||||
if (mKeys.IsNull()) {
|
||||
return;
|
||||
}
|
||||
RefPtr<dom::MediaKeySession> session(
|
||||
mKeys->GetPendingSession(aCreateSessionToken));
|
||||
if (session) {
|
||||
session->SetSessionId(aSessionId);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId,
|
||||
bool aSuccess)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mKeys.IsNull()) {
|
||||
return;
|
||||
}
|
||||
mKeys->OnSessionLoaded(aPromiseId, aSuccess);
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::OnSessionMessage(const nsAString& aSessionId,
|
||||
dom::MediaKeyMessageType aMessageType,
|
||||
nsTArray<uint8_t>& aMessage)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mKeys.IsNull()) {
|
||||
return;
|
||||
}
|
||||
RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
|
||||
if (session) {
|
||||
session->DispatchKeyMessage(aMessageType, aMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::OnKeyStatusesChange(const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mKeys.IsNull()) {
|
||||
return;
|
||||
}
|
||||
RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
|
||||
if (session) {
|
||||
session->DispatchKeyStatusesChange();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::OnExpirationChange(const nsAString& aSessionId,
|
||||
GMPTimestamp aExpiryTime)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mKeys.IsNull()) {
|
||||
return;
|
||||
}
|
||||
RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
|
||||
if (session) {
|
||||
// Expiry of 0 is interpreted as "never expire". See bug 1345341.
|
||||
double t = (aExpiryTime == 0) ? std::numeric_limits<double>::quiet_NaN()
|
||||
: static_cast<double>(aExpiryTime);
|
||||
session->SetExpiration(t);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::OnSessionClosed(const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
bool keyStatusesChange = false;
|
||||
{
|
||||
CDMCaps::AutoLock caps(Capabilites());
|
||||
keyStatusesChange = caps.RemoveKeysForSession(nsString(aSessionId));
|
||||
}
|
||||
if (keyStatusesChange) {
|
||||
OnKeyStatusesChange(aSessionId);
|
||||
}
|
||||
if (mKeys.IsNull()) {
|
||||
return;
|
||||
}
|
||||
RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
|
||||
if (session) {
|
||||
session->OnClosed();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::OnDecrypted(uint32_t aId,
|
||||
DecryptStatus aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::OnSessionError(const nsAString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsAString& aMsg)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mKeys.IsNull()) {
|
||||
return;
|
||||
}
|
||||
RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
|
||||
if (session) {
|
||||
session->DispatchKeyError(aSystemCode);
|
||||
}
|
||||
LogToConsole(aMsg);
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::OnRejectPromise(uint32_t aPromiseId,
|
||||
nsresult aDOMException,
|
||||
const nsCString& aMsg)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
RejectPromise(aPromiseId, aDOMException, aMsg);
|
||||
}
|
||||
|
||||
const nsString&
|
||||
ChromiumCDMProxy::KeySystem() const
|
||||
{
|
||||
return mKeySystem;
|
||||
}
|
||||
|
||||
CDMCaps&
|
||||
ChromiumCDMProxy::Capabilites()
|
||||
{
|
||||
return mCapabilites;
|
||||
}
|
||||
|
||||
RefPtr<DecryptPromise>
|
||||
ChromiumCDMProxy::Decrypt(MediaRawData* aSample)
|
||||
{
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
|
||||
if (!cdm) {
|
||||
return DecryptPromise::CreateAndReject(DecryptResult(AbortedErr, aSample),
|
||||
__func__);
|
||||
}
|
||||
RefPtr<MediaRawData> sample = aSample;
|
||||
return InvokeAsync(
|
||||
mGMPThread, __func__, [cdm, sample]() { return cdm->Decrypt(sample); });
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
|
||||
nsTArray<nsCString>& aSessionIds)
|
||||
{
|
||||
CDMCaps::AutoLock caps(Capabilites());
|
||||
caps.GetSessionIdsForKeyId(aKeyId, aSessionIds);
|
||||
}
|
||||
|
||||
void
|
||||
ChromiumCDMProxy::Terminated()
|
||||
{
|
||||
if (!mKeys.IsNull()) {
|
||||
mKeys->Terminated();
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<gmp::ChromiumCDMParent>
|
||||
ChromiumCDMProxy::GetCDMParent()
|
||||
{
|
||||
MutexAutoLock lock(mCDMMutex);
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = mCDM;
|
||||
return cdm.forget();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
132
dom/media/gmp/ChromiumCDMProxy.h
Normal file
132
dom/media/gmp/ChromiumCDMProxy.h
Normal file
@ -0,0 +1,132 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef ChromiumCDMProxy_h_
|
||||
#define ChromiumCDMProxy_h_
|
||||
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "mozilla/AbstractThread.h"
|
||||
#include "ChromiumCDMParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class MediaRawData;
|
||||
class DecryptJob;
|
||||
|
||||
class ChromiumCDMProxy : public CDMProxy
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ChromiumCDMProxy, override)
|
||||
|
||||
ChromiumCDMProxy(dom::MediaKeys* aKeys,
|
||||
const nsAString& aKeySystem,
|
||||
GMPCrashHelper* aCrashHelper,
|
||||
bool aAllowDistinctiveIdentifier,
|
||||
bool aAllowPersistentState,
|
||||
nsIEventTarget* aMainThread);
|
||||
|
||||
void Init(PromiseId aPromiseId,
|
||||
const nsAString& aOrigin,
|
||||
const nsAString& aTopLevelOrigin,
|
||||
const nsAString& aGMPName) override;
|
||||
|
||||
void CreateSession(uint32_t aCreateSessionToken,
|
||||
dom::MediaKeySessionType aSessionType,
|
||||
PromiseId aPromiseId,
|
||||
const nsAString& aInitDataType,
|
||||
nsTArray<uint8_t>& aInitData) override;
|
||||
|
||||
void LoadSession(PromiseId aPromiseId,
|
||||
dom::MediaKeySessionType aSessionType,
|
||||
const nsAString& aSessionId) override;
|
||||
|
||||
void SetServerCertificate(PromiseId aPromiseId,
|
||||
nsTArray<uint8_t>& aCert) override;
|
||||
|
||||
void UpdateSession(const nsAString& aSessionId,
|
||||
PromiseId aPromiseId,
|
||||
nsTArray<uint8_t>& aResponse) override;
|
||||
|
||||
void CloseSession(const nsAString& aSessionId, PromiseId aPromiseId) override;
|
||||
|
||||
void RemoveSession(const nsAString& aSessionId,
|
||||
PromiseId aPromiseId) override;
|
||||
|
||||
void Shutdown() override;
|
||||
|
||||
void Terminated() override;
|
||||
|
||||
const nsCString& GetNodeId() const override;
|
||||
|
||||
void OnSetSessionId(uint32_t aCreateSessionToken,
|
||||
const nsAString& aSessionId) override;
|
||||
|
||||
void OnResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess) override;
|
||||
|
||||
void OnSessionMessage(const nsAString& aSessionId,
|
||||
dom::MediaKeyMessageType aMessageType,
|
||||
nsTArray<uint8_t>& aMessage) override;
|
||||
|
||||
void OnExpirationChange(const nsAString& aSessionId,
|
||||
GMPTimestamp aExpiryTime) override;
|
||||
|
||||
void OnSessionClosed(const nsAString& aSessionId) override;
|
||||
|
||||
void OnSessionError(const nsAString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsAString& aMsg) override;
|
||||
|
||||
void OnRejectPromise(uint32_t aPromiseId,
|
||||
nsresult aDOMException,
|
||||
const nsCString& aMsg) override;
|
||||
|
||||
RefPtr<DecryptPromise> Decrypt(MediaRawData* aSample) override;
|
||||
|
||||
void OnDecrypted(uint32_t aId,
|
||||
DecryptStatus aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData) override;
|
||||
|
||||
void RejectPromise(PromiseId aId,
|
||||
nsresult aExceptionCode,
|
||||
const nsCString& aReason) override;
|
||||
|
||||
void ResolvePromise(PromiseId aId) override;
|
||||
|
||||
const nsString& KeySystem() const override;
|
||||
|
||||
CDMCaps& Capabilites() override;
|
||||
|
||||
void OnKeyStatusesChange(const nsAString& aSessionId) override;
|
||||
|
||||
void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
|
||||
nsTArray<nsCString>& aSessionIds) override;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsOnOwnerThread() override;
|
||||
#endif
|
||||
|
||||
ChromiumCDMProxy* AsChromiumCDMProxy() override { return this; }
|
||||
|
||||
// Threadsafe. Note this may return a reference to a shutdown
|
||||
// CDM, which will fail on all operations.
|
||||
already_AddRefed<gmp::ChromiumCDMParent> GetCDMParent();
|
||||
|
||||
private:
|
||||
void OnCDMCreated(uint32_t aPromiseId);
|
||||
|
||||
~ChromiumCDMProxy();
|
||||
|
||||
GMPCrashHelper* mCrashHelper;
|
||||
|
||||
Mutex mCDMMutex;
|
||||
RefPtr<gmp::ChromiumCDMParent> mCDM;
|
||||
RefPtr<AbstractThread> mGMPThread;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GMPCDMProxy_h_
|
@ -328,6 +328,7 @@ GMPCDMProxy::gmp_CreateSession(UniquePtr<CreateSessionData>&& aData)
|
||||
|
||||
void
|
||||
GMPCDMProxy::LoadSession(PromiseId aPromiseId,
|
||||
dom::MediaKeySessionType aSessionType,
|
||||
const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
@ -43,6 +43,7 @@ public:
|
||||
nsTArray<uint8_t>& aInitData) override;
|
||||
|
||||
void LoadSession(PromiseId aPromiseId,
|
||||
dom::MediaKeySessionType aSessionType,
|
||||
const nsAString& aSessionId) override;
|
||||
|
||||
void SetServerCertificate(PromiseId aPromiseId,
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "prio.h"
|
||||
#include "base/task.h"
|
||||
#include "widevine-adapter/WidevineAdapter.h"
|
||||
#include "ChromiumCDMAdapter.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
@ -346,9 +347,10 @@ GMPChild::AnswerStartPlugin(const nsString& aAdapter)
|
||||
#endif
|
||||
|
||||
bool isWidevine = aAdapter.EqualsLiteral("widevine");
|
||||
bool isChromium = aAdapter.EqualsLiteral("chromium");
|
||||
#if defined(MOZ_GMP_SANDBOX) && defined(XP_MACOSX)
|
||||
MacSandboxPluginType pluginType = MacSandboxPluginType_GMPlugin_Default;
|
||||
if (isWidevine) {
|
||||
if (isWidevine || isChromium) {
|
||||
pluginType = MacSandboxPluginType_GMPlugin_EME_Widevine;
|
||||
}
|
||||
if (!SetMacSandboxInfo(pluginType)) {
|
||||
@ -358,7 +360,13 @@ GMPChild::AnswerStartPlugin(const nsString& aAdapter)
|
||||
}
|
||||
#endif
|
||||
|
||||
GMPAdapter* adapter = (isWidevine) ? new WidevineAdapter() : nullptr;
|
||||
GMPAdapter* adapter = nullptr;
|
||||
if (isWidevine) {
|
||||
adapter = new WidevineAdapter();
|
||||
} else if (isChromium) {
|
||||
adapter = new ChromiumCDMAdapter();
|
||||
}
|
||||
|
||||
if (!mGMPLoader->Load(libPath.get(),
|
||||
libPath.Length(),
|
||||
platformAPI,
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "GMPDecryptorChild.h"
|
||||
#include "GMPVideoDecoderChild.h"
|
||||
#include "GMPVideoEncoderChild.h"
|
||||
#include "ChromiumCDMChild.h"
|
||||
#include "base/task.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -93,6 +94,21 @@ GMPContentChild::DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor)
|
||||
return true;
|
||||
}
|
||||
|
||||
PChromiumCDMChild*
|
||||
GMPContentChild::AllocPChromiumCDMChild()
|
||||
{
|
||||
ChromiumCDMChild* actor = new ChromiumCDMChild(this);
|
||||
actor->AddRef();
|
||||
return actor;
|
||||
}
|
||||
|
||||
bool
|
||||
GMPContentChild::DeallocPChromiumCDMChild(PChromiumCDMChild* aActor)
|
||||
{
|
||||
static_cast<ChromiumCDMChild*>(aActor)->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPContentChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor)
|
||||
{
|
||||
@ -144,6 +160,24 @@ GMPContentChild::RecvPGMPVideoEncoderConstructor(PGMPVideoEncoderChild* aActor)
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPContentChild::RecvPChromiumCDMConstructor(PChromiumCDMChild* aActor)
|
||||
{
|
||||
ChromiumCDMChild* child = static_cast<ChromiumCDMChild*>(aActor);
|
||||
cdm::Host_8* host = child;
|
||||
|
||||
void* cdm = nullptr;
|
||||
GMPErr err = mGMPChild->GetAPI(CHROMIUM_CDM_API, host, &cdm);
|
||||
if (err != GMPNoErr || !cdm) {
|
||||
NS_WARNING("GMPGetAPI call failed trying to get CDM.");
|
||||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
child->Init(static_cast<cdm::ContentDecryptionModule_8*>(cdm));
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void
|
||||
GMPContentChild::CloseActive()
|
||||
{
|
||||
@ -165,6 +199,11 @@ GMPContentChild::CloseActive()
|
||||
for (auto iter = videoEncoders.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
iter.Get()->GetKey()->SendShutdown();
|
||||
}
|
||||
|
||||
const ManagedContainer<PChromiumCDMChild>& cdms = ManagedPChromiumCDMChild();
|
||||
for (auto iter = cdms.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
iter.Get()->GetKey()->SendShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
@ -172,7 +211,8 @@ GMPContentChild::IsUsed()
|
||||
{
|
||||
return !ManagedPGMPDecryptorChild().IsEmpty() ||
|
||||
!ManagedPGMPVideoDecoderChild().IsEmpty() ||
|
||||
!ManagedPGMPVideoEncoderChild().IsEmpty();
|
||||
!ManagedPGMPVideoEncoderChild().IsEmpty() ||
|
||||
!ManagedPChromiumCDMChild().IsEmpty();
|
||||
}
|
||||
|
||||
} // namespace gmp
|
||||
|
@ -26,6 +26,8 @@ public:
|
||||
mozilla::ipc::IPCResult RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor) override;
|
||||
mozilla::ipc::IPCResult RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor, const uint32_t& aDecryptorId) override;
|
||||
mozilla::ipc::IPCResult RecvPGMPVideoEncoderConstructor(PGMPVideoEncoderChild* aActor) override;
|
||||
mozilla::ipc::IPCResult RecvPChromiumCDMConstructor(
|
||||
PChromiumCDMChild* aActor) override;
|
||||
|
||||
PGMPDecryptorChild* AllocPGMPDecryptorChild() override;
|
||||
bool DeallocPGMPDecryptorChild(PGMPDecryptorChild* aActor) override;
|
||||
@ -36,6 +38,9 @@ public:
|
||||
PGMPVideoEncoderChild* AllocPGMPVideoEncoderChild() override;
|
||||
bool DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor) override;
|
||||
|
||||
PChromiumCDMChild* AllocPChromiumCDMChild() override;
|
||||
bool DeallocPChromiumCDMChild(PChromiumCDMChild* aActor) override;
|
||||
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
void ProcessingError(Result aCode, const char* aReason) override;
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "GMPServiceChild.h"
|
||||
#include "GMPVideoDecoderParent.h"
|
||||
#include "GMPVideoEncoderParent.h"
|
||||
#include "ChromiumCDMParent.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Unused.h"
|
||||
@ -65,9 +66,8 @@ private:
|
||||
void
|
||||
GMPContentParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
MOZ_ASSERT(mDecryptors.IsEmpty() &&
|
||||
mVideoDecoders.IsEmpty() &&
|
||||
mVideoEncoders.IsEmpty());
|
||||
MOZ_ASSERT(mDecryptors.IsEmpty() && mVideoDecoders.IsEmpty() &&
|
||||
mVideoEncoders.IsEmpty() && mChromiumCDMs.IsEmpty());
|
||||
NS_DispatchToCurrentThread(new ReleaseGMPContentParent(this));
|
||||
}
|
||||
|
||||
@ -77,6 +77,15 @@ GMPContentParent::CheckThread()
|
||||
MOZ_ASSERT(mGMPThread == NS_GetCurrentThread());
|
||||
}
|
||||
|
||||
void
|
||||
GMPContentParent::ChromiumCDMDestroyed(ChromiumCDMParent* aDecoder)
|
||||
{
|
||||
MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
|
||||
|
||||
MOZ_ALWAYS_TRUE(mChromiumCDMs.RemoveElement(aDecoder));
|
||||
CloseIfUnused();
|
||||
}
|
||||
|
||||
void
|
||||
GMPContentParent::VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder)
|
||||
{
|
||||
@ -124,9 +133,8 @@ GMPContentParent::RemoveCloseBlocker()
|
||||
void
|
||||
GMPContentParent::CloseIfUnused()
|
||||
{
|
||||
if (mDecryptors.IsEmpty() &&
|
||||
mVideoDecoders.IsEmpty() &&
|
||||
mVideoEncoders.IsEmpty() &&
|
||||
if (mDecryptors.IsEmpty() && mVideoDecoders.IsEmpty() &&
|
||||
mVideoEncoders.IsEmpty() && mChromiumCDMs.IsEmpty() &&
|
||||
mCloseBlockerCount == 0) {
|
||||
RefPtr<GMPContentParent> toClose;
|
||||
if (mParent) {
|
||||
@ -159,7 +167,7 @@ GMPContentParent::GetGMPDecryptor(GMPDecryptorParent** aGMPDP)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIThread*
|
||||
nsCOMPtr<nsIThread>
|
||||
GMPContentParent::GMPThread()
|
||||
{
|
||||
if (!mGMPThread) {
|
||||
@ -180,6 +188,21 @@ GMPContentParent::GMPThread()
|
||||
return mGMPThread;
|
||||
}
|
||||
|
||||
already_AddRefed<ChromiumCDMParent>
|
||||
GMPContentParent::GetChromiumCDM()
|
||||
{
|
||||
PChromiumCDMParent* actor = SendPChromiumCDMConstructor();
|
||||
if (!actor) {
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<ChromiumCDMParent> parent = static_cast<ChromiumCDMParent*>(actor);
|
||||
|
||||
// TODO: Remove parent from mChromiumCDMs in ChromiumCDMParent::Destroy().
|
||||
mChromiumCDMs.AppendElement(parent);
|
||||
|
||||
return parent.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPContentParent::GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD,
|
||||
uint32_t aDecryptorId)
|
||||
@ -217,6 +240,14 @@ GMPContentParent::GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PChromiumCDMParent*
|
||||
GMPContentParent::AllocPChromiumCDMParent()
|
||||
{
|
||||
ChromiumCDMParent* parent = new ChromiumCDMParent(this, GetPluginId());
|
||||
NS_ADDREF(parent);
|
||||
return parent;
|
||||
}
|
||||
|
||||
PGMPVideoDecoderParent*
|
||||
GMPContentParent::AllocPGMPVideoDecoderParent(const uint32_t& aDecryptorId)
|
||||
{
|
||||
@ -225,6 +256,14 @@ GMPContentParent::AllocPGMPVideoDecoderParent(const uint32_t& aDecryptorId)
|
||||
return vdp;
|
||||
}
|
||||
|
||||
bool
|
||||
GMPContentParent::DeallocPChromiumCDMParent(PChromiumCDMParent* aActor)
|
||||
{
|
||||
ChromiumCDMParent* parent = static_cast<ChromiumCDMParent*>(aActor);
|
||||
NS_RELEASE(parent);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
GMPContentParent::DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor)
|
||||
{
|
||||
|
@ -17,6 +17,7 @@ class GMPDecryptorParent;
|
||||
class GMPParent;
|
||||
class GMPVideoDecoderParent;
|
||||
class GMPVideoEncoderParent;
|
||||
class ChromiumCDMParent;
|
||||
|
||||
class GMPContentParent final : public PGMPContentParent,
|
||||
public GMPSharedMem
|
||||
@ -36,7 +37,10 @@ public:
|
||||
nsresult GetGMPDecryptor(GMPDecryptorParent** aGMPKS);
|
||||
void DecryptorDestroyed(GMPDecryptorParent* aSession);
|
||||
|
||||
nsIThread* GMPThread();
|
||||
already_AddRefed<ChromiumCDMParent> GetChromiumCDM();
|
||||
void ChromiumCDMDestroyed(ChromiumCDMParent* aCDM);
|
||||
|
||||
nsCOMPtr<nsIThread> GMPThread();
|
||||
|
||||
// GMPSharedMem
|
||||
void CheckThread() override;
|
||||
@ -92,6 +96,9 @@ private:
|
||||
PGMPDecryptorParent* AllocPGMPDecryptorParent() override;
|
||||
bool DeallocPGMPDecryptorParent(PGMPDecryptorParent* aActor) override;
|
||||
|
||||
PChromiumCDMParent* AllocPChromiumCDMParent() override;
|
||||
bool DeallocPChromiumCDMParent(PChromiumCDMParent* aActor) override;
|
||||
|
||||
void CloseIfUnused();
|
||||
// Needed because NewRunnableMethod tried to use the class that the method
|
||||
// lives on to store the receiver, but PGMPContentParent isn't refcounted.
|
||||
@ -103,6 +110,7 @@ private:
|
||||
nsTArray<RefPtr<GMPVideoDecoderParent>> mVideoDecoders;
|
||||
nsTArray<RefPtr<GMPVideoEncoderParent>> mVideoEncoders;
|
||||
nsTArray<RefPtr<GMPDecryptorParent>> mDecryptors;
|
||||
nsTArray<RefPtr<ChromiumCDMParent>> mChromiumCDMs;
|
||||
nsCOMPtr<nsIThread> mGMPThread;
|
||||
RefPtr<GMPParent> mParent;
|
||||
nsCString mDisplayName;
|
||||
|
@ -119,7 +119,7 @@ private:
|
||||
uint32_t mPluginId;
|
||||
GMPDecryptorProxyCallback* mCallback;
|
||||
#ifdef DEBUG
|
||||
nsIThread* const mGMPThread;
|
||||
nsCOMPtr<nsIThread> const mGMPThread;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
19
dom/media/gmp/GMPLog.h
Normal file
19
dom/media/gmp/GMPLog.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* -*- 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 GMP_LOG_h_
|
||||
#define GMP_LOG_h_
|
||||
|
||||
#include "mozilla/Logging.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
extern LogModule* GetGMPLog();
|
||||
|
||||
#define GMP_LOG(msg, ...) MOZ_LOG(GetGMPLog(), LogLevel::Debug, (msg, ##__VA_ARGS__))
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GMP_LOG_h_
|
@ -9,6 +9,7 @@
|
||||
#include "gmp-video-codec.h"
|
||||
#include "gmp-video-frame-encoded.h"
|
||||
#include "gmp-decryption.h"
|
||||
#include "IPCMessageUtils.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
|
@ -43,6 +43,7 @@ using CrashReporter::GetIDFromMinidump;
|
||||
|
||||
#include "mozilla/dom/WidevineCDMManifestBinding.h"
|
||||
#include "widevine-adapter/WidevineAdapter.h"
|
||||
#include "ChromiumCDMAdapter.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -323,7 +324,7 @@ void
|
||||
GMPParent::ChildTerminated()
|
||||
{
|
||||
RefPtr<GMPParent> self(this);
|
||||
nsIThread* gmpThread = GMPThread();
|
||||
nsCOMPtr<nsIThread> gmpThread = GMPThread();
|
||||
|
||||
if (!gmpThread) {
|
||||
// Bug 1163239 - this can happen on shutdown.
|
||||
@ -372,26 +373,20 @@ GMPParent::State() const
|
||||
return mState;
|
||||
}
|
||||
|
||||
// Not changing to use mService since we'll be removing it
|
||||
nsIThread*
|
||||
nsCOMPtr<nsIThread>
|
||||
GMPParent::GMPThread()
|
||||
{
|
||||
if (!mGMPThread) {
|
||||
nsCOMPtr<mozIGeckoMediaPluginService> mps = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
|
||||
MOZ_ASSERT(mps);
|
||||
if (!mps) {
|
||||
return nullptr;
|
||||
}
|
||||
// Not really safe if we just grab to the mGMPThread, as we don't know
|
||||
// what thread we're running on and other threads may be trying to
|
||||
// access this without locks! However, debug only, and primary failure
|
||||
// mode outside of compiler-helped TSAN is a leak. But better would be
|
||||
// to use swap() under a lock.
|
||||
mps->GetThread(getter_AddRefs(mGMPThread));
|
||||
MOZ_ASSERT(mGMPThread);
|
||||
nsCOMPtr<mozIGeckoMediaPluginService> mps =
|
||||
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
|
||||
MOZ_ASSERT(mps);
|
||||
if (!mps) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return mGMPThread;
|
||||
// Note: GeckoMediaPluginService::GetThread() is threadsafe, and returns
|
||||
// nullptr if the GeckoMediaPluginService has started shutdown.
|
||||
nsCOMPtr<nsIThread> gmpThread;
|
||||
mps->GetThread(getter_AddRefs(gmpThread));
|
||||
return gmpThread;
|
||||
}
|
||||
|
||||
/* static */
|
||||
@ -589,7 +584,8 @@ GMPParent::RecvPGMPTimerConstructor(PGMPTimerParent* actor)
|
||||
PGMPTimerParent*
|
||||
GMPParent::AllocPGMPTimerParent()
|
||||
{
|
||||
GMPTimerParent* p = new GMPTimerParent(GMPThread());
|
||||
nsCOMPtr<nsIThread> thread = GMPThread();
|
||||
GMPTimerParent* p = new GMPTimerParent(thread);
|
||||
mTimers.AppendElement(p); // Released in DeallocPGMPTimerParent, or on shutdown.
|
||||
return p;
|
||||
}
|
||||
@ -734,6 +730,22 @@ GMPParent::ReadChromiumManifestFile(nsIFile* aFile)
|
||||
&GMPParent::ParseChromiumManifest, NS_ConvertUTF8toUTF16(json));
|
||||
}
|
||||
|
||||
static bool
|
||||
IsCDMAPISupported(const mozilla::dom::WidevineCDMManifest& aManifest)
|
||||
{
|
||||
nsresult ignored; // Note: ToInteger returns 0 on failure.
|
||||
int32_t moduleVersion = aManifest.mX_cdm_module_versions.ToInteger(&ignored);
|
||||
int32_t interfaceVersion =
|
||||
aManifest.mX_cdm_interface_versions.ToInteger(&ignored);
|
||||
int32_t hostVersion = aManifest.mX_cdm_host_versions.ToInteger(&ignored);
|
||||
if (MediaPrefs::EMEChromiumAPIEnabled()) {
|
||||
return ChromiumCDMAdapter::Supports(
|
||||
moduleVersion, interfaceVersion, hostVersion);
|
||||
}
|
||||
return WidevineAdapter::Supports(
|
||||
moduleVersion, interfaceVersion, hostVersion);
|
||||
}
|
||||
|
||||
RefPtr<GenericPromise>
|
||||
GMPParent::ParseChromiumManifest(const nsAString& aJSON)
|
||||
{
|
||||
@ -745,10 +757,7 @@ GMPParent::ParseChromiumManifest(const nsAString& 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))) {
|
||||
if (!IsCDMAPISupported(m)) {
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||
}
|
||||
|
||||
@ -786,7 +795,7 @@ GMPParent::ParseChromiumManifest(const nsAString& aJSON)
|
||||
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||
}
|
||||
|
||||
GMPCapability video(NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER));
|
||||
GMPCapability video;
|
||||
|
||||
nsCString codecsString = NS_ConvertUTF16toUTF8(m.mX_cdm_codecs);
|
||||
nsTArray<nsCString> codecs;
|
||||
@ -808,15 +817,20 @@ GMPParent::ParseChromiumManifest(const nsAString& aJSON)
|
||||
}
|
||||
|
||||
video.mAPITags.AppendElement(kEMEKeySystem);
|
||||
|
||||
if (MediaPrefs::EMEChromiumAPIEnabled()) {
|
||||
video.mAPIName = NS_LITERAL_CSTRING(CHROMIUM_CDM_API);
|
||||
mAdapter = NS_LITERAL_STRING("chromium");
|
||||
} else {
|
||||
video.mAPIName = NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER);
|
||||
mAdapter = NS_LITERAL_STRING("widevine");
|
||||
|
||||
GMPCapability decrypt(NS_LITERAL_CSTRING(GMP_API_DECRYPTOR));
|
||||
decrypt.mAPITags.AppendElement(kEMEKeySystem);
|
||||
mCapabilities.AppendElement(Move(decrypt));
|
||||
}
|
||||
mCapabilities.AppendElement(Move(video));
|
||||
|
||||
GMPCapability decrypt(NS_LITERAL_CSTRING(GMP_API_DECRYPTOR));
|
||||
|
||||
decrypt.mAPITags.AppendElement(kEMEKeySystem);
|
||||
mCapabilities.AppendElement(Move(decrypt));
|
||||
|
||||
mAdapter = NS_LITERAL_STRING("widevine");
|
||||
|
||||
return GenericPromise::CreateAndResolve(true, __func__);
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ public:
|
||||
void DeleteProcess();
|
||||
|
||||
GMPState State() const;
|
||||
nsIThread* GMPThread();
|
||||
nsCOMPtr<nsIThread> GMPThread();
|
||||
|
||||
// A GMP can either be a single instance shared across all NodeIds (like
|
||||
// in the OpenH264 case), or we can require a new plugin instance for every
|
||||
@ -207,7 +207,6 @@ private:
|
||||
|
||||
nsTArray<RefPtr<GMPTimerParent>> mTimers;
|
||||
nsTArray<RefPtr<GMPStorageParent>> mStorage;
|
||||
nsCOMPtr<nsIThread> mGMPThread;
|
||||
// NodeId the plugin is assigned to, or empty if the the plugin is not
|
||||
// assigned to a NodeId.
|
||||
nsCString mNodeId;
|
||||
|
@ -229,6 +229,47 @@ GeckoMediaPluginService::Init()
|
||||
return GetThread(getter_AddRefs(thread));
|
||||
}
|
||||
|
||||
RefPtr<GetCDMParentPromise>
|
||||
GeckoMediaPluginService::GetCDM(const NodeId& aNodeId,
|
||||
nsTArray<nsCString> aTags,
|
||||
GMPCrashHelper* aHelper)
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
|
||||
if (mShuttingDownOnGMPThread || aTags.IsEmpty()) {
|
||||
return GetCDMParentPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
||||
}
|
||||
|
||||
typedef MozPromiseHolder<GetCDMParentPromise> PromiseHolder;
|
||||
PromiseHolder* rawHolder(new PromiseHolder());
|
||||
RefPtr<GetCDMParentPromise> promise = rawHolder->Ensure(__func__);
|
||||
RefPtr<AbstractThread> thread(GetAbstractGMPThread());
|
||||
RefPtr<GMPCrashHelper> helper(aHelper);
|
||||
GetContentParent(
|
||||
aHelper, aNodeId, NS_LITERAL_CSTRING(CHROMIUM_CDM_API), aTags)
|
||||
->Then(thread,
|
||||
__func__,
|
||||
[rawHolder, helper](RefPtr<GMPContentParent::CloseBlocker> wrapper) {
|
||||
RefPtr<GMPContentParent> parent = wrapper->mParent;
|
||||
UniquePtr<PromiseHolder> holder(rawHolder);
|
||||
RefPtr<ChromiumCDMParent> cdm = parent->GetChromiumCDM();
|
||||
if (!parent) {
|
||||
holder->Reject(NS_ERROR_FAILURE, __func__);
|
||||
return;
|
||||
}
|
||||
if (helper) {
|
||||
cdm->SetCrashHelper(helper);
|
||||
}
|
||||
holder->Resolve(cdm, __func__);
|
||||
},
|
||||
[rawHolder] {
|
||||
UniquePtr<PromiseHolder> holder(rawHolder);
|
||||
holder->Reject(NS_ERROR_FAILURE, __func__);
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
void
|
||||
GeckoMediaPluginService::ShutdownGMPThread()
|
||||
{
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "GMPContentParent.h"
|
||||
#include "GMPCrashHelper.h"
|
||||
#include "ChromiumCDMParent.h"
|
||||
|
||||
template <class> struct already_AddRefed;
|
||||
|
||||
@ -35,7 +36,29 @@ extern LogModule* GetGMPLog();
|
||||
|
||||
namespace gmp {
|
||||
|
||||
typedef MozPromise<RefPtr<GMPContentParent::CloseBlocker>, nsresult, /* IsExclusive = */ true> GetGMPContentParentPromise;
|
||||
struct NodeId
|
||||
{
|
||||
NodeId(const nsAString& aOrigin,
|
||||
const nsAString& aTopLevelOrigin,
|
||||
const nsAString& aGMPName)
|
||||
: mOrigin(aOrigin)
|
||||
, mTopLevelOrigin(aTopLevelOrigin)
|
||||
, mGMPName(aGMPName)
|
||||
{
|
||||
}
|
||||
nsString mOrigin;
|
||||
nsString mTopLevelOrigin;
|
||||
nsString mGMPName;
|
||||
};
|
||||
|
||||
typedef MozPromise<RefPtr<GMPContentParent::CloseBlocker>,
|
||||
nsresult,
|
||||
/* IsExclusive = */ true>
|
||||
GetGMPContentParentPromise;
|
||||
typedef MozPromise<RefPtr<ChromiumCDMParent>,
|
||||
nsresult,
|
||||
/* IsExclusive = */ true>
|
||||
GetCDMParentPromise;
|
||||
|
||||
class GeckoMediaPluginService : public mozIGeckoMediaPluginService
|
||||
, public nsIObserver
|
||||
@ -47,6 +70,10 @@ public:
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
RefPtr<GetCDMParentPromise> GetCDM(const NodeId& aNodeId,
|
||||
nsTArray<nsCString> aTags,
|
||||
GMPCrashHelper* aHelper);
|
||||
|
||||
// mozIGeckoMediaPluginService
|
||||
NS_IMETHOD GetThread(nsIThread** aThread) override;
|
||||
NS_IMETHOD GetDecryptingGMPVideoDecoder(GMPCrashHelper* aHelper,
|
||||
@ -90,11 +117,17 @@ protected:
|
||||
|
||||
virtual void InitializePlugins(AbstractThread* aAbstractGMPThread) = 0;
|
||||
|
||||
virtual RefPtr<GetGMPContentParentPromise>
|
||||
GetContentParent(GMPCrashHelper* aHelper,
|
||||
const nsACString& aNodeId,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags) = 0;
|
||||
virtual RefPtr<GetGMPContentParentPromise> GetContentParent(
|
||||
GMPCrashHelper* aHelper,
|
||||
const nsACString& aNodeIdString,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags) = 0;
|
||||
|
||||
virtual RefPtr<GetGMPContentParentPromise> GetContentParent(
|
||||
GMPCrashHelper* aHelper,
|
||||
const NodeId& aNodeId,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags) = 0;
|
||||
|
||||
nsresult GMPDispatch(nsIRunnable* event, uint32_t flags = NS_DISPATCH_NORMAL);
|
||||
nsresult GMPDispatch(already_AddRefed<nsIRunnable> event, uint32_t flags = NS_DISPATCH_NORMAL);
|
||||
|
@ -54,7 +54,7 @@ GeckoMediaPluginServiceChild::GetSingleton()
|
||||
|
||||
RefPtr<GetGMPContentParentPromise>
|
||||
GeckoMediaPluginServiceChild::GetContentParent(GMPCrashHelper* aHelper,
|
||||
const nsACString& aNodeId,
|
||||
const nsACString& aNodeIdString,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags)
|
||||
{
|
||||
@ -64,7 +64,83 @@ GeckoMediaPluginServiceChild::GetContentParent(GMPCrashHelper* aHelper,
|
||||
RefPtr<GetGMPContentParentPromise> promise = rawHolder->Ensure(__func__);
|
||||
RefPtr<AbstractThread> thread(GetAbstractGMPThread());
|
||||
|
||||
nsCString nodeId(aNodeId);
|
||||
nsCString nodeIdString(aNodeIdString);
|
||||
nsCString api(aAPI);
|
||||
nsTArray<nsCString> tags(aTags);
|
||||
RefPtr<GMPCrashHelper> helper(aHelper);
|
||||
RefPtr<GeckoMediaPluginServiceChild> self(this);
|
||||
GetServiceChild()->Then(
|
||||
thread,
|
||||
__func__,
|
||||
[self, nodeIdString, api, tags, helper, rawHolder](GMPServiceChild* child) {
|
||||
UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>> holder(rawHolder);
|
||||
nsresult rv;
|
||||
|
||||
nsTArray<base::ProcessId> alreadyBridgedTo;
|
||||
child->GetAlreadyBridgedTo(alreadyBridgedTo);
|
||||
|
||||
base::ProcessId otherProcess;
|
||||
nsCString displayName;
|
||||
uint32_t pluginId = 0;
|
||||
ipc::Endpoint<PGMPContentParent> endpoint;
|
||||
bool ok = child->SendLaunchGMP(nodeIdString,
|
||||
api,
|
||||
tags,
|
||||
alreadyBridgedTo,
|
||||
&pluginId,
|
||||
&otherProcess,
|
||||
&displayName,
|
||||
&endpoint,
|
||||
&rv);
|
||||
if (helper && pluginId) {
|
||||
// Note: Even if the launch failed, we need to connect the crash
|
||||
// helper so that if the launch failed due to the plugin crashing,
|
||||
// we can report the crash via the crash reporter. The crash
|
||||
// handling notification will arrive shortly if the launch failed
|
||||
// due to the plugin crashing.
|
||||
self->ConnectCrashHelper(pluginId, helper);
|
||||
}
|
||||
|
||||
if (!ok || NS_FAILED(rv)) {
|
||||
LOGD(("GeckoMediaPluginServiceChild::GetContentParent SendLaunchGMP "
|
||||
"failed rv=0x%x",
|
||||
static_cast<uint32_t>(rv)));
|
||||
holder->Reject(rv, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<GMPContentParent> parent =
|
||||
child->GetBridgedGMPContentParent(otherProcess, Move(endpoint));
|
||||
if (!alreadyBridgedTo.Contains(otherProcess)) {
|
||||
parent->SetDisplayName(displayName);
|
||||
parent->SetPluginId(pluginId);
|
||||
}
|
||||
RefPtr<GMPContentParent::CloseBlocker> blocker(
|
||||
new GMPContentParent::CloseBlocker(parent));
|
||||
holder->Resolve(blocker, __func__);
|
||||
},
|
||||
[rawHolder](nsresult rv) {
|
||||
UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>> holder(rawHolder);
|
||||
holder->Reject(rv, __func__);
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
RefPtr<GetGMPContentParentPromise>
|
||||
GeckoMediaPluginServiceChild::GetContentParent(GMPCrashHelper* aHelper,
|
||||
const NodeId& aNodeId,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags)
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
|
||||
MozPromiseHolder<GetGMPContentParentPromise>* rawHolder =
|
||||
new MozPromiseHolder<GetGMPContentParentPromise>();
|
||||
RefPtr<GetGMPContentParentPromise> promise = rawHolder->Ensure(__func__);
|
||||
RefPtr<AbstractThread> thread(GetAbstractGMPThread());
|
||||
|
||||
NodeIdData nodeId(aNodeId.mOrigin, aNodeId.mTopLevelOrigin, aNodeId.mGMPName);
|
||||
nsCString api(aAPI);
|
||||
nsTArray<nsCString> tags(aTags);
|
||||
RefPtr<GMPCrashHelper> helper(aHelper);
|
||||
@ -82,15 +158,15 @@ GeckoMediaPluginServiceChild::GetContentParent(GMPCrashHelper* aHelper,
|
||||
uint32_t pluginId = 0;
|
||||
ipc::Endpoint<PGMPContentParent> endpoint;
|
||||
|
||||
bool ok = child->SendLaunchGMP(nodeId,
|
||||
api,
|
||||
tags,
|
||||
alreadyBridgedTo,
|
||||
&pluginId,
|
||||
&otherProcess,
|
||||
&displayName,
|
||||
&endpoint,
|
||||
&rv);
|
||||
bool ok = child->SendLaunchGMPForNodeId(nodeId,
|
||||
api,
|
||||
tags,
|
||||
alreadyBridgedTo,
|
||||
&pluginId,
|
||||
&otherProcess,
|
||||
&displayName,
|
||||
&endpoint,
|
||||
&rv);
|
||||
|
||||
if (helper && pluginId) {
|
||||
// Note: Even if the launch failed, we need to connect the crash
|
||||
|
@ -48,11 +48,17 @@ protected:
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
virtual RefPtr<GetGMPContentParentPromise>
|
||||
GetContentParent(GMPCrashHelper* aHelper,
|
||||
const nsACString& aNodeId,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags) override;
|
||||
virtual RefPtr<GetGMPContentParentPromise> GetContentParent(
|
||||
GMPCrashHelper* aHelper,
|
||||
const nsACString& aNodeIdString,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags) override;
|
||||
|
||||
RefPtr<GetGMPContentParentPromise> GetContentParent(
|
||||
GMPCrashHelper* aHelper,
|
||||
const NodeId& aNodeId,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags) override;
|
||||
|
||||
private:
|
||||
friend class OpenPGMPServiceChild;
|
||||
|
@ -353,10 +353,11 @@ GeckoMediaPluginServiceParent::EnsureInitialized() {
|
||||
}
|
||||
|
||||
RefPtr<GetGMPContentParentPromise>
|
||||
GeckoMediaPluginServiceParent::GetContentParent(GMPCrashHelper* aHelper,
|
||||
const nsACString& aNodeId,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags)
|
||||
GeckoMediaPluginServiceParent::GetContentParent(
|
||||
GMPCrashHelper* aHelper,
|
||||
const nsACString& aNodeIdString,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags)
|
||||
{
|
||||
RefPtr<AbstractThread> thread(GetAbstractGMPThread());
|
||||
if (!thread) {
|
||||
@ -367,14 +368,16 @@ GeckoMediaPluginServiceParent::GetContentParent(GMPCrashHelper* aHelper,
|
||||
PromiseHolder* rawHolder = new PromiseHolder();
|
||||
RefPtr<GeckoMediaPluginServiceParent> self(this);
|
||||
RefPtr<GetGMPContentParentPromise> promise = rawHolder->Ensure(__func__);
|
||||
nsCString nodeId(aNodeId);
|
||||
nsCString nodeIdString(aNodeIdString);
|
||||
nsTArray<nsCString> tags(aTags);
|
||||
nsCString api(aAPI);
|
||||
RefPtr<GMPCrashHelper> helper(aHelper);
|
||||
EnsureInitialized()->Then(thread, __func__,
|
||||
[self, tags, api, nodeId, helper, rawHolder]() -> void {
|
||||
EnsureInitialized()->Then(
|
||||
thread,
|
||||
__func__,
|
||||
[self, tags, api, nodeIdString, helper, rawHolder]() -> void {
|
||||
UniquePtr<PromiseHolder> holder(rawHolder);
|
||||
RefPtr<GMPParent> gmp = self->SelectPluginForAPI(nodeId, api, tags);
|
||||
RefPtr<GMPParent> gmp = self->SelectPluginForAPI(nodeIdString, api, tags);
|
||||
LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)self, (void *)gmp, api.get()));
|
||||
if (!gmp) {
|
||||
NS_WARNING("GeckoMediaPluginServiceParent::GetContentParentFrom failed");
|
||||
@ -393,6 +396,25 @@ GeckoMediaPluginServiceParent::GetContentParent(GMPCrashHelper* aHelper,
|
||||
return promise;
|
||||
}
|
||||
|
||||
RefPtr<GetGMPContentParentPromise>
|
||||
GeckoMediaPluginServiceParent::GetContentParent(
|
||||
GMPCrashHelper* aHelper,
|
||||
const NodeId& aNodeId,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags)
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
|
||||
nsCString nodeIdString;
|
||||
nsresult rv = GetNodeId(
|
||||
aNodeId.mOrigin, aNodeId.mTopLevelOrigin, aNodeId.mGMPName, nodeIdString);
|
||||
if (NS_FAILED(rv)) {
|
||||
return GetGMPContentParentPromise::CreateAndReject(NS_ERROR_FAILURE,
|
||||
__func__);
|
||||
}
|
||||
return GetContentParent(aHelper, nodeIdString, aAPI, aTags);
|
||||
}
|
||||
|
||||
void
|
||||
GeckoMediaPluginServiceParent::InitializePlugins(
|
||||
AbstractThread* aAbstractGMPThread)
|
||||
@ -1736,6 +1758,36 @@ GMPServiceParent::RecvLaunchGMP(const nsCString& aNodeId,
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPServiceParent::RecvLaunchGMPForNodeId(
|
||||
const NodeIdData& aNodeId,
|
||||
const nsCString& aApi,
|
||||
nsTArray<nsCString>&& aTags,
|
||||
nsTArray<ProcessId>&& aAlreadyBridgedTo,
|
||||
uint32_t* aOutPluginId,
|
||||
ProcessId* aOutId,
|
||||
nsCString* aOutDisplayName,
|
||||
Endpoint<PGMPContentParent>* aOutEndpoint,
|
||||
nsresult* aOutRv)
|
||||
{
|
||||
nsCString nodeId;
|
||||
nsresult rv = mService->GetNodeId(
|
||||
aNodeId.mOrigin(), aNodeId.mTopLevelOrigin(), aNodeId.mGMPName(), nodeId);
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
*aOutRv = rv;
|
||||
return IPC_OK();
|
||||
}
|
||||
return RecvLaunchGMP(nodeId,
|
||||
aApi,
|
||||
Move(aTags),
|
||||
Move(aAlreadyBridgedTo),
|
||||
aOutPluginId,
|
||||
aOutId,
|
||||
aOutDisplayName,
|
||||
aOutEndpoint,
|
||||
aOutRv);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
GMPServiceParent::RecvGetGMPNodeId(const nsString& aOrigin,
|
||||
const nsString& aTopLevelOrigin,
|
||||
|
@ -117,11 +117,17 @@ protected:
|
||||
RefPtr<GenericPromise::AllPromiseType> LoadFromEnvironment();
|
||||
RefPtr<GenericPromise> AddOnGMPThread(nsString aDirectory);
|
||||
|
||||
virtual RefPtr<GetGMPContentParentPromise>
|
||||
GetContentParent(GMPCrashHelper* aHelper,
|
||||
const nsACString& aNodeId,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags) override;
|
||||
virtual RefPtr<GetGMPContentParentPromise> GetContentParent(
|
||||
GMPCrashHelper* aHelper,
|
||||
const nsACString& aNodeIdString,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags) override;
|
||||
|
||||
RefPtr<GetGMPContentParentPromise> GetContentParent(
|
||||
GMPCrashHelper* aHelper,
|
||||
const NodeId& aNodeId,
|
||||
const nsCString& aAPI,
|
||||
const nsTArray<nsCString>& aTags) override;
|
||||
|
||||
private:
|
||||
// Creates a copy of aOriginal. Note that the caller is responsible for
|
||||
@ -223,23 +229,34 @@ public:
|
||||
}
|
||||
virtual ~GMPServiceParent();
|
||||
|
||||
mozilla::ipc::IPCResult RecvGetGMPNodeId(const nsString& aOrigin,
|
||||
const nsString& aTopLevelOrigin,
|
||||
const nsString& aGMPName,
|
||||
nsCString* aID) override;
|
||||
ipc::IPCResult RecvGetGMPNodeId(const nsString& aOrigin,
|
||||
const nsString& aTopLevelOrigin,
|
||||
const nsString& aGMPName,
|
||||
nsCString* aID) override;
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
static bool Create(Endpoint<PGMPServiceParent>&& aGMPService);
|
||||
|
||||
mozilla::ipc::IPCResult RecvLaunchGMP(const nsCString& aNodeId,
|
||||
const nsCString& aAPI,
|
||||
nsTArray<nsCString>&& aTags,
|
||||
nsTArray<ProcessId>&& aAlreadyBridgedTo,
|
||||
uint32_t* aOutPluginId,
|
||||
ProcessId* aOutID,
|
||||
nsCString* aOutDisplayName,
|
||||
Endpoint<PGMPContentParent>* aOutEndpoint,
|
||||
nsresult* aOutRv) override;
|
||||
ipc::IPCResult RecvLaunchGMP(const nsCString& aNodeId,
|
||||
const nsCString& aAPI,
|
||||
nsTArray<nsCString>&& aTags,
|
||||
nsTArray<ProcessId>&& aAlreadyBridgedTo,
|
||||
uint32_t* aOutPluginId,
|
||||
ProcessId* aOutID,
|
||||
nsCString* aOutDisplayName,
|
||||
Endpoint<PGMPContentParent>* aOutEndpoint,
|
||||
nsresult* aOutRv) override;
|
||||
|
||||
ipc::IPCResult RecvLaunchGMPForNodeId(
|
||||
const NodeIdData& nodeId,
|
||||
const nsCString& aAPI,
|
||||
nsTArray<nsCString>&& aTags,
|
||||
nsTArray<ProcessId>&& aAlreadyBridgedTo,
|
||||
uint32_t* aOutPluginId,
|
||||
ProcessId* aOutID,
|
||||
nsCString* aOutDisplayName,
|
||||
Endpoint<PGMPContentParent>* aOutEndpoint,
|
||||
nsresult* aOutRv) override;
|
||||
|
||||
private:
|
||||
void CloseTransport(Monitor* aSyncMonitor, bool* aCompleted);
|
||||
|
@ -9,6 +9,12 @@ using GMPMediaKeyStatus from "gmp-decryption.h";
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
||||
struct NodeIdData {
|
||||
nsString mOrigin;
|
||||
nsString mTopLevelOrigin;
|
||||
nsString mGMPName;
|
||||
};
|
||||
|
||||
struct GMPDecryptionData {
|
||||
uint8_t[] mKeyId;
|
||||
uint8_t[] mIV;
|
||||
@ -54,5 +60,48 @@ struct GMPKeyInformation {
|
||||
GMPMediaKeyStatus status;
|
||||
};
|
||||
|
||||
struct CDMInputBuffer {
|
||||
uint8_t[] mData;
|
||||
uint8_t[] mKeyId;
|
||||
uint8_t[] mIV;
|
||||
int64_t mTimestamp;
|
||||
int64_t mDuration;
|
||||
uint16_t[] mClearBytes;
|
||||
uint32_t[] mCipherBytes;
|
||||
bool mIsEncrypted;
|
||||
};
|
||||
|
||||
struct CDMVideoDecoderConfig {
|
||||
uint32_t mCodec;
|
||||
uint32_t mProfile;
|
||||
uint32_t mFormat;
|
||||
int32_t mImageWidth;
|
||||
int32_t mImageHeight;
|
||||
uint8_t[] mExtraData;
|
||||
};
|
||||
|
||||
struct CDMKeyInformation {
|
||||
uint8_t[] mKeyId;
|
||||
uint32_t mStatus;
|
||||
uint32_t mSystemCode;
|
||||
};
|
||||
|
||||
struct CDMVideoPlane {
|
||||
uint32_t mPlaneOffset;
|
||||
uint32_t mStride;
|
||||
};
|
||||
|
||||
struct CDMVideoFrame {
|
||||
uint32_t mFormat;
|
||||
int32_t mImageWidth;
|
||||
int32_t mImageHeight;
|
||||
uint8_t[] mData;
|
||||
CDMVideoPlane mYPlane;
|
||||
CDMVideoPlane mUPlane;
|
||||
CDMVideoPlane mVPlane;
|
||||
int64_t mTimestamp;
|
||||
int64_t mDuration;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "prio.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "GMPService.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -230,4 +231,12 @@ LogToConsole(const nsAString& aMsg)
|
||||
console->LogStringMessage(msg.get());
|
||||
}
|
||||
|
||||
RefPtr<AbstractThread>
|
||||
GetGMPAbstractThread()
|
||||
{
|
||||
RefPtr<gmp::GeckoMediaPluginService> service =
|
||||
gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
|
||||
return service ? service->GetAbstractGMPThread() : nullptr;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -7,10 +7,14 @@
|
||||
#define GMPUtils_h_
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/AbstractThread.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsClassHashtable.h"
|
||||
|
||||
#define CHROMIUM_CDM_API "chromium-cdm8-host4"
|
||||
|
||||
class nsIFile;
|
||||
class nsCString;
|
||||
class nsISimpleEnumerator;
|
||||
@ -81,6 +85,9 @@ HaveGMPFor(const nsCString& aAPI,
|
||||
void
|
||||
LogToConsole(const nsAString& aMsg);
|
||||
|
||||
RefPtr<AbstractThread>
|
||||
GetGMPAbstractThread();
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
@ -209,7 +209,8 @@ GMPVideoDecoderParent::Reset()
|
||||
LogToBrowserConsole(NS_LITERAL_STRING("GMPVideoDecoderParent timed out waiting for ResetComplete()"));
|
||||
});
|
||||
CancelResetCompleteTimeout();
|
||||
mResetCompleteTimeout = SimpleTimer::Create(task, 5000, mPlugin->GMPThread());
|
||||
nsCOMPtr<nsIThread> thread = mPlugin->GMPThread();
|
||||
mResetCompleteTimeout = SimpleTimer::Create(task, 5000, thread);
|
||||
|
||||
// Async IPC, we don't have access to a return value.
|
||||
return NS_OK;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "runnable_utils.h"
|
||||
#include "GMPUtils.h"
|
||||
#include "mozilla/SystemGroup.h"
|
||||
#include "GMPCrashHelper.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
106
dom/media/gmp/PChromiumCDM.ipdl
Normal file
106
dom/media/gmp/PChromiumCDM.ipdl
Normal file
@ -0,0 +1,106 @@
|
||||
/* -*- 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 protocol PGMPContent;
|
||||
include GMPTypes;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
||||
async protocol PChromiumCDM
|
||||
{
|
||||
manager PGMPContent;
|
||||
child:
|
||||
|
||||
// cdm::ContentDecryptionModule8
|
||||
async Init(bool aAllowDistinctiveIdentifier,
|
||||
bool aAllowPersistentState);
|
||||
|
||||
async SetServerCertificate(uint32_t aPromiseId,
|
||||
uint8_t[] aServerCert);
|
||||
|
||||
async CreateSessionAndGenerateRequest(uint32_t aPromiseId,
|
||||
uint32_t aSessionType,
|
||||
uint32_t aInitDataType,
|
||||
uint8_t[] aInitData);
|
||||
|
||||
async LoadSession(uint32_t aPromiseId,
|
||||
uint32_t aSessionType,
|
||||
nsCString aSessionId);
|
||||
|
||||
async UpdateSession(uint32_t aPromiseId,
|
||||
nsCString aSessionId,
|
||||
uint8_t[] aResponse);
|
||||
|
||||
async CloseSession(uint32_t aPromiseId,
|
||||
nsCString aSessionId);
|
||||
|
||||
async RemoveSession(uint32_t aPromiseId,
|
||||
nsCString aSessionId);
|
||||
|
||||
async Decrypt(uint32_t aId, CDMInputBuffer aBuffer);
|
||||
|
||||
async InitializeVideoDecoder(CDMVideoDecoderConfig aConfig);
|
||||
|
||||
async DeinitializeVideoDecoder();
|
||||
|
||||
async ResetVideoDecoder();
|
||||
|
||||
async DecryptAndDecodeFrame(CDMInputBuffer aBuffer);
|
||||
|
||||
async Drain();
|
||||
|
||||
async Destroy();
|
||||
|
||||
parent:
|
||||
async __delete__();
|
||||
|
||||
// cdm::Host8
|
||||
async OnResolveNewSessionPromise(uint32_t aPromiseId, nsCString aSessionId);
|
||||
|
||||
async OnResolvePromise(uint32_t aPromiseId);
|
||||
|
||||
async OnRejectPromise(uint32_t aPromiseId,
|
||||
uint32_t aError,
|
||||
uint32_t aSystemCode,
|
||||
nsCString aErrorMessage);
|
||||
|
||||
async OnSessionMessage(nsCString aSessionId,
|
||||
uint32_t aMessageType,
|
||||
uint8_t[] aMessage);
|
||||
|
||||
async OnSessionKeysChange(nsCString aSessionId,
|
||||
CDMKeyInformation[] aKeysInfo);
|
||||
|
||||
async OnExpirationChange(nsCString aSessionId,
|
||||
double aSecondsSinceEpoch);
|
||||
|
||||
async OnSessionClosed(nsCString aSessionId);
|
||||
|
||||
async OnLegacySessionError(nsCString aSessionId,
|
||||
uint32_t aError,
|
||||
uint32_t aSystemCode,
|
||||
nsCString aMessage);
|
||||
|
||||
async ResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccessful);
|
||||
|
||||
// Return values of cdm::ContentDecryptionModule8::Decrypt
|
||||
async Decrypted(uint32_t aId, uint32_t aStatus, uint8_t[] aData);
|
||||
|
||||
async OnDecoderInitDone(uint32_t aStatus);
|
||||
|
||||
// Return values of cdm::ContentDecryptionModule8::DecryptAndDecodeFrame
|
||||
async Decoded(CDMVideoFrame aFrame);
|
||||
async DecodeFailed(uint32_t aStatus);
|
||||
|
||||
async ResetVideoDecoderComplete();
|
||||
|
||||
async DrainComplete();
|
||||
|
||||
async Shutdown();
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
@ -6,6 +6,7 @@
|
||||
include protocol PGMPVideoDecoder;
|
||||
include protocol PGMPVideoEncoder;
|
||||
include protocol PGMPDecryptor;
|
||||
include protocol PChromiumCDM;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
@ -15,11 +16,13 @@ intr protocol PGMPContent
|
||||
manages PGMPDecryptor;
|
||||
manages PGMPVideoDecoder;
|
||||
manages PGMPVideoEncoder;
|
||||
manages PChromiumCDM;
|
||||
|
||||
child:
|
||||
async PGMPDecryptor();
|
||||
async PGMPVideoDecoder(uint32_t aDecryptorId);
|
||||
async PGMPVideoEncoder();
|
||||
async PChromiumCDM();
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
|
@ -4,6 +4,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
include protocol PGMPContent;
|
||||
include GMPTypes;
|
||||
|
||||
using base::ProcessId from "base/process.h";
|
||||
|
||||
@ -23,6 +24,16 @@ parent:
|
||||
Endpoint<PGMPContentParent> endpoint,
|
||||
nsresult aResult);
|
||||
|
||||
sync LaunchGMPForNodeId(NodeIdData nodeId,
|
||||
nsCString api,
|
||||
nsCString[] tags,
|
||||
ProcessId[] alreadyBridgedTo)
|
||||
returns (uint32_t pluginId,
|
||||
ProcessId id,
|
||||
nsCString displayName,
|
||||
Endpoint<PGMPContentParent> endpoint,
|
||||
nsresult aResult);
|
||||
|
||||
sync GetGMPNodeId(nsString origin, nsString topLevelOrigin, nsString gmpName)
|
||||
returns (nsCString id);
|
||||
};
|
||||
|
@ -12,6 +12,8 @@ XPIDL_SOURCES += [
|
||||
]
|
||||
|
||||
EXPORTS += [
|
||||
'ChromiumCDMParent.h',
|
||||
'ChromiumCDMProxy.h',
|
||||
'DecryptJob.h',
|
||||
'gmp-api/gmp-decryption.h',
|
||||
'gmp-api/gmp-entrypoints.h',
|
||||
@ -68,6 +70,10 @@ EXPORTS += [
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ChromiumCDMAdapter.cpp',
|
||||
'ChromiumCDMChild.cpp',
|
||||
'ChromiumCDMParent.cpp',
|
||||
'ChromiumCDMProxy.cpp',
|
||||
'DecryptJob.cpp',
|
||||
'GMPCDMCallbackProxy.cpp',
|
||||
'GMPCDMProxy.cpp',
|
||||
@ -111,6 +117,7 @@ DIRS += [
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'GMPTypes.ipdlh',
|
||||
'PChromiumCDM.ipdl',
|
||||
'PGMP.ipdl',
|
||||
'PGMPContent.ipdl',
|
||||
'PGMPDecryptor.ipdl',
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "gmp-api/gmp-video-codec.h"
|
||||
#include "gmp-api/gmp-platform.h"
|
||||
|
||||
static const GMPPlatformAPI* sPlatform = nullptr;
|
||||
const GMPPlatformAPI* sPlatform = nullptr;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -16,7 +16,9 @@ SOURCES += [
|
||||
|
||||
EXPORTS += [
|
||||
'WidevineDecryptor.h',
|
||||
'WidevineUtils.h'
|
||||
'WidevineFileIO.h',
|
||||
'WidevineUtils.h',
|
||||
'WidevineVideoFrame.h'
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
148
dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.cpp
Normal file
148
dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.cpp
Normal file
@ -0,0 +1,148 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ChromiumCDMVideoDecoder.h"
|
||||
#include "ChromiumCDMProxy.h"
|
||||
#include "content_decryption_module.h"
|
||||
#include "GMPService.h"
|
||||
#include "GMPVideoDecoder.h"
|
||||
#include "MP4Decoder.h"
|
||||
#include "VPXDecoder.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
ChromiumCDMVideoDecoder::ChromiumCDMVideoDecoder(
|
||||
const GMPVideoDecoderParams& aParams,
|
||||
CDMProxy* aCDMProxy)
|
||||
: mCDMParent(aCDMProxy->AsChromiumCDMProxy()->GetCDMParent())
|
||||
, mConfig(aParams.mConfig)
|
||||
, mCrashHelper(aParams.mCrashHelper)
|
||||
, mGMPThread(GetGMPAbstractThread())
|
||||
, mImageContainer(aParams.mImageContainer)
|
||||
{
|
||||
}
|
||||
|
||||
ChromiumCDMVideoDecoder::~ChromiumCDMVideoDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
ToCDMH264Profile(uint8_t aProfile)
|
||||
{
|
||||
switch (aProfile) {
|
||||
case 66:
|
||||
return cdm::VideoDecoderConfig::kH264ProfileBaseline;
|
||||
case 77:
|
||||
return cdm::VideoDecoderConfig::kH264ProfileMain;
|
||||
case 88:
|
||||
return cdm::VideoDecoderConfig::kH264ProfileExtended;
|
||||
case 100:
|
||||
return cdm::VideoDecoderConfig::kH264ProfileHigh;
|
||||
case 110:
|
||||
return cdm::VideoDecoderConfig::kH264ProfileHigh10;
|
||||
case 122:
|
||||
return cdm::VideoDecoderConfig::kH264ProfileHigh422;
|
||||
case 144:
|
||||
return cdm::VideoDecoderConfig::kH264ProfileHigh444Predictive;
|
||||
}
|
||||
return cdm::VideoDecoderConfig::kUnknownVideoCodecProfile;
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder::InitPromise>
|
||||
ChromiumCDMVideoDecoder::Init()
|
||||
{
|
||||
if (!mCDMParent) {
|
||||
// Must have failed to get the CDMParent from the ChromiumCDMProxy
|
||||
// in our constructor; the MediaKeys must have shut down the CDM
|
||||
// before we had a chance to start up the decoder.
|
||||
return InitPromise::CreateAndReject(
|
||||
NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
|
||||
}
|
||||
|
||||
gmp::CDMVideoDecoderConfig config;
|
||||
if (MP4Decoder::IsH264(mConfig.mMimeType)) {
|
||||
config.mCodec() = cdm::VideoDecoderConfig::kCodecH264;
|
||||
config.mProfile() =
|
||||
ToCDMH264Profile(mConfig.mExtraData->SafeElementAt(1, 0));
|
||||
config.mExtraData() = *mConfig.mExtraData;
|
||||
mConvertToAnnexB = true;
|
||||
} else if (VPXDecoder::IsVP8(mConfig.mMimeType)) {
|
||||
config.mCodec() = cdm::VideoDecoderConfig::kCodecVp8;
|
||||
config.mProfile() = cdm::VideoDecoderConfig::kProfileNotNeeded;
|
||||
} else if (VPXDecoder::IsVP9(mConfig.mMimeType)) {
|
||||
config.mCodec() = cdm::VideoDecoderConfig::kCodecVp9;
|
||||
config.mProfile() = cdm::VideoDecoderConfig::kProfileNotNeeded;
|
||||
} else {
|
||||
return MediaDataDecoder::InitPromise::CreateAndReject(
|
||||
NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__);
|
||||
}
|
||||
config.mImageWidth() = mConfig.mImage.width;
|
||||
config.mImageHeight() = mConfig.mImage.height;
|
||||
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = mCDMParent;
|
||||
VideoInfo info = mConfig;
|
||||
RefPtr<layers::ImageContainer> imageContainer = mImageContainer;
|
||||
return InvokeAsync(
|
||||
mGMPThread, __func__, [cdm, config, info, imageContainer]() {
|
||||
return cdm->InitializeVideoDecoder(config, info, imageContainer);
|
||||
});
|
||||
}
|
||||
|
||||
const char*
|
||||
ChromiumCDMVideoDecoder::GetDescriptionName() const
|
||||
{
|
||||
return "Chromium CDM video decoder";
|
||||
}
|
||||
|
||||
MediaDataDecoder::ConversionRequired
|
||||
ChromiumCDMVideoDecoder::NeedsConversion() const
|
||||
{
|
||||
return mConvertToAnnexB ? ConversionRequired::kNeedAnnexB
|
||||
: ConversionRequired::kNeedNone;
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder::DecodePromise>
|
||||
ChromiumCDMVideoDecoder::Decode(MediaRawData* aSample)
|
||||
{
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = mCDMParent;
|
||||
RefPtr<MediaRawData> sample = aSample;
|
||||
return InvokeAsync(mGMPThread, __func__, [cdm, sample]() {
|
||||
return cdm->DecryptAndDecodeFrame(sample);
|
||||
});
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder::FlushPromise>
|
||||
ChromiumCDMVideoDecoder::Flush()
|
||||
{
|
||||
MOZ_ASSERT(mCDMParent);
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = mCDMParent;
|
||||
return InvokeAsync(
|
||||
mGMPThread, __func__, [cdm]() { return cdm->FlushVideoDecoder(); });
|
||||
}
|
||||
|
||||
RefPtr<MediaDataDecoder::DecodePromise>
|
||||
ChromiumCDMVideoDecoder::Drain()
|
||||
{
|
||||
MOZ_ASSERT(mCDMParent);
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = mCDMParent;
|
||||
return InvokeAsync(mGMPThread, __func__, [cdm]() { return cdm->Drain(); });
|
||||
}
|
||||
|
||||
RefPtr<ShutdownPromise>
|
||||
ChromiumCDMVideoDecoder::Shutdown()
|
||||
{
|
||||
if (!mCDMParent) {
|
||||
// Must have failed to get the CDMParent from the ChromiumCDMProxy
|
||||
// in our constructor; the MediaKeys must have shut down the CDM
|
||||
// before we had a chance to start up the decoder.
|
||||
return ShutdownPromise::CreateAndResolve(true, __func__);
|
||||
}
|
||||
RefPtr<gmp::ChromiumCDMParent> cdm = mCDMParent;
|
||||
return InvokeAsync(
|
||||
mGMPThread, __func__, [cdm]() { return cdm->ShutdownVideoDecoder(); });
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
46
dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.h
Normal file
46
dom/media/platforms/agnostic/eme/ChromiumCDMVideoDecoder.h
Normal file
@ -0,0 +1,46 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef ChromiumCDMVideoDecoder_h_
|
||||
#define ChromiumCDMVideoDecoder_h_
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "ChromiumCDMParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class CDMProxy;
|
||||
struct GMPVideoDecoderParams;
|
||||
|
||||
class ChromiumCDMVideoDecoder : public MediaDataDecoder
|
||||
{
|
||||
public:
|
||||
ChromiumCDMVideoDecoder(const GMPVideoDecoderParams& aParams,
|
||||
CDMProxy* aCDMProxy);
|
||||
|
||||
RefPtr<InitPromise> Init() override;
|
||||
RefPtr<DecodePromise> Decode(MediaRawData* aSample) override;
|
||||
RefPtr<FlushPromise> Flush() override;
|
||||
RefPtr<DecodePromise> Drain() override;
|
||||
RefPtr<ShutdownPromise> Shutdown() override;
|
||||
const char* GetDescriptionName() const override;
|
||||
ConversionRequired NeedsConversion() const override;
|
||||
|
||||
private:
|
||||
~ChromiumCDMVideoDecoder();
|
||||
|
||||
RefPtr<gmp::ChromiumCDMParent> mCDMParent;
|
||||
const VideoInfo mConfig;
|
||||
RefPtr<GMPCrashHelper> mCrashHelper;
|
||||
RefPtr<AbstractThread> mGMPThread;
|
||||
RefPtr<layers::ImageContainer> mImageContainer;
|
||||
MozPromiseHolder<InitPromise> mInitPromise;
|
||||
bool mConvertToAnnexB = false;
|
||||
};
|
||||
|
||||
} // mozilla
|
||||
|
||||
#endif // ChromiumCDMVideoDecoder_h_
|
@ -353,7 +353,11 @@ EMEDecoderModule::CreateVideoDecoder(const CreateDecoderParams& aParams)
|
||||
RefPtr<MediaDataDecoderProxy> wrapper =
|
||||
CreateDecoderWrapper(mProxy, aParams);
|
||||
auto params = GMPVideoDecoderParams(aParams);
|
||||
wrapper->SetProxyTarget(new EMEVideoDecoder(mProxy, params));
|
||||
if (MediaPrefs::EMEChromiumAPIEnabled()) {
|
||||
wrapper->SetProxyTarget(new ChromiumCDMVideoDecoder(params, mProxy));
|
||||
} else {
|
||||
wrapper->SetProxyTarget(new EMEVideoDecoder(mProxy, params));
|
||||
}
|
||||
return wrapper.forget();
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS += [
|
||||
'ChromiumCDMVideoDecoder.h',
|
||||
'DecryptThroughputLimit.h',
|
||||
'EMEDecoderModule.h',
|
||||
'EMEVideoDecoder.h',
|
||||
@ -12,6 +13,7 @@ EXPORTS += [
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ChromiumCDMVideoDecoder.cpp',
|
||||
'EMEDecoderModule.cpp',
|
||||
'EMEVideoDecoder.cpp',
|
||||
'SamplesWaitingForKey.cpp',
|
||||
|
@ -79,19 +79,20 @@ GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType,
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCString api = MediaPrefs::EMEChromiumAPIEnabled()
|
||||
? NS_LITERAL_CSTRING(CHROMIUM_CDM_API)
|
||||
: NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER);
|
||||
|
||||
if (MP4Decoder::IsH264(aMimeType)) {
|
||||
return HaveGMPFor(NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
|
||||
{ NS_LITERAL_CSTRING("h264"), aGMP.value()});
|
||||
return HaveGMPFor(api, { NS_LITERAL_CSTRING("h264"), aGMP.value()});
|
||||
}
|
||||
|
||||
if (VPXDecoder::IsVP9(aMimeType)) {
|
||||
return HaveGMPFor(NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
|
||||
{ NS_LITERAL_CSTRING("vp9"), aGMP.value()});
|
||||
return HaveGMPFor(api, { NS_LITERAL_CSTRING("vp9"), aGMP.value()});
|
||||
}
|
||||
|
||||
if (VPXDecoder::IsVP8(aMimeType)) {
|
||||
return HaveGMPFor(NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
|
||||
{ NS_LITERAL_CSTRING("vp8"), aGMP.value()});
|
||||
return HaveGMPFor(api, { NS_LITERAL_CSTRING("vp8"), aGMP.value()});
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -33,7 +33,7 @@ CompositionTransaction::CompositionTransaction(
|
||||
, mReplaceLength(aReplaceLength)
|
||||
, mRanges(aTextRangeArray)
|
||||
, mStringToInsert(aStringToInsert)
|
||||
, mEditorBase(aEditorBase)
|
||||
, mEditorBase(&aEditorBase)
|
||||
, mRangeUpdater(aRangeUpdater)
|
||||
, mFixed(false)
|
||||
{
|
||||
@ -45,6 +45,7 @@ CompositionTransaction::~CompositionTransaction()
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(CompositionTransaction, EditTransactionBase,
|
||||
mEditorBase,
|
||||
mTextNode)
|
||||
// mRangeList can't lead to cycles
|
||||
|
||||
@ -60,9 +61,13 @@ NS_IMPL_RELEASE_INHERITED(CompositionTransaction, EditTransactionBase)
|
||||
NS_IMETHODIMP
|
||||
CompositionTransaction::DoTransaction()
|
||||
{
|
||||
if (NS_WARN_IF(!mEditorBase)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// Fail before making any changes if there's no selection controller
|
||||
nsCOMPtr<nsISelectionController> selCon;
|
||||
mEditorBase.GetSelectionController(getter_AddRefs(selCon));
|
||||
mEditorBase->GetSelectionController(getter_AddRefs(selCon));
|
||||
NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
// Advance caret: This requires the presentation shell to get the selection.
|
||||
@ -108,9 +113,13 @@ CompositionTransaction::DoTransaction()
|
||||
NS_IMETHODIMP
|
||||
CompositionTransaction::UndoTransaction()
|
||||
{
|
||||
if (NS_WARN_IF(!mEditorBase)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// Get the selection first so we'll fail before making any changes if we
|
||||
// can't get it
|
||||
RefPtr<Selection> selection = mEditorBase.GetSelection();
|
||||
RefPtr<Selection> selection = mEditorBase->GetSelection();
|
||||
NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
nsresult rv = mTextNode->DeleteData(mOffset, mStringToInsert.Length());
|
||||
@ -171,7 +180,10 @@ CompositionTransaction::GetTxnDescription(nsAString& aString)
|
||||
nsresult
|
||||
CompositionTransaction::SetSelectionForRanges()
|
||||
{
|
||||
return SetIMESelection(mEditorBase, mTextNode, mOffset,
|
||||
if (NS_WARN_IF(!mEditorBase)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
return SetIMESelection(*mEditorBase, mTextNode, mOffset,
|
||||
mStringToInsert.Length(), mRanges);
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ private:
|
||||
nsString mStringToInsert;
|
||||
|
||||
// The editor, which is used to get the selection controller.
|
||||
EditorBase& mEditorBase;
|
||||
RefPtr<EditorBase> mEditorBase;
|
||||
|
||||
RangeUpdater* mRangeUpdater;
|
||||
|
||||
|
@ -49,6 +49,7 @@ CreateElementTransaction::~CreateElementTransaction()
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(CreateElementTransaction,
|
||||
EditTransactionBase,
|
||||
mEditorBase,
|
||||
mParent,
|
||||
mNewNode,
|
||||
mRefNode)
|
||||
@ -62,7 +63,9 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
|
||||
NS_IMETHODIMP
|
||||
CreateElementTransaction::DoTransaction()
|
||||
{
|
||||
MOZ_ASSERT(mEditorBase && mTag && mParent);
|
||||
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mTag) || NS_WARN_IF(!mParent)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
mNewNode = mEditorBase->CreateHTMLContent(mTag);
|
||||
NS_ENSURE_STATE(mNewNode);
|
||||
@ -105,7 +108,9 @@ CreateElementTransaction::DoTransaction()
|
||||
NS_IMETHODIMP
|
||||
CreateElementTransaction::UndoTransaction()
|
||||
{
|
||||
MOZ_ASSERT(mEditorBase && mParent);
|
||||
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mParent)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
mParent->RemoveChild(*mNewNode, rv);
|
||||
@ -116,7 +121,9 @@ CreateElementTransaction::UndoTransaction()
|
||||
NS_IMETHODIMP
|
||||
CreateElementTransaction::RedoTransaction()
|
||||
{
|
||||
MOZ_ASSERT(mEditorBase && mParent);
|
||||
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mParent)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// First, reset mNewNode so it has no attributes or content
|
||||
// XXX We never actually did this, we only cleared mNewNode's contents if it
|
||||
|
@ -57,7 +57,7 @@ protected:
|
||||
virtual ~CreateElementTransaction();
|
||||
|
||||
// The document into which the new node will be inserted.
|
||||
EditorBase* mEditorBase;
|
||||
RefPtr<EditorBase> mEditorBase;
|
||||
|
||||
// The tag (mapping to object type) for the new element.
|
||||
nsCOMPtr<nsIAtom> mTag;
|
||||
|
@ -15,7 +15,7 @@ namespace mozilla {
|
||||
DeleteNodeTransaction::DeleteNodeTransaction(EditorBase& aEditorBase,
|
||||
nsINode& aNodeToDelete,
|
||||
RangeUpdater* aRangeUpdater)
|
||||
: mEditorBase(aEditorBase)
|
||||
: mEditorBase(&aEditorBase)
|
||||
, mNodeToDelete(&aNodeToDelete)
|
||||
, mParentNode(aNodeToDelete.GetParentNode())
|
||||
, mRangeUpdater(aRangeUpdater)
|
||||
@ -31,6 +31,7 @@ DeleteNodeTransaction::~DeleteNodeTransaction()
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteNodeTransaction, EditTransactionBase,
|
||||
mEditorBase,
|
||||
mNodeToDelete,
|
||||
mParentNode,
|
||||
mRefNode)
|
||||
@ -43,8 +44,8 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
|
||||
bool
|
||||
DeleteNodeTransaction::CanDoIt() const
|
||||
{
|
||||
if (NS_WARN_IF(!mNodeToDelete) || !mParentNode ||
|
||||
!mEditorBase.IsModifiableNode(mParentNode)) {
|
||||
if (NS_WARN_IF(!mNodeToDelete) || NS_WARN_IF(!mEditorBase) ||
|
||||
!mParentNode || !mEditorBase->IsModifiableNode(mParentNode)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -46,7 +46,7 @@ protected:
|
||||
virtual ~DeleteNodeTransaction();
|
||||
|
||||
// The editor for this transaction.
|
||||
EditorBase& mEditorBase;
|
||||
RefPtr<EditorBase> mEditorBase;
|
||||
|
||||
// The element to delete.
|
||||
nsCOMPtr<nsINode> mNodeToDelete;
|
||||
|
@ -27,7 +27,7 @@ using namespace dom;
|
||||
DeleteRangeTransaction::DeleteRangeTransaction(EditorBase& aEditorBase,
|
||||
nsRange& aRangeToDelete,
|
||||
RangeUpdater* aRangeUpdater)
|
||||
: mEditorBase(aEditorBase)
|
||||
: mEditorBase(&aEditorBase)
|
||||
, mRangeToDelete(aRangeToDelete.CloneRange())
|
||||
, mRangeUpdater(aRangeUpdater)
|
||||
{
|
||||
@ -35,6 +35,7 @@ DeleteRangeTransaction::DeleteRangeTransaction(EditorBase& aEditorBase,
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteRangeTransaction,
|
||||
EditAggregateTransaction,
|
||||
mEditorBase,
|
||||
mRangeToDelete)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteRangeTransaction)
|
||||
@ -43,7 +44,9 @@ NS_INTERFACE_MAP_END_INHERITING(EditAggregateTransaction)
|
||||
NS_IMETHODIMP
|
||||
DeleteRangeTransaction::DoTransaction()
|
||||
{
|
||||
MOZ_ASSERT(mRangeToDelete);
|
||||
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mRangeToDelete)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// build the child transactions
|
||||
nsCOMPtr<nsINode> startParent = mRangeToDelete->GetStartParent();
|
||||
@ -77,9 +80,9 @@ DeleteRangeTransaction::DoTransaction()
|
||||
|
||||
// only set selection to deletion point if editor gives permission
|
||||
bool bAdjustSelection;
|
||||
mEditorBase.ShouldTxnSetSelection(&bAdjustSelection);
|
||||
mEditorBase->ShouldTxnSetSelection(&bAdjustSelection);
|
||||
if (bAdjustSelection) {
|
||||
RefPtr<Selection> selection = mEditorBase.GetSelection();
|
||||
RefPtr<Selection> selection = mEditorBase->GetSelection();
|
||||
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
||||
rv = selection->Collapse(startParent, startOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -117,6 +120,10 @@ DeleteRangeTransaction::CreateTxnsToDeleteBetween(nsINode* aNode,
|
||||
int32_t aStartOffset,
|
||||
int32_t aEndOffset)
|
||||
{
|
||||
if (NS_WARN_IF(!mEditorBase)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// see what kind of node we have
|
||||
if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) {
|
||||
// if the node is a chardata node, then delete chardata content
|
||||
@ -131,7 +138,7 @@ DeleteRangeTransaction::CreateTxnsToDeleteBetween(nsINode* aNode,
|
||||
static_cast<nsGenericDOMDataNode*>(aNode);
|
||||
|
||||
RefPtr<DeleteTextTransaction> deleteTextTransaction =
|
||||
new DeleteTextTransaction(mEditorBase, *charDataNode, aStartOffset,
|
||||
new DeleteTextTransaction(*mEditorBase, *charDataNode, aStartOffset,
|
||||
numToDel, mRangeUpdater);
|
||||
// If the text node isn't editable, it should be never undone/redone.
|
||||
// So, the transaction shouldn't be recorded.
|
||||
@ -150,7 +157,7 @@ DeleteRangeTransaction::CreateTxnsToDeleteBetween(nsINode* aNode,
|
||||
break;
|
||||
}
|
||||
RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
|
||||
new DeleteNodeTransaction(mEditorBase, *child, mRangeUpdater);
|
||||
new DeleteNodeTransaction(*mEditorBase, *child, mRangeUpdater);
|
||||
// XXX This is odd handling. Even if some children are not editable,
|
||||
// editor should append transactions because they could be editable
|
||||
// at undoing/redoing. Additionally, if the transaction needs to
|
||||
@ -169,6 +176,10 @@ DeleteRangeTransaction::CreateTxnsToDeleteContent(nsINode* aNode,
|
||||
int32_t aOffset,
|
||||
nsIEditor::EDirection aAction)
|
||||
{
|
||||
if (NS_WARN_IF(!mEditorBase)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// see what kind of node we have
|
||||
if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) {
|
||||
// if the node is a chardata node, then delete chardata content
|
||||
@ -185,7 +196,7 @@ DeleteRangeTransaction::CreateTxnsToDeleteContent(nsINode* aNode,
|
||||
RefPtr<nsGenericDOMDataNode> dataNode =
|
||||
static_cast<nsGenericDOMDataNode*>(aNode);
|
||||
RefPtr<DeleteTextTransaction> deleteTextTransaction =
|
||||
new DeleteTextTransaction(mEditorBase, *dataNode, start, numToDelete,
|
||||
new DeleteTextTransaction(*mEditorBase, *dataNode, start, numToDelete,
|
||||
mRangeUpdater);
|
||||
// If the text node isn't editable, it should be never undone/redone.
|
||||
// So, the transaction shouldn't be recorded.
|
||||
@ -202,6 +213,10 @@ DeleteRangeTransaction::CreateTxnsToDeleteContent(nsINode* aNode,
|
||||
nsresult
|
||||
DeleteRangeTransaction::CreateTxnsToDeleteNodesBetween()
|
||||
{
|
||||
if (NS_WARN_IF(!mEditorBase)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
|
||||
|
||||
nsresult rv = iter->Init(mRangeToDelete);
|
||||
@ -214,7 +229,7 @@ DeleteRangeTransaction::CreateTxnsToDeleteNodesBetween()
|
||||
}
|
||||
|
||||
RefPtr<DeleteNodeTransaction> deleteNodeTransaction =
|
||||
new DeleteNodeTransaction(mEditorBase, *node, mRangeUpdater);
|
||||
new DeleteNodeTransaction(*mEditorBase, *node, mRangeUpdater);
|
||||
// XXX This is odd handling. Even if some nodes in the range are not
|
||||
// editable, editor should append transactions because they could
|
||||
// at undoing/redoing. Additionally, if the transaction needs to
|
||||
|
@ -61,7 +61,7 @@ protected:
|
||||
nsIEditor::EDirection aAction);
|
||||
|
||||
// The editor for this transaction.
|
||||
EditorBase& mEditorBase;
|
||||
RefPtr<EditorBase> mEditorBase;
|
||||
|
||||
// P1 in the range.
|
||||
RefPtr<nsRange> mRangeToDelete;
|
||||
|
@ -25,7 +25,7 @@ DeleteTextTransaction::DeleteTextTransaction(
|
||||
uint32_t aOffset,
|
||||
uint32_t aNumCharsToDelete,
|
||||
RangeUpdater* aRangeUpdater)
|
||||
: mEditorBase(aEditorBase)
|
||||
: mEditorBase(&aEditorBase)
|
||||
, mCharData(&aCharData)
|
||||
, mOffset(aOffset)
|
||||
, mNumCharsToDelete(aNumCharsToDelete)
|
||||
@ -36,6 +36,7 @@ DeleteTextTransaction::DeleteTextTransaction(
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteTextTransaction, EditTransactionBase,
|
||||
mEditorBase,
|
||||
mCharData)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteTextTransaction)
|
||||
@ -44,16 +45,18 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
|
||||
bool
|
||||
DeleteTextTransaction::CanDoIt() const
|
||||
{
|
||||
if (NS_WARN_IF(!mCharData)) {
|
||||
if (NS_WARN_IF(!mCharData) || NS_WARN_IF(!mEditorBase)) {
|
||||
return false;
|
||||
}
|
||||
return mEditorBase.IsModifiableNode(mCharData);
|
||||
return mEditorBase->IsModifiableNode(mCharData);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DeleteTextTransaction::DoTransaction()
|
||||
{
|
||||
MOZ_ASSERT(mCharData);
|
||||
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mCharData)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// Get the text that we're about to delete
|
||||
nsresult rv = mCharData->SubstringData(mOffset, mNumCharsToDelete,
|
||||
@ -67,8 +70,8 @@ DeleteTextTransaction::DoTransaction()
|
||||
}
|
||||
|
||||
// Only set selection to deletion point if editor gives permission
|
||||
if (mEditorBase.GetShouldTxnSetSelection()) {
|
||||
RefPtr<Selection> selection = mEditorBase.GetSelection();
|
||||
if (mEditorBase->GetShouldTxnSetSelection()) {
|
||||
RefPtr<Selection> selection = mEditorBase->GetSelection();
|
||||
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
||||
rv = selection->Collapse(mCharData, mOffset);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv),
|
||||
@ -84,8 +87,9 @@ DeleteTextTransaction::DoTransaction()
|
||||
NS_IMETHODIMP
|
||||
DeleteTextTransaction::UndoTransaction()
|
||||
{
|
||||
MOZ_ASSERT(mCharData);
|
||||
|
||||
if (NS_WARN_IF(!mCharData)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
return mCharData->InsertData(mOffset, mDeletedText);
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ public:
|
||||
|
||||
protected:
|
||||
// The provider of basic editing operations.
|
||||
EditorBase& mEditorBase;
|
||||
RefPtr<EditorBase> mEditorBase;
|
||||
|
||||
// The CharacterData node to operate upon.
|
||||
RefPtr<nsGenericDOMDataNode> mCharData;
|
||||
|
@ -465,6 +465,13 @@ EditorBase::PreDestroy(bool aDestroyingFrames)
|
||||
mSpellcheckCheckboxState = eTriUnset;
|
||||
mRootElement = nullptr;
|
||||
|
||||
// Transaction may grab this instance. Therefore, they should be released
|
||||
// here for stopping the circular reference with this instance.
|
||||
if (mTxnMgr) {
|
||||
mTxnMgr->Clear();
|
||||
mTxnMgr = nullptr;
|
||||
}
|
||||
|
||||
mDidPreDestroy = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -7301,14 +7301,17 @@ HTMLEditRules::PinSelectionToNewBlock(Selection* aSelection)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!mNewBlock)) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
// get the (collapsed) selection location
|
||||
nsCOMPtr<nsIDOMNode> selNode, temp;
|
||||
nsCOMPtr<nsIDOMNode> selNode;
|
||||
int32_t selOffset;
|
||||
nsresult rv =
|
||||
EditorBase::GetStartNodeAndOffset(aSelection,
|
||||
getter_AddRefs(selNode), &selOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
temp = selNode;
|
||||
|
||||
// use ranges and sRangeHelper to compare sel point to new block
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(selNode);
|
||||
@ -7318,24 +7321,23 @@ HTMLEditRules::PinSelectionToNewBlock(Selection* aSelection)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = range->SetEnd(selNode, selOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIContent> block = mNewBlock.get();
|
||||
NS_ENSURE_TRUE(block, NS_ERROR_NO_INTERFACE);
|
||||
bool nodeBefore, nodeAfter;
|
||||
rv = nsRange::CompareNodeToRange(block, range, &nodeBefore, &nodeAfter);
|
||||
rv = nsRange::CompareNodeToRange(mNewBlock, range, &nodeBefore, &nodeAfter);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (nodeBefore && nodeAfter) {
|
||||
return NS_OK; // selection is inside block
|
||||
} else if (nodeBefore) {
|
||||
// selection is after block. put at end of block.
|
||||
nsCOMPtr<nsIDOMNode> tmp = GetAsDOMNode(mNewBlock);
|
||||
NS_ENSURE_STATE(mHTMLEditor);
|
||||
tmp = GetAsDOMNode(mHTMLEditor->GetLastEditableChild(*block));
|
||||
nsCOMPtr<nsINode> tmp = mHTMLEditor->GetLastEditableChild(*mNewBlock);
|
||||
if (!tmp) {
|
||||
tmp = mNewBlock;
|
||||
}
|
||||
uint32_t endPoint;
|
||||
if (EditorBase::IsTextNode(tmp) ||
|
||||
mHTMLEditor->IsContainer(tmp)) {
|
||||
rv = EditorBase::GetLengthOfDOMNode(tmp, endPoint);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
endPoint = tmp->Length();
|
||||
} else {
|
||||
tmp = EditorBase::GetNodeLocation(tmp, (int32_t*)&endPoint);
|
||||
endPoint++; // want to be after this node
|
||||
@ -7343,9 +7345,11 @@ HTMLEditRules::PinSelectionToNewBlock(Selection* aSelection)
|
||||
return aSelection->Collapse(tmp, (int32_t)endPoint);
|
||||
} else {
|
||||
// selection is before block. put at start of block.
|
||||
nsCOMPtr<nsIDOMNode> tmp = GetAsDOMNode(mNewBlock);
|
||||
NS_ENSURE_STATE(mHTMLEditor);
|
||||
tmp = GetAsDOMNode(mHTMLEditor->GetFirstEditableChild(*block));
|
||||
nsCOMPtr<nsINode> tmp = mHTMLEditor->GetFirstEditableChild(*mNewBlock);
|
||||
if (!tmp) {
|
||||
tmp = mNewBlock;
|
||||
}
|
||||
int32_t offset;
|
||||
if (EditorBase::IsTextNode(tmp) ||
|
||||
mHTMLEditor->IsContainer(tmp)) {
|
||||
|
@ -28,7 +28,7 @@ InsertNodeTransaction::InsertNodeTransaction(nsIContent& aNode,
|
||||
: mNode(&aNode)
|
||||
, mParent(&aParent)
|
||||
, mOffset(aOffset)
|
||||
, mEditorBase(aEditorBase)
|
||||
, mEditorBase(&aEditorBase)
|
||||
{
|
||||
}
|
||||
|
||||
@ -37,6 +37,7 @@ InsertNodeTransaction::~InsertNodeTransaction()
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertNodeTransaction, EditTransactionBase,
|
||||
mEditorBase,
|
||||
mNode,
|
||||
mParent)
|
||||
|
||||
@ -48,7 +49,9 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
|
||||
NS_IMETHODIMP
|
||||
InsertNodeTransaction::DoTransaction()
|
||||
{
|
||||
MOZ_ASSERT(mNode && mParent);
|
||||
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mNode) || NS_WARN_IF(!mParent)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
uint32_t count = mParent->GetChildCount();
|
||||
if (mOffset > static_cast<int32_t>(count) || mOffset == -1) {
|
||||
@ -59,15 +62,15 @@ InsertNodeTransaction::DoTransaction()
|
||||
// Note, it's ok for ref to be null. That means append.
|
||||
nsCOMPtr<nsIContent> ref = mParent->GetChildAt(mOffset);
|
||||
|
||||
mEditorBase.MarkNodeDirty(GetAsDOMNode(mNode));
|
||||
mEditorBase->MarkNodeDirty(GetAsDOMNode(mNode));
|
||||
|
||||
ErrorResult rv;
|
||||
mParent->InsertBefore(*mNode, ref, rv);
|
||||
NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
|
||||
|
||||
// Only set selection to insertion point if editor gives permission
|
||||
if (mEditorBase.GetShouldTxnSetSelection()) {
|
||||
RefPtr<Selection> selection = mEditorBase.GetSelection();
|
||||
if (mEditorBase->GetShouldTxnSetSelection()) {
|
||||
RefPtr<Selection> selection = mEditorBase->GetSelection();
|
||||
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
||||
// Place the selection just after the inserted element
|
||||
selection->Collapse(mParent, mOffset + 1);
|
||||
@ -80,8 +83,9 @@ InsertNodeTransaction::DoTransaction()
|
||||
NS_IMETHODIMP
|
||||
InsertNodeTransaction::UndoTransaction()
|
||||
{
|
||||
MOZ_ASSERT(mNode && mParent);
|
||||
|
||||
if (NS_WARN_IF(!mNode) || NS_WARN_IF(!mParent)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
ErrorResult rv;
|
||||
mParent->RemoveChild(*mNode, rv);
|
||||
return rv.StealNSResult();
|
||||
|
@ -50,7 +50,7 @@ protected:
|
||||
int32_t mOffset;
|
||||
|
||||
// The editor for this transaction.
|
||||
EditorBase& mEditorBase;
|
||||
RefPtr<EditorBase> mEditorBase;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -26,7 +26,7 @@ InsertTextTransaction::InsertTextTransaction(Text& aTextNode,
|
||||
: mTextNode(&aTextNode)
|
||||
, mOffset(aOffset)
|
||||
, mStringToInsert(aStringToInsert)
|
||||
, mEditorBase(aEditorBase)
|
||||
, mEditorBase(&aEditorBase)
|
||||
, mRangeUpdater(aRangeUpdater)
|
||||
{
|
||||
}
|
||||
@ -36,6 +36,7 @@ InsertTextTransaction::~InsertTextTransaction()
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertTextTransaction, EditTransactionBase,
|
||||
mEditorBase,
|
||||
mTextNode)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(InsertTextTransaction, EditTransactionBase)
|
||||
@ -50,12 +51,16 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
|
||||
NS_IMETHODIMP
|
||||
InsertTextTransaction::DoTransaction()
|
||||
{
|
||||
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mTextNode)) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsresult rv = mTextNode->InsertData(mOffset, mStringToInsert);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Only set selection to insertion point if editor gives permission
|
||||
if (mEditorBase.GetShouldTxnSetSelection()) {
|
||||
RefPtr<Selection> selection = mEditorBase.GetSelection();
|
||||
if (mEditorBase->GetShouldTxnSetSelection()) {
|
||||
RefPtr<Selection> selection = mEditorBase->GetSelection();
|
||||
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
||||
DebugOnly<nsresult> rv =
|
||||
selection->Collapse(mTextNode, mOffset + mStringToInsert.Length());
|
||||
|
@ -76,7 +76,7 @@ private:
|
||||
nsString mStringToInsert;
|
||||
|
||||
// The editor, which we'll need to get the selection.
|
||||
EditorBase& mEditorBase;
|
||||
RefPtr<EditorBase> mEditorBase;
|
||||
|
||||
RangeUpdater* mRangeUpdater;
|
||||
};
|
||||
|
@ -21,7 +21,7 @@ using namespace dom;
|
||||
JoinNodeTransaction::JoinNodeTransaction(EditorBase& aEditorBase,
|
||||
nsINode& aLeftNode,
|
||||
nsINode& aRightNode)
|
||||
: mEditorBase(aEditorBase)
|
||||
: mEditorBase(&aEditorBase)
|
||||
, mLeftNode(&aLeftNode)
|
||||
, mRightNode(&aRightNode)
|
||||
, mOffset(0)
|
||||
@ -29,6 +29,7 @@ JoinNodeTransaction::JoinNodeTransaction(EditorBase& aEditorBase,
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(JoinNodeTransaction, EditTransactionBase,
|
||||
mEditorBase,
|
||||
mLeftNode,
|
||||
mRightNode,
|
||||
mParent)
|
||||
@ -41,10 +42,11 @@ JoinNodeTransaction::CanDoIt() const
|
||||
{
|
||||
if (NS_WARN_IF(!mLeftNode) ||
|
||||
NS_WARN_IF(!mRightNode) ||
|
||||
NS_WARN_IF(!mEditorBase) ||
|
||||
!mLeftNode->GetParentNode()) {
|
||||
return false;
|
||||
}
|
||||
return mEditorBase.IsModifiableNode(mLeftNode->GetParentNode());
|
||||
return mEditorBase->IsModifiableNode(mLeftNode->GetParentNode());
|
||||
}
|
||||
|
||||
// After DoTransaction() and RedoTransaction(), the left node is removed from
|
||||
@ -52,6 +54,12 @@ JoinNodeTransaction::CanDoIt() const
|
||||
NS_IMETHODIMP
|
||||
JoinNodeTransaction::DoTransaction()
|
||||
{
|
||||
if (NS_WARN_IF(!mEditorBase) ||
|
||||
NS_WARN_IF(!mLeftNode) ||
|
||||
NS_WARN_IF(!mRightNode)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// Get the parent node
|
||||
nsCOMPtr<nsINode> leftParent = mLeftNode->GetParentNode();
|
||||
NS_ENSURE_TRUE(leftParent, NS_ERROR_NULL_POINTER);
|
||||
@ -67,7 +75,7 @@ JoinNodeTransaction::DoTransaction()
|
||||
mParent = leftParent;
|
||||
mOffset = mLeftNode->Length();
|
||||
|
||||
return mEditorBase.JoinNodesImpl(mRightNode, mLeftNode, mParent);
|
||||
return mEditorBase->JoinNodesImpl(mRightNode, mLeftNode, mParent);
|
||||
}
|
||||
|
||||
//XXX: What if instead of split, we just deleted the unneeded children of
|
||||
@ -75,7 +83,11 @@ JoinNodeTransaction::DoTransaction()
|
||||
NS_IMETHODIMP
|
||||
JoinNodeTransaction::UndoTransaction()
|
||||
{
|
||||
MOZ_ASSERT(mParent);
|
||||
if (NS_WARN_IF(!mParent) ||
|
||||
NS_WARN_IF(!mLeftNode) ||
|
||||
NS_WARN_IF(!mRightNode)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// First, massage the existing node so it is in its post-split state
|
||||
ErrorResult rv;
|
||||
|
@ -48,7 +48,7 @@ public:
|
||||
NS_DECL_EDITTRANSACTIONBASE
|
||||
|
||||
protected:
|
||||
EditorBase& mEditorBase;
|
||||
RefPtr<EditorBase> mEditorBase;
|
||||
|
||||
// The nodes to operate upon. After the merge, mRightNode remains and
|
||||
// mLeftNode is removed from the content tree.
|
||||
|
@ -25,7 +25,7 @@ PlaceholderTransaction::PlaceholderTransaction(
|
||||
, mCompositionTransaction(nullptr)
|
||||
, mCommitted(false)
|
||||
, mStartSel(Move(aSelState))
|
||||
, mEditorBase(aEditorBase)
|
||||
, mEditorBase(&aEditorBase)
|
||||
{
|
||||
mName = aName;
|
||||
}
|
||||
@ -41,6 +41,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PlaceholderTransaction,
|
||||
if (tmp->mStartSel) {
|
||||
ImplCycleCollectionUnlink(*tmp->mStartSel);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorBase);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndSel);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
@ -49,6 +50,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PlaceholderTransaction,
|
||||
if (tmp->mStartSel) {
|
||||
ImplCycleCollectionTraverse(cb, *tmp->mStartSel, "mStartSel", 0);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndSel);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
@ -69,6 +71,10 @@ PlaceholderTransaction::DoTransaction()
|
||||
NS_IMETHODIMP
|
||||
PlaceholderTransaction::UndoTransaction()
|
||||
{
|
||||
if (NS_WARN_IF(!mEditorBase)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// Undo transactions.
|
||||
nsresult rv = EditAggregateTransaction::UndoTransaction();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -76,7 +82,7 @@ PlaceholderTransaction::UndoTransaction()
|
||||
NS_ENSURE_TRUE(mStartSel, NS_ERROR_NULL_POINTER);
|
||||
|
||||
// now restore selection
|
||||
RefPtr<Selection> selection = mEditorBase.GetSelection();
|
||||
RefPtr<Selection> selection = mEditorBase->GetSelection();
|
||||
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
||||
return mStartSel->RestoreSelection(selection);
|
||||
}
|
||||
@ -84,12 +90,16 @@ PlaceholderTransaction::UndoTransaction()
|
||||
NS_IMETHODIMP
|
||||
PlaceholderTransaction::RedoTransaction()
|
||||
{
|
||||
if (NS_WARN_IF(!mEditorBase)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// Redo transactions.
|
||||
nsresult rv = EditAggregateTransaction::RedoTransaction();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// now restore selection
|
||||
RefPtr<Selection> selection = mEditorBase.GetSelection();
|
||||
RefPtr<Selection> selection = mEditorBase->GetSelection();
|
||||
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
||||
return mEndSel.RestoreSelection(selection);
|
||||
}
|
||||
@ -254,7 +264,11 @@ PlaceholderTransaction::Commit()
|
||||
nsresult
|
||||
PlaceholderTransaction::RememberEndingSelection()
|
||||
{
|
||||
RefPtr<Selection> selection = mEditorBase.GetSelection();
|
||||
if (NS_WARN_IF(!mEditorBase)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
RefPtr<Selection> selection = mEditorBase->GetSelection();
|
||||
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
||||
mEndSel.SaveSelection(selection);
|
||||
return NS_OK;
|
||||
|
@ -82,7 +82,7 @@ protected:
|
||||
SelectionState mEndSel;
|
||||
|
||||
// The editor for this transaction.
|
||||
EditorBase& mEditorBase;
|
||||
RefPtr<EditorBase> mEditorBase;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -19,7 +19,7 @@ using namespace dom;
|
||||
SplitNodeTransaction::SplitNodeTransaction(EditorBase& aEditorBase,
|
||||
nsIContent& aNode,
|
||||
int32_t aOffset)
|
||||
: mEditorBase(aEditorBase)
|
||||
: mEditorBase(&aEditorBase)
|
||||
, mExistingRightNode(&aNode)
|
||||
, mOffset(aOffset)
|
||||
{
|
||||
@ -30,6 +30,7 @@ SplitNodeTransaction::~SplitNodeTransaction()
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(SplitNodeTransaction, EditTransactionBase,
|
||||
mEditorBase,
|
||||
mParent,
|
||||
mNewLeftNode)
|
||||
|
||||
@ -41,6 +42,10 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
|
||||
NS_IMETHODIMP
|
||||
SplitNodeTransaction::DoTransaction()
|
||||
{
|
||||
if (NS_WARN_IF(!mEditorBase)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// Create a new node
|
||||
ErrorResult rv;
|
||||
// Don't use .downcast directly because AsContent has an assertion we want
|
||||
@ -48,16 +53,16 @@ SplitNodeTransaction::DoTransaction()
|
||||
NS_ASSERTION(!rv.Failed() && clone, "Could not create clone");
|
||||
NS_ENSURE_TRUE(!rv.Failed() && clone, rv.StealNSResult());
|
||||
mNewLeftNode = dont_AddRef(clone.forget().take()->AsContent());
|
||||
mEditorBase.MarkNodeDirty(mExistingRightNode->AsDOMNode());
|
||||
mEditorBase->MarkNodeDirty(mExistingRightNode->AsDOMNode());
|
||||
|
||||
// Get the parent node
|
||||
mParent = mExistingRightNode->GetParentNode();
|
||||
NS_ENSURE_TRUE(mParent, NS_ERROR_NULL_POINTER);
|
||||
|
||||
// Insert the new node
|
||||
rv = mEditorBase.SplitNodeImpl(*mExistingRightNode, mOffset, *mNewLeftNode);
|
||||
if (mEditorBase.GetShouldTxnSetSelection()) {
|
||||
RefPtr<Selection> selection = mEditorBase.GetSelection();
|
||||
rv = mEditorBase->SplitNodeImpl(*mExistingRightNode, mOffset, *mNewLeftNode);
|
||||
if (mEditorBase->GetShouldTxnSetSelection()) {
|
||||
RefPtr<Selection> selection = mEditorBase->GetSelection();
|
||||
NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
|
||||
rv = selection->Collapse(mNewLeftNode, mOffset);
|
||||
}
|
||||
@ -67,10 +72,14 @@ SplitNodeTransaction::DoTransaction()
|
||||
NS_IMETHODIMP
|
||||
SplitNodeTransaction::UndoTransaction()
|
||||
{
|
||||
MOZ_ASSERT(mNewLeftNode && mParent);
|
||||
if (NS_WARN_IF(!mEditorBase) ||
|
||||
NS_WARN_IF(!mNewLeftNode) ||
|
||||
NS_WARN_IF(!mParent)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
// This assumes Do inserted the new node in front of the prior existing node
|
||||
return mEditorBase.JoinNodesImpl(mExistingRightNode, mNewLeftNode, mParent);
|
||||
return mEditorBase->JoinNodesImpl(mExistingRightNode, mNewLeftNode, mParent);
|
||||
}
|
||||
|
||||
/* Redo cannot simply resplit the right node, because subsequent transactions
|
||||
@ -80,7 +89,10 @@ SplitNodeTransaction::UndoTransaction()
|
||||
NS_IMETHODIMP
|
||||
SplitNodeTransaction::RedoTransaction()
|
||||
{
|
||||
MOZ_ASSERT(mNewLeftNode && mParent);
|
||||
if (NS_WARN_IF(!mNewLeftNode) ||
|
||||
NS_WARN_IF(!mParent)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
ErrorResult rv;
|
||||
// First, massage the existing node so it is in its post-split state
|
||||
|
@ -49,7 +49,7 @@ public:
|
||||
protected:
|
||||
virtual ~SplitNodeTransaction();
|
||||
|
||||
EditorBase& mEditorBase;
|
||||
RefPtr<EditorBase> mEditorBase;
|
||||
|
||||
// The node to operate upon.
|
||||
nsCOMPtr<nsIContent> mExistingRightNode;
|
||||
|
@ -44,9 +44,9 @@ RemoveStyleSheet(EditorBase& aEditor, StyleSheet* aSheet)
|
||||
* AddStyleSheetTransaction
|
||||
******************************************************************************/
|
||||
|
||||
AddStyleSheetTransaction::AddStyleSheetTransaction(EditorBase& aEditor,
|
||||
AddStyleSheetTransaction::AddStyleSheetTransaction(EditorBase& aEditorBase,
|
||||
StyleSheet* aSheet)
|
||||
: mEditor(aEditor)
|
||||
: mEditorBase(&aEditorBase)
|
||||
, mSheet(aSheet)
|
||||
{
|
||||
MOZ_ASSERT(aSheet);
|
||||
@ -54,6 +54,7 @@ AddStyleSheetTransaction::AddStyleSheetTransaction(EditorBase& aEditor,
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(AddStyleSheetTransaction,
|
||||
EditTransactionBase,
|
||||
mEditorBase,
|
||||
mSheet)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AddStyleSheetTransaction)
|
||||
@ -62,18 +63,20 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
|
||||
NS_IMETHODIMP
|
||||
AddStyleSheetTransaction::DoTransaction()
|
||||
{
|
||||
NS_ENSURE_TRUE(mSheet, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
AddStyleSheet(mEditor, mSheet);
|
||||
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mSheet)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
AddStyleSheet(*mEditorBase, mSheet);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AddStyleSheetTransaction::UndoTransaction()
|
||||
{
|
||||
NS_ENSURE_TRUE(mSheet, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
RemoveStyleSheet(mEditor, mSheet);
|
||||
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mSheet)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
RemoveStyleSheet(*mEditorBase, mSheet);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -88,9 +91,10 @@ AddStyleSheetTransaction::GetTxnDescription(nsAString& aString)
|
||||
* RemoveStyleSheetTransaction
|
||||
******************************************************************************/
|
||||
|
||||
RemoveStyleSheetTransaction::RemoveStyleSheetTransaction(EditorBase& aEditor,
|
||||
StyleSheet* aSheet)
|
||||
: mEditor(aEditor)
|
||||
RemoveStyleSheetTransaction::RemoveStyleSheetTransaction(
|
||||
EditorBase& aEditorBase,
|
||||
StyleSheet* aSheet)
|
||||
: mEditorBase(&aEditorBase)
|
||||
, mSheet(aSheet)
|
||||
{
|
||||
MOZ_ASSERT(aSheet);
|
||||
@ -98,6 +102,7 @@ RemoveStyleSheetTransaction::RemoveStyleSheetTransaction(EditorBase& aEditor,
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(RemoveStyleSheetTransaction,
|
||||
EditTransactionBase,
|
||||
mEditorBase,
|
||||
mSheet)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RemoveStyleSheetTransaction)
|
||||
@ -106,18 +111,20 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
|
||||
NS_IMETHODIMP
|
||||
RemoveStyleSheetTransaction::DoTransaction()
|
||||
{
|
||||
NS_ENSURE_TRUE(mSheet, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
RemoveStyleSheet(mEditor, mSheet);
|
||||
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mSheet)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
RemoveStyleSheet(*mEditorBase, mSheet);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
RemoveStyleSheetTransaction::UndoTransaction()
|
||||
{
|
||||
NS_ENSURE_TRUE(mSheet, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
AddStyleSheet(mEditor, mSheet);
|
||||
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mSheet)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
AddStyleSheet(*mEditorBase, mSheet);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ public:
|
||||
|
||||
protected:
|
||||
// The editor that created this transaction.
|
||||
EditorBase& mEditor;
|
||||
RefPtr<EditorBase> mEditorBase;
|
||||
// The style sheet to add.
|
||||
RefPtr<mozilla::StyleSheet> mSheet;
|
||||
};
|
||||
@ -55,7 +55,7 @@ public:
|
||||
|
||||
protected:
|
||||
// The editor that created this transaction.
|
||||
EditorBase& mEditor;
|
||||
RefPtr<EditorBase> mEditorBase;
|
||||
// The style sheet to remove.
|
||||
RefPtr<StyleSheet> mSheet;
|
||||
|
||||
|
19
editor/libeditor/crashtests/1348851.html
Normal file
19
editor/libeditor/crashtests/1348851.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script>
|
||||
function boom(){
|
||||
document.designMode = "on";
|
||||
document.execCommand("insertlinebreak");
|
||||
document.designMode = "off";
|
||||
document.designMode = "on";
|
||||
document.execCommand("insertunorderedlist");
|
||||
}
|
||||
addEventListener("DOMContentLoaded", boom);
|
||||
</script>
|
||||
</head>
|
||||
<body style="display:flex;">
|
||||
<!--comment-->
|
||||
</body>
|
||||
</html>
|
@ -72,3 +72,4 @@ load 1272490.html
|
||||
load 1317704.html
|
||||
load 1317718.html
|
||||
load 1324505.html
|
||||
load 1348851.html
|
||||
|
@ -142,10 +142,12 @@ AnimationState::LoopLength() const
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Maybe<TimeStamp>
|
||||
FrameAnimator::GetCurrentImgFrameEndTime(AnimationState& aState) const
|
||||
FrameAnimator::GetCurrentImgFrameEndTime(AnimationState& aState,
|
||||
DrawableSurface& aFrames) const
|
||||
{
|
||||
TimeStamp currentFrameTime = aState.mCurrentAnimationFrameTime;
|
||||
Maybe<FrameTimeout> timeout = GetTimeoutForFrame(aState, aState.mCurrentAnimationFrameIndex);
|
||||
Maybe<FrameTimeout> timeout =
|
||||
GetTimeoutForFrame(aState, aFrames, aState.mCurrentAnimationFrameIndex);
|
||||
|
||||
if (timeout.isNothing()) {
|
||||
MOZ_ASSERT(aState.GetHasBeenDecoded() && !aState.GetIsCurrentlyDecoded());
|
||||
@ -171,7 +173,9 @@ FrameAnimator::GetCurrentImgFrameEndTime(AnimationState& aState) const
|
||||
}
|
||||
|
||||
RefreshResult
|
||||
FrameAnimator::AdvanceFrame(AnimationState& aState, TimeStamp aTime)
|
||||
FrameAnimator::AdvanceFrame(AnimationState& aState,
|
||||
DrawableSurface& aFrames,
|
||||
TimeStamp aTime)
|
||||
{
|
||||
NS_ASSERTION(aTime <= TimeStamp::Now(),
|
||||
"Given time appears to be in the future");
|
||||
@ -231,7 +235,7 @@ FrameAnimator::AdvanceFrame(AnimationState& aState, TimeStamp aTime)
|
||||
// the appropriate notification on the main thread. Make sure we stay in sync
|
||||
// with AnimationState.
|
||||
MOZ_ASSERT(nextFrameIndex < aState.KnownFrameCount());
|
||||
RawAccessFrameRef nextFrame = GetRawFrame(nextFrameIndex);
|
||||
RawAccessFrameRef nextFrame = GetRawFrame(aFrames, nextFrameIndex);
|
||||
|
||||
// We should always check to see if we have the next frame even if we have
|
||||
// previously finished decoding. If we needed to redecode (e.g. due to a draw
|
||||
@ -243,7 +247,7 @@ FrameAnimator::AdvanceFrame(AnimationState& aState, TimeStamp aTime)
|
||||
return ret;
|
||||
}
|
||||
|
||||
Maybe<FrameTimeout> nextFrameTimeout = GetTimeoutForFrame(aState, nextFrameIndex);
|
||||
Maybe<FrameTimeout> nextFrameTimeout = GetTimeoutForFrame(aState, aFrames, nextFrameIndex);
|
||||
// GetTimeoutForFrame can only return none if frame doesn't exist,
|
||||
// but we just got it above.
|
||||
MOZ_ASSERT(nextFrameTimeout.isSome());
|
||||
@ -257,11 +261,11 @@ FrameAnimator::AdvanceFrame(AnimationState& aState, TimeStamp aTime)
|
||||
MOZ_ASSERT(nextFrameIndex == currentFrameIndex + 1);
|
||||
|
||||
// Change frame
|
||||
if (!DoBlend(&ret.mDirtyRect, currentFrameIndex, nextFrameIndex)) {
|
||||
if (!DoBlend(aFrames, &ret.mDirtyRect, currentFrameIndex, nextFrameIndex)) {
|
||||
// something went wrong, move on to next
|
||||
NS_WARNING("FrameAnimator::AdvanceFrame(): Compositing of frame failed");
|
||||
nextFrame->SetCompositingFailed(true);
|
||||
Maybe<TimeStamp> currentFrameEndTime = GetCurrentImgFrameEndTime(aState);
|
||||
Maybe<TimeStamp> currentFrameEndTime = GetCurrentImgFrameEndTime(aState, aFrames);
|
||||
MOZ_ASSERT(currentFrameEndTime.isSome());
|
||||
aState.mCurrentAnimationFrameTime = *currentFrameEndTime;
|
||||
aState.mCurrentAnimationFrameIndex = nextFrameIndex;
|
||||
@ -272,7 +276,7 @@ FrameAnimator::AdvanceFrame(AnimationState& aState, TimeStamp aTime)
|
||||
nextFrame->SetCompositingFailed(false);
|
||||
}
|
||||
|
||||
Maybe<TimeStamp> currentFrameEndTime = GetCurrentImgFrameEndTime(aState);
|
||||
Maybe<TimeStamp> currentFrameEndTime = GetCurrentImgFrameEndTime(aState, aFrames);
|
||||
MOZ_ASSERT(currentFrameEndTime.isSome());
|
||||
aState.mCurrentAnimationFrameTime = *currentFrameEndTime;
|
||||
|
||||
@ -312,9 +316,29 @@ FrameAnimator::RequestRefresh(AnimationState& aState, const TimeStamp& aTime)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Get the animation frames once now, and pass them down to callees because
|
||||
// the surface could be discarded at anytime on a different thread. This is
|
||||
// must easier to reason about then trying to write code that is safe to
|
||||
// having the surface disappear at anytime.
|
||||
LookupResult result =
|
||||
SurfaceCache::Lookup(ImageKey(mImage),
|
||||
RasterSurfaceKey(mSize,
|
||||
DefaultSurfaceFlags(),
|
||||
PlaybackType::eAnimated));
|
||||
|
||||
if (!result) {
|
||||
if (result.Type() == MatchType::NOT_FOUND) {
|
||||
// No surface, and nothing pending, must have been discarded but
|
||||
// we haven't been notified yet.
|
||||
aState.SetDiscarded(true);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// only advance the frame if the current time is greater than or
|
||||
// equal to the current frame's end time.
|
||||
Maybe<TimeStamp> currentFrameEndTime = GetCurrentImgFrameEndTime(aState);
|
||||
Maybe<TimeStamp> currentFrameEndTime =
|
||||
GetCurrentImgFrameEndTime(aState, result.Surface());
|
||||
if (currentFrameEndTime.isNothing()) {
|
||||
MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
|
||||
MOZ_ASSERT(aState.GetHasBeenDecoded() && !aState.GetIsCurrentlyDecoded());
|
||||
@ -327,12 +351,12 @@ FrameAnimator::RequestRefresh(AnimationState& aState, const TimeStamp& aTime)
|
||||
while (*currentFrameEndTime <= aTime) {
|
||||
TimeStamp oldFrameEndTime = *currentFrameEndTime;
|
||||
|
||||
RefreshResult frameRes = AdvanceFrame(aState, aTime);
|
||||
RefreshResult frameRes = AdvanceFrame(aState, result.Surface(), aTime);
|
||||
|
||||
// Accumulate our result for returning to callers.
|
||||
ret.Accumulate(frameRes);
|
||||
|
||||
currentFrameEndTime = GetCurrentImgFrameEndTime(aState);
|
||||
currentFrameEndTime = GetCurrentImgFrameEndTime(aState, result.Surface());
|
||||
// AdvanceFrame can't advance to a frame that doesn't exist yet.
|
||||
MOZ_ASSERT(currentFrameEndTime.isSome());
|
||||
|
||||
@ -396,9 +420,10 @@ FrameAnimator::GetCompositedFrame(AnimationState& aState)
|
||||
|
||||
Maybe<FrameTimeout>
|
||||
FrameAnimator::GetTimeoutForFrame(AnimationState& aState,
|
||||
DrawableSurface& aFrames,
|
||||
uint32_t aFrameNum) const
|
||||
{
|
||||
RawAccessFrameRef frame = GetRawFrame(aFrameNum);
|
||||
RawAccessFrameRef frame = GetRawFrame(aFrames, aFrameNum);
|
||||
if (frame) {
|
||||
AnimationData data = frame->GetAnimationData();
|
||||
return Some(data.mTimeout);
|
||||
@ -454,37 +479,29 @@ FrameAnimator::CollectSizeOfCompositingSurfaces(
|
||||
}
|
||||
|
||||
RawAccessFrameRef
|
||||
FrameAnimator::GetRawFrame(uint32_t aFrameNum) const
|
||||
FrameAnimator::GetRawFrame(DrawableSurface& aFrames, uint32_t aFrameNum) const
|
||||
{
|
||||
LookupResult result =
|
||||
SurfaceCache::Lookup(ImageKey(mImage),
|
||||
RasterSurfaceKey(mSize,
|
||||
DefaultSurfaceFlags(),
|
||||
PlaybackType::eAnimated));
|
||||
if (!result) {
|
||||
return RawAccessFrameRef();
|
||||
}
|
||||
|
||||
// Seek to the frame we want. If seeking fails, it means we couldn't get the
|
||||
// frame we're looking for, so we bail here to avoid returning the wrong frame
|
||||
// to the caller.
|
||||
if (NS_FAILED(result.Surface().Seek(aFrameNum))) {
|
||||
if (NS_FAILED(aFrames.Seek(aFrameNum))) {
|
||||
return RawAccessFrameRef(); // Not available yet.
|
||||
}
|
||||
|
||||
return result.Surface()->RawAccessRef();
|
||||
return aFrames->RawAccessRef();
|
||||
}
|
||||
|
||||
//******************************************************************************
|
||||
// DoBlend gets called when the timer for animation get fired and we have to
|
||||
// update the composited frame of the animation.
|
||||
bool
|
||||
FrameAnimator::DoBlend(IntRect* aDirtyRect,
|
||||
FrameAnimator::DoBlend(DrawableSurface& aFrames,
|
||||
IntRect* aDirtyRect,
|
||||
uint32_t aPrevFrameIndex,
|
||||
uint32_t aNextFrameIndex)
|
||||
{
|
||||
RawAccessFrameRef prevFrame = GetRawFrame(aPrevFrameIndex);
|
||||
RawAccessFrameRef nextFrame = GetRawFrame(aNextFrameIndex);
|
||||
RawAccessFrameRef prevFrame = GetRawFrame(aFrames, aPrevFrameIndex);
|
||||
RawAccessFrameRef nextFrame = GetRawFrame(aFrames, aNextFrameIndex);
|
||||
|
||||
MOZ_ASSERT(prevFrame && nextFrame, "Should have frames here");
|
||||
|
||||
|
@ -21,6 +21,7 @@ namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
class RasterImage;
|
||||
class DrawableSurface;
|
||||
|
||||
class AnimationState
|
||||
{
|
||||
@ -305,15 +306,19 @@ private: // methods
|
||||
* @returns a RefreshResult that shows whether the frame was successfully
|
||||
* advanced, and its resulting dirty rect.
|
||||
*/
|
||||
RefreshResult AdvanceFrame(AnimationState& aState, TimeStamp aTime);
|
||||
RefreshResult AdvanceFrame(AnimationState& aState,
|
||||
DrawableSurface& aFrames,
|
||||
TimeStamp aTime);
|
||||
|
||||
/**
|
||||
* Get the @aIndex-th frame in the frame index, ignoring results of blending.
|
||||
*/
|
||||
RawAccessFrameRef GetRawFrame(uint32_t aFrameNum) const;
|
||||
RawAccessFrameRef GetRawFrame(DrawableSurface& aFrames,
|
||||
uint32_t aFrameNum) const;
|
||||
|
||||
/// @return the given frame's timeout if it is available
|
||||
Maybe<FrameTimeout> GetTimeoutForFrame(AnimationState& aState,
|
||||
DrawableSurface& aFrames,
|
||||
uint32_t aFrameNum) const;
|
||||
|
||||
/**
|
||||
@ -322,9 +327,11 @@ private: // methods
|
||||
* In the error case (like if the requested frame is not currently
|
||||
* decoded), returns None().
|
||||
*/
|
||||
Maybe<TimeStamp> GetCurrentImgFrameEndTime(AnimationState& aState) const;
|
||||
Maybe<TimeStamp> GetCurrentImgFrameEndTime(AnimationState& aState,
|
||||
DrawableSurface& aFrames) const;
|
||||
|
||||
bool DoBlend(gfx::IntRect* aDirtyRect,
|
||||
bool DoBlend(DrawableSurface& aFrames,
|
||||
gfx::IntRect* aDirtyRect,
|
||||
uint32_t aPrevFrameIndex,
|
||||
uint32_t aNextFrameIndex);
|
||||
|
||||
|
@ -1225,6 +1225,9 @@ RasterImage::Decode(const IntSize& aSize,
|
||||
// Create a decoder.
|
||||
RefPtr<IDecodingTask> task;
|
||||
if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) {
|
||||
task = DecoderFactory::CreateAnimationDecoder(mDecoderType, WrapNotNull(this),
|
||||
mSourceBuffer, mSize,
|
||||
decoderFlags, surfaceFlags);
|
||||
mAnimationState->SetDiscarded(false);
|
||||
// If the animation is finished we can draw right away because we just draw
|
||||
// the final frame all the time from now on. See comment in
|
||||
@ -1232,9 +1235,6 @@ RasterImage::Decode(const IntSize& aSize,
|
||||
if (mAnimationFinished) {
|
||||
mAnimationState->SetCompositedFrameInvalid(false);
|
||||
}
|
||||
task = DecoderFactory::CreateAnimationDecoder(mDecoderType, WrapNotNull(this),
|
||||
mSourceBuffer, mSize,
|
||||
decoderFlags, surfaceFlags);
|
||||
} else {
|
||||
task = DecoderFactory::CreateDecoder(mDecoderType, WrapNotNull(this),
|
||||
mSourceBuffer, mSize, aSize,
|
||||
|
@ -61,15 +61,7 @@ NS_DEFINE_NAMED_CID(NS_COLLATIONFACTORY_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_SCRIPTABLEDATEFORMAT_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_LANGUAGEATOMSERVICE_CID);
|
||||
NS_DEFINE_NAMED_CID(NS_PLATFORMCHARSET_CID);
|
||||
#ifdef XP_WIN
|
||||
NS_DEFINE_NAMED_CID(NS_COLLATION_CID);
|
||||
#endif
|
||||
#ifdef USE_UNIX_LOCALE
|
||||
NS_DEFINE_NAMED_CID(NS_COLLATION_CID);
|
||||
#endif
|
||||
#ifdef USE_MAC_LOCALE
|
||||
NS_DEFINE_NAMED_CID(NS_COLLATION_CID);
|
||||
#endif
|
||||
|
||||
static const mozilla::Module::CIDEntry kIntlCIDs[] = {
|
||||
{ &kMOZ_LOCALESERVICE_CID, false, nullptr, mozilla::intl::LocaleServiceConstructor },
|
||||
@ -89,15 +81,7 @@ static const mozilla::Module::CIDEntry kIntlCIDs[] = {
|
||||
{ &kNS_SCRIPTABLEDATEFORMAT_CID, false, nullptr, NS_NewScriptableDateFormat },
|
||||
{ &kNS_LANGUAGEATOMSERVICE_CID, false, nullptr, nsLanguageAtomServiceConstructor },
|
||||
{ &kNS_PLATFORMCHARSET_CID, false, nullptr, nsPlatformCharsetConstructor },
|
||||
#ifdef XP_WIN
|
||||
{ &kNS_COLLATION_CID, false, nullptr, nsCollationWinConstructor },
|
||||
#endif
|
||||
#ifdef USE_UNIX_LOCALE
|
||||
{ &kNS_COLLATION_CID, false, nullptr, nsCollationUnixConstructor },
|
||||
#endif
|
||||
#ifdef USE_MAC_LOCALE
|
||||
{ &kNS_COLLATION_CID, false, nullptr, nsCollationMacUCConstructor },
|
||||
#endif
|
||||
{ &kNS_COLLATION_CID, false, nullptr, nsCollationConstructor },
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
@ -119,15 +103,7 @@ static const mozilla::Module::ContractIDEntry kIntlContracts[] = {
|
||||
{ NS_SCRIPTABLEDATEFORMAT_CONTRACTID, &kNS_SCRIPTABLEDATEFORMAT_CID },
|
||||
{ NS_LANGUAGEATOMSERVICE_CONTRACTID, &kNS_LANGUAGEATOMSERVICE_CID },
|
||||
{ NS_PLATFORMCHARSET_CONTRACTID, &kNS_PLATFORMCHARSET_CID },
|
||||
#ifdef XP_WIN
|
||||
{ NS_COLLATION_CONTRACTID, &kNS_COLLATION_CID },
|
||||
#endif
|
||||
#ifdef USE_UNIX_LOCALE
|
||||
{ NS_COLLATION_CONTRACTID, &kNS_COLLATION_CID },
|
||||
#endif
|
||||
#ifdef USE_MAC_LOCALE
|
||||
{ NS_COLLATION_CONTRACTID, &kNS_COLLATION_CID },
|
||||
#endif
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'nsCollationMacUC.cpp',
|
||||
'nsMacCharset.cpp',
|
||||
]
|
||||
|
||||
|
@ -1,213 +0,0 @@
|
||||
/* -*- 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 "nsCollationMacUC.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "prmem.h"
|
||||
#include "nsString.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsCollationMacUC, nsICollation)
|
||||
|
||||
nsCollationMacUC::nsCollationMacUC()
|
||||
: mInit(false)
|
||||
, mHasCollator(false)
|
||||
, mLastStrength(-1)
|
||||
, mCollatorICU(nullptr)
|
||||
{ }
|
||||
|
||||
nsCollationMacUC::~nsCollationMacUC()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
nsresult res =
|
||||
#endif
|
||||
CleanUpCollator();
|
||||
NS_ASSERTION(NS_SUCCEEDED(res), "CleanUpCollator failed");
|
||||
}
|
||||
|
||||
nsresult nsCollationMacUC::ConvertStrength(const int32_t aNSStrength,
|
||||
UCollationStrength* aICUStrength,
|
||||
UColAttributeValue* aCaseLevelOut)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aICUStrength);
|
||||
NS_ENSURE_TRUE((aNSStrength < 4), NS_ERROR_FAILURE);
|
||||
|
||||
UCollationStrength strength = UCOL_DEFAULT;
|
||||
UColAttributeValue caseLevel = UCOL_OFF;
|
||||
switch (aNSStrength) {
|
||||
case kCollationCaseInSensitive:
|
||||
strength = UCOL_PRIMARY;
|
||||
break;
|
||||
case kCollationCaseInsensitiveAscii:
|
||||
strength = UCOL_SECONDARY;
|
||||
break;
|
||||
case kCollationAccentInsenstive:
|
||||
caseLevel = UCOL_ON;
|
||||
strength = UCOL_PRIMARY;
|
||||
break;
|
||||
case kCollationCaseSensitive:
|
||||
strength = UCOL_TERTIARY;
|
||||
break;
|
||||
default:
|
||||
NS_WARNING("Bad aNSStrength passed to ConvertStrength.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
*aICUStrength = strength;
|
||||
*aCaseLevelOut = caseLevel;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsCollationMacUC::EnsureCollator(const int32_t newStrength)
|
||||
{
|
||||
NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
|
||||
if (mHasCollator && (mLastStrength == newStrength))
|
||||
return NS_OK;
|
||||
|
||||
nsresult res;
|
||||
res = CleanUpCollator();
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
UErrorCode status;
|
||||
status = U_ZERO_ERROR;
|
||||
mCollatorICU = ucol_open(mLocale.get(), &status);
|
||||
NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
|
||||
|
||||
UCollationStrength strength;
|
||||
UColAttributeValue caseLevel;
|
||||
res = ConvertStrength(newStrength, &strength, &caseLevel);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
status = U_ZERO_ERROR;
|
||||
ucol_setAttribute(mCollatorICU, UCOL_STRENGTH, strength, &status);
|
||||
NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
|
||||
ucol_setAttribute(mCollatorICU, UCOL_CASE_LEVEL, caseLevel, &status);
|
||||
NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
|
||||
ucol_setAttribute(mCollatorICU, UCOL_ALTERNATE_HANDLING, UCOL_DEFAULT, &status);
|
||||
NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
|
||||
ucol_setAttribute(mCollatorICU, UCOL_NUMERIC_COLLATION, UCOL_OFF, &status);
|
||||
NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
|
||||
ucol_setAttribute(mCollatorICU, UCOL_NORMALIZATION_MODE, UCOL_ON, &status);
|
||||
NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
|
||||
ucol_setAttribute(mCollatorICU, UCOL_CASE_FIRST, UCOL_DEFAULT, &status);
|
||||
NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
|
||||
|
||||
mHasCollator = true;
|
||||
|
||||
mLastStrength = newStrength;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsCollationMacUC::CleanUpCollator(void)
|
||||
{
|
||||
if (mHasCollator) {
|
||||
ucol_close(mCollatorICU);
|
||||
mHasCollator = false;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCollationMacUC::Initialize(const nsACString& locale)
|
||||
{
|
||||
NS_ENSURE_TRUE((!mInit), NS_ERROR_ALREADY_INITIALIZED);
|
||||
nsCOMPtr<nsILocale> appLocale;
|
||||
|
||||
mLocale = locale;
|
||||
|
||||
mInit = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCollationMacUC::AllocateRawSortKey(int32_t strength, const nsAString& stringIn,
|
||||
uint8_t** key, uint32_t* outLen)
|
||||
{
|
||||
NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_ARG_POINTER(key);
|
||||
NS_ENSURE_ARG_POINTER(outLen);
|
||||
|
||||
nsresult res = EnsureCollator(strength);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
uint32_t stringInLen = stringIn.Length();
|
||||
|
||||
const UChar* str = (const UChar*)stringIn.BeginReading();
|
||||
|
||||
int32_t keyLength = ucol_getSortKey(mCollatorICU, str, stringInLen, nullptr, 0);
|
||||
NS_ENSURE_TRUE((stringInLen == 0 || keyLength > 0), NS_ERROR_FAILURE);
|
||||
|
||||
// Since key is freed elsewhere with PR_Free, allocate with PR_Malloc.
|
||||
uint8_t* newKey = (uint8_t*)PR_Malloc(keyLength + 1);
|
||||
if (!newKey) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
keyLength = ucol_getSortKey(mCollatorICU, str, stringInLen, newKey, keyLength + 1);
|
||||
NS_ENSURE_TRUE((stringInLen == 0 || keyLength > 0), NS_ERROR_FAILURE);
|
||||
|
||||
*key = newKey;
|
||||
*outLen = keyLength;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCollationMacUC::CompareString(int32_t strength, const nsAString& string1,
|
||||
const nsAString& string2, int32_t* result)
|
||||
{
|
||||
NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
*result = 0;
|
||||
|
||||
nsresult rv = EnsureCollator(strength);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
UCollationResult uresult;
|
||||
uresult = ucol_strcoll(mCollatorICU,
|
||||
(const UChar*)string1.BeginReading(),
|
||||
string1.Length(),
|
||||
(const UChar*)string2.BeginReading(),
|
||||
string2.Length());
|
||||
int32_t res;
|
||||
switch (uresult) {
|
||||
case UCOL_LESS:
|
||||
res = -1;
|
||||
break;
|
||||
case UCOL_EQUAL:
|
||||
res = 0;
|
||||
break;
|
||||
case UCOL_GREATER:
|
||||
res = 1;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("ucol_strcoll returned bad UCollationResult");
|
||||
}
|
||||
*result = res;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsCollationMacUC::CompareRawSortKey(const uint8_t* key1, uint32_t len1,
|
||||
const uint8_t* key2, uint32_t len2,
|
||||
int32_t* result)
|
||||
{
|
||||
NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_ARG_POINTER(key1);
|
||||
NS_ENSURE_ARG_POINTER(key2);
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
*result = 0;
|
||||
|
||||
int32_t tmpResult = strcmp((const char*)key1, (const char*)key2);
|
||||
int32_t res;
|
||||
if (tmpResult < 0) {
|
||||
res = -1;
|
||||
} else if (tmpResult > 0) {
|
||||
res = 1;
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
*result = res;
|
||||
return NS_OK;
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/* -*- 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 nsCollationMacUC_h_
|
||||
#define nsCollationMacUC_h_
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsICollation.h"
|
||||
#include "nsCollation.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "unicode/ucol.h"
|
||||
|
||||
class nsCollationMacUC final : public nsICollation {
|
||||
|
||||
public:
|
||||
nsCollationMacUC();
|
||||
|
||||
// nsISupports interface
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// nsICollation interface
|
||||
NS_DECL_NSICOLLATION
|
||||
|
||||
protected:
|
||||
~nsCollationMacUC();
|
||||
|
||||
nsresult ConvertStrength(const int32_t aStrength,
|
||||
UCollationStrength* aStrengthOut,
|
||||
UColAttributeValue* aCaseLevelOut);
|
||||
nsresult EnsureCollator(const int32_t newStrength);
|
||||
nsresult CleanUpCollator(void);
|
||||
|
||||
private:
|
||||
bool mInit;
|
||||
bool mHasCollator;
|
||||
nsCString mLocale;
|
||||
int32_t mLastStrength;
|
||||
UCollator* mCollatorICU;
|
||||
};
|
||||
|
||||
#endif /* nsCollationMacUC_h_ */
|
@ -32,7 +32,6 @@ XPIDL_MODULE = 'locale'
|
||||
|
||||
EXPORTS += [
|
||||
'DateTimeFormat.h',
|
||||
'nsCollation.h',
|
||||
'nsCollationCID.h',
|
||||
'nsILanguageAtomService.h',
|
||||
'nsIPlatformCharset.h',
|
||||
@ -48,7 +47,7 @@ EXPORTS.mozilla.intl += [
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'LocaleService.cpp',
|
||||
'nsCollation.cpp',
|
||||
'nsCollationFactory.cpp',
|
||||
'nsLanguageAtomService.cpp',
|
||||
'nsLocale.cpp',
|
||||
'nsLocaleService.cpp',
|
||||
@ -60,10 +59,12 @@ UNIFIED_SOURCES += [
|
||||
if CONFIG['ENABLE_INTL_API']:
|
||||
UNIFIED_SOURCES += [
|
||||
'DateTimeFormat.cpp',
|
||||
'nsCollation.cpp',
|
||||
]
|
||||
else:
|
||||
UNIFIED_SOURCES += [
|
||||
'DateTimeFormatAndroid.cpp',
|
||||
'nsCollationAndroid.cpp',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
|
@ -4,142 +4,214 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsCollation.h"
|
||||
#include "nsCollationCID.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "prmem.h"
|
||||
#include "nsIUnicodeEncoder.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "mozilla/intl/LocaleService.h"
|
||||
#include "nsString.h"
|
||||
|
||||
using mozilla::dom::EncodingUtils;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NS_DEFINE_CID(kCollationCID, NS_COLLATION_CID);
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsCollationFactory, nsICollationFactory)
|
||||
|
||||
nsresult nsCollationFactory::CreateCollation(nsICollation** instancePtr)
|
||||
{
|
||||
nsAutoCString appLocale;
|
||||
mozilla::intl::LocaleService::GetInstance()->GetAppLocaleAsLangTag(appLocale);
|
||||
|
||||
return CreateCollationForLocale(appLocale, instancePtr);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCollationFactory::CreateCollationForLocale(const nsACString& locale, nsICollation** instancePtr)
|
||||
{
|
||||
// Create a collation interface instance.
|
||||
//
|
||||
nsICollation *inst;
|
||||
nsresult res;
|
||||
|
||||
res = CallCreateInstance(kCollationCID, &inst);
|
||||
if (NS_FAILED(res)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
inst->Initialize(locale);
|
||||
|
||||
*instancePtr = inst;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
NS_IMPL_ISUPPORTS(nsCollation, nsICollation)
|
||||
|
||||
nsCollation::nsCollation()
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsCollation);
|
||||
}
|
||||
: mInit(false)
|
||||
, mHasCollator(false)
|
||||
, mLastStrength(-1)
|
||||
, mCollatorICU(nullptr)
|
||||
{ }
|
||||
|
||||
nsCollation::~nsCollation()
|
||||
{
|
||||
MOZ_COUNT_DTOR(nsCollation);
|
||||
#ifdef DEBUG
|
||||
nsresult res =
|
||||
#endif
|
||||
CleanUpCollator();
|
||||
NS_ASSERTION(NS_SUCCEEDED(res), "CleanUpCollator failed");
|
||||
}
|
||||
|
||||
nsresult nsCollation::NormalizeString(const nsAString& stringIn, nsAString& stringOut)
|
||||
nsresult
|
||||
nsCollation::ConvertStrength(const int32_t aNSStrength,
|
||||
UCollationStrength* aICUStrength,
|
||||
UColAttributeValue* aCaseLevelOut)
|
||||
{
|
||||
int32_t aLength = stringIn.Length();
|
||||
NS_ENSURE_ARG_POINTER(aICUStrength);
|
||||
NS_ENSURE_TRUE((aNSStrength < 4), NS_ERROR_FAILURE);
|
||||
|
||||
if (aLength <= 64) {
|
||||
char16_t conversionBuffer[64];
|
||||
ToLowerCase(PromiseFlatString(stringIn).get(), conversionBuffer, aLength);
|
||||
stringOut.Assign(conversionBuffer, aLength);
|
||||
UCollationStrength strength = UCOL_DEFAULT;
|
||||
UColAttributeValue caseLevel = UCOL_OFF;
|
||||
switch (aNSStrength) {
|
||||
case kCollationCaseInSensitive:
|
||||
strength = UCOL_PRIMARY;
|
||||
break;
|
||||
case kCollationCaseInsensitiveAscii:
|
||||
strength = UCOL_SECONDARY;
|
||||
break;
|
||||
case kCollationAccentInsenstive:
|
||||
caseLevel = UCOL_ON;
|
||||
strength = UCOL_PRIMARY;
|
||||
break;
|
||||
case kCollationCaseSensitive:
|
||||
strength = UCOL_TERTIARY;
|
||||
break;
|
||||
default:
|
||||
NS_WARNING("Bad aNSStrength passed to ConvertStrength.");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
else {
|
||||
char16_t* conversionBuffer;
|
||||
conversionBuffer = new char16_t[aLength];
|
||||
if (!conversionBuffer) {
|
||||
|
||||
*aICUStrength = strength;
|
||||
*aCaseLevelOut = caseLevel;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCollation::EnsureCollator(const int32_t newStrength)
|
||||
{
|
||||
NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
|
||||
if (mHasCollator && (mLastStrength == newStrength))
|
||||
return NS_OK;
|
||||
|
||||
nsresult res;
|
||||
res = CleanUpCollator();
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
UErrorCode status;
|
||||
status = U_ZERO_ERROR;
|
||||
mCollatorICU = ucol_open(mLocale.get(), &status);
|
||||
NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
|
||||
|
||||
UCollationStrength strength;
|
||||
UColAttributeValue caseLevel;
|
||||
res = ConvertStrength(newStrength, &strength, &caseLevel);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
status = U_ZERO_ERROR;
|
||||
ucol_setAttribute(mCollatorICU, UCOL_STRENGTH, strength, &status);
|
||||
NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
|
||||
ucol_setAttribute(mCollatorICU, UCOL_CASE_LEVEL, caseLevel, &status);
|
||||
NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
|
||||
ucol_setAttribute(mCollatorICU, UCOL_ALTERNATE_HANDLING, UCOL_DEFAULT, &status);
|
||||
NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
|
||||
ucol_setAttribute(mCollatorICU, UCOL_NUMERIC_COLLATION, UCOL_OFF, &status);
|
||||
NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
|
||||
ucol_setAttribute(mCollatorICU, UCOL_NORMALIZATION_MODE, UCOL_ON, &status);
|
||||
NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
|
||||
ucol_setAttribute(mCollatorICU, UCOL_CASE_FIRST, UCOL_DEFAULT, &status);
|
||||
NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
|
||||
|
||||
mHasCollator = true;
|
||||
|
||||
mLastStrength = newStrength;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCollation::CleanUpCollator(void)
|
||||
{
|
||||
if (mHasCollator) {
|
||||
ucol_close(mCollatorICU);
|
||||
mHasCollator = false;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCollation::Initialize(const nsACString& locale)
|
||||
{
|
||||
NS_ENSURE_TRUE((!mInit), NS_ERROR_ALREADY_INITIALIZED);
|
||||
|
||||
mLocale = locale;
|
||||
|
||||
mInit = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCollation::AllocateRawSortKey(int32_t strength, const nsAString& stringIn,
|
||||
uint8_t** key, uint32_t* outLen)
|
||||
{
|
||||
NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_ARG_POINTER(key);
|
||||
NS_ENSURE_ARG_POINTER(outLen);
|
||||
|
||||
nsresult res = EnsureCollator(strength);
|
||||
NS_ENSURE_SUCCESS(res, res);
|
||||
|
||||
uint32_t stringInLen = stringIn.Length();
|
||||
|
||||
const UChar* str = (const UChar*)stringIn.BeginReading();
|
||||
|
||||
int32_t keyLength = ucol_getSortKey(mCollatorICU, str, stringInLen, nullptr, 0);
|
||||
NS_ENSURE_TRUE((stringInLen == 0 || keyLength > 0), NS_ERROR_FAILURE);
|
||||
|
||||
// Since key is freed elsewhere with PR_Free, allocate with PR_Malloc.
|
||||
uint8_t* newKey = (uint8_t*)PR_Malloc(keyLength + 1);
|
||||
if (!newKey) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
ToLowerCase(PromiseFlatString(stringIn).get(), conversionBuffer, aLength);
|
||||
stringOut.Assign(conversionBuffer, aLength);
|
||||
delete [] conversionBuffer;
|
||||
}
|
||||
|
||||
keyLength = ucol_getSortKey(mCollatorICU, str, stringInLen, newKey, keyLength + 1);
|
||||
NS_ENSURE_TRUE((stringInLen == 0 || keyLength > 0), NS_ERROR_FAILURE);
|
||||
|
||||
*key = newKey;
|
||||
*outLen = keyLength;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsCollation::SetCharset(const char* aCharset)
|
||||
NS_IMETHODIMP
|
||||
nsCollation::CompareString(int32_t strength, const nsAString& string1,
|
||||
const nsAString& string2, int32_t* result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aCharset);
|
||||
NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
*result = 0;
|
||||
|
||||
nsDependentCString label(aCharset);
|
||||
nsAutoCString encoding;
|
||||
if (!EncodingUtils::FindEncodingForLabelNoReplacement(label, encoding)) {
|
||||
return NS_ERROR_UCONV_NOCONV;
|
||||
nsresult rv = EnsureCollator(strength);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
UCollationResult uresult;
|
||||
uresult = ucol_strcoll(mCollatorICU,
|
||||
(const UChar*)string1.BeginReading(),
|
||||
string1.Length(),
|
||||
(const UChar*)string2.BeginReading(),
|
||||
string2.Length());
|
||||
int32_t res;
|
||||
switch (uresult) {
|
||||
case UCOL_LESS:
|
||||
res = -1;
|
||||
break;
|
||||
case UCOL_EQUAL:
|
||||
res = 0;
|
||||
break;
|
||||
case UCOL_GREATER:
|
||||
res = 1;
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("ucol_strcoll returned bad UCollationResult");
|
||||
}
|
||||
mEncoder = EncodingUtils::EncoderForEncoding(encoding);
|
||||
*result = res;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsCollation::UnicodeToChar(const nsAString& aSrc, char** dst)
|
||||
NS_IMETHODIMP
|
||||
nsCollation::CompareRawSortKey(const uint8_t* key1, uint32_t len1,
|
||||
const uint8_t* key2, uint32_t len2,
|
||||
int32_t* result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(dst);
|
||||
NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_ARG_POINTER(key1);
|
||||
NS_ENSURE_ARG_POINTER(key2);
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
*result = 0;
|
||||
|
||||
nsresult res = NS_OK;
|
||||
if (!mEncoder)
|
||||
res = SetCharset("ISO-8859-1");
|
||||
|
||||
if (NS_SUCCEEDED(res)) {
|
||||
const nsPromiseFlatString& src = PromiseFlatString(aSrc);
|
||||
const char16_t *unichars = src.get();
|
||||
int32_t unicharLength = src.Length();
|
||||
int32_t dstLength;
|
||||
res = mEncoder->GetMaxLength(unichars, unicharLength, &dstLength);
|
||||
if (NS_SUCCEEDED(res)) {
|
||||
int32_t bufLength = dstLength + 1 + 32; // extra 32 bytes for Finish() call
|
||||
*dst = (char *) PR_Malloc(bufLength);
|
||||
if (*dst) {
|
||||
**dst = '\0';
|
||||
res = mEncoder->Convert(unichars, &unicharLength, *dst, &dstLength);
|
||||
|
||||
if (NS_SUCCEEDED(res) || (NS_ERROR_UENC_NOMAPPING == res)) {
|
||||
// Finishes the conversion. The converter has the possibility to write some
|
||||
// extra data and flush its final state.
|
||||
int32_t finishLength = bufLength - dstLength; // remaining unused buffer length
|
||||
if (finishLength > 0) {
|
||||
res = mEncoder->Finish((*dst + dstLength), &finishLength);
|
||||
if (NS_SUCCEEDED(res)) {
|
||||
(*dst)[dstLength + finishLength] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (NS_FAILED(res)) {
|
||||
PR_Free(*dst);
|
||||
*dst = nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
res = NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
int32_t tmpResult = strcmp((const char*)key1, (const char*)key2);
|
||||
int32_t res;
|
||||
if (tmpResult < 0) {
|
||||
res = -1;
|
||||
} else if (tmpResult > 0) {
|
||||
res = 1;
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
|
||||
return res;
|
||||
*result = res;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,52 +1,48 @@
|
||||
|
||||
/* -*- 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
|
||||
/* -*- 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 nsCollation_h__
|
||||
#define nsCollation_h__
|
||||
|
||||
#ifndef nsCollation_h_
|
||||
#define nsCollation_h_
|
||||
|
||||
#include "nsICollation.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsICollation.h"
|
||||
#include "nsCollationFactory.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsIUnicodeEncoder;
|
||||
#ifdef ENABLE_INTL_API
|
||||
#include "unicode/ucol.h"
|
||||
#endif
|
||||
|
||||
// Create a collation interface for the current app's locale.
|
||||
//
|
||||
class nsCollationFactory final : public nsICollationFactory {
|
||||
|
||||
~nsCollationFactory() {}
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD CreateCollation(nsICollation** instancePtr) override;
|
||||
NS_IMETHOD CreateCollationForLocale(const nsACString& locale, nsICollation** instancePtr) override;
|
||||
|
||||
nsCollationFactory() {}
|
||||
};
|
||||
|
||||
|
||||
struct nsCollation {
|
||||
|
||||
public:
|
||||
class nsCollation final : public nsICollation {
|
||||
|
||||
public:
|
||||
nsCollation();
|
||||
|
||||
~nsCollation();
|
||||
|
||||
// normalize string before collation key generation
|
||||
nsresult NormalizeString(const nsAString& stringIn, nsAString& stringOut);
|
||||
// nsISupports interface
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// charset conversion util, C string buffer is allocate by PR_Malloc, caller should call PR_Free
|
||||
nsresult SetCharset(const char* aCharset);
|
||||
nsresult UnicodeToChar(const nsAString& aSrc, char** dst);
|
||||
// nsICollation interface
|
||||
NS_DECL_NSICOLLATION
|
||||
|
||||
protected:
|
||||
nsCOMPtr <nsIUnicodeEncoder> mEncoder;
|
||||
~nsCollation();
|
||||
|
||||
#ifdef ENABLE_INTL_API
|
||||
nsresult ConvertStrength(const int32_t aStrength,
|
||||
UCollationStrength* aStrengthOut,
|
||||
UColAttributeValue* aCaseLevelOut);
|
||||
nsresult EnsureCollator(const int32_t newStrength);
|
||||
nsresult CleanUpCollator(void);
|
||||
|
||||
private:
|
||||
bool mInit;
|
||||
bool mHasCollator;
|
||||
nsCString mLocale;
|
||||
int32_t mLastStrength;
|
||||
UCollator* mCollatorICU;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* nsCollation_h__ */
|
||||
#endif /* nsCollation_h_ */
|
||||
|
85
intl/locale/nsCollationAndroid.cpp
Normal file
85
intl/locale/nsCollationAndroid.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/* -*- 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 "nsCollation.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "prmem.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
nsCollation::nsCollation()
|
||||
{
|
||||
}
|
||||
|
||||
nsCollation::~nsCollation()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsCollation, nsICollation)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCollation::Initialize(const nsACString& locale)
|
||||
{
|
||||
// Android doesn't have locale support
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCollation::CompareString(int32_t strength,
|
||||
const nsAString& string1,
|
||||
const nsAString& string2,
|
||||
int32_t* result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
|
||||
nsAutoString stringNormalized1(string1);
|
||||
nsAutoString stringNormalized2(string2);
|
||||
if (strength != kCollationCaseSensitive) {
|
||||
ToLowerCase(stringNormalized1);
|
||||
ToLowerCase(stringNormalized2);
|
||||
}
|
||||
|
||||
*result = strcmp(NS_ConvertUTF16toUTF8(stringNormalized1).get(),
|
||||
NS_ConvertUTF16toUTF8(stringNormalized2).get());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCollation::AllocateRawSortKey(int32_t strength,
|
||||
const nsAString& stringIn,
|
||||
uint8_t** key, uint32_t* outLen)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(key);
|
||||
NS_ENSURE_ARG_POINTER(outLen);
|
||||
|
||||
nsAutoString stringNormalized(stringIn);
|
||||
if (strength != kCollationCaseSensitive) {
|
||||
ToLowerCase(stringNormalized);
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 str(stringNormalized);
|
||||
size_t len = str.Length() + 1;
|
||||
void *buffer = PR_Malloc(len);
|
||||
memcpy(buffer, str.get(), len);
|
||||
*key = (uint8_t *)buffer;
|
||||
*outLen = len;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCollation::CompareRawSortKey(const uint8_t* key1, uint32_t len1,
|
||||
const uint8_t* key2, uint32_t len2,
|
||||
int32_t* result)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(key1);
|
||||
NS_ENSURE_ARG_POINTER(key2);
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
|
||||
*result = strcmp((const char *)key1, (const char *)key2);
|
||||
return NS_OK;
|
||||
}
|
43
intl/locale/nsCollationFactory.cpp
Normal file
43
intl/locale/nsCollationFactory.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
/* -*- 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 "nsCollationFactory.h"
|
||||
#include "nsCollationCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "mozilla/intl/LocaleService.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NS_DEFINE_CID(kCollationCID, NS_COLLATION_CID);
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsCollationFactory, nsICollationFactory)
|
||||
|
||||
nsresult nsCollationFactory::CreateCollation(nsICollation** instancePtr)
|
||||
{
|
||||
nsAutoCString appLocale;
|
||||
mozilla::intl::LocaleService::GetInstance()->GetAppLocaleAsLangTag(appLocale);
|
||||
|
||||
return CreateCollationForLocale(appLocale, instancePtr);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsCollationFactory::CreateCollationForLocale(const nsACString& locale, nsICollation** instancePtr)
|
||||
{
|
||||
// Create a collation interface instance.
|
||||
//
|
||||
nsICollation *inst;
|
||||
nsresult res;
|
||||
|
||||
res = CallCreateInstance(kCollationCID, &inst);
|
||||
if (NS_FAILED(res)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
inst->Initialize(locale);
|
||||
|
||||
*instancePtr = inst;
|
||||
|
||||
return res;
|
||||
}
|
28
intl/locale/nsCollationFactory.h
Normal file
28
intl/locale/nsCollationFactory.h
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
/* -*- 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 nsCollationFactory_h__
|
||||
#define nsCollationFactory_h__
|
||||
|
||||
|
||||
#include "nsICollation.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
// Create a collation interface for the current app's locale.
|
||||
//
|
||||
class nsCollationFactory final : public nsICollationFactory {
|
||||
|
||||
~nsCollationFactory() {}
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICOLLATIONFACTORY
|
||||
|
||||
nsCollationFactory() {}
|
||||
};
|
||||
|
||||
#endif /* nsCollationFactory_h__ */
|
@ -132,8 +132,7 @@ interface nsIScriptableDateFormat : nsISupports
|
||||
* you depend on any specific format or language.
|
||||
*
|
||||
* @param locale
|
||||
* Locale code of locale used to format the date or an empty string
|
||||
* to follow user preference.
|
||||
Ignored. If you want to use specific locale, use Intl API instead.
|
||||
* @param dateFormatSelector
|
||||
* Indicate which format should preferably be used for the date.
|
||||
* Use one of the dateFormat* constants.
|
||||
|
@ -6,6 +6,7 @@
|
||||
#ifndef nsLocaleConstructors_h__
|
||||
#define nsLocaleConstructors_h__
|
||||
|
||||
#include "nsCollation.h"
|
||||
#include "nsCollationCID.h"
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
#include "nsILocaleService.h"
|
||||
@ -16,26 +17,6 @@
|
||||
#include "LocaleService.h"
|
||||
#include "OSPreferences.h"
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
#define USE_MAC_LOCALE
|
||||
#endif
|
||||
|
||||
#if defined(XP_UNIX) && !defined(XP_MACOSX)
|
||||
#define USE_UNIX_LOCALE
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "windows/nsCollationWin.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_MAC_LOCALE
|
||||
#include "mac/nsCollationMacUC.h"
|
||||
#endif
|
||||
|
||||
#ifdef USE_UNIX_LOCALE
|
||||
#include "unix/nsCollationUnix.h"
|
||||
#endif
|
||||
|
||||
#define NSLOCALE_MAKE_CTOR(ctor_, iface_, func_) \
|
||||
static nsresult \
|
||||
ctor_(nsISupports* aOuter, REFNSIID aIID, void** aResult) \
|
||||
@ -54,6 +35,7 @@ ctor_(nsISupports* aOuter, REFNSIID aIID, void** aResult) \
|
||||
|
||||
|
||||
NSLOCALE_MAKE_CTOR(CreateLocaleService, nsILocaleService, NS_NewLocaleService)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsCollation)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsCollationFactory)
|
||||
//NS_GENERIC_FACTORY_CONSTRUCTOR(nsScriptableDateTimeFormat)
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsLanguageAtomService)
|
||||
@ -68,16 +50,4 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(OSPreferences,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsCollationWin)
|
||||
#endif
|
||||
|
||||
#ifdef USE_UNIX_LOCALE
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsCollationUnix)
|
||||
#endif
|
||||
|
||||
#ifdef USE_MAC_LOCALE
|
||||
NS_GENERIC_FACTORY_CONSTRUCTOR(nsCollationMacUC)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user