icecat: add release icecat-140.9.0-1gnu1 for ecne
This commit is contained in:
parent
8eb1f1732f
commit
a5f93cb214
1197 changed files with 30593 additions and 15344 deletions
|
|
@ -7,6 +7,7 @@
|
|||
#include "DocumentOrShadowRoot.h"
|
||||
#include "mozilla/AnimationComparator.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/PointerLockManager.h"
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/StyleSheet.h"
|
||||
|
|
@ -112,6 +113,10 @@ void DocumentOrShadowRoot::RemoveSheetFromStylesIfApplicable(
|
|||
void DocumentOrShadowRoot::OnSetAdoptedStyleSheets(StyleSheet& aSheet,
|
||||
uint32_t aIndex,
|
||||
ErrorResult& aRv) {
|
||||
if (MOZ_UNLIKELY(aIndex > mAdoptedStyleSheets.Length())) {
|
||||
MOZ_ASSERT_UNREACHABLE("Out of sync proxy");
|
||||
return;
|
||||
}
|
||||
Document& doc = *AsNode().OwnerDoc();
|
||||
// 1. If value’s constructed flag is not set, or its constructor document is
|
||||
// not equal to this DocumentOrShadowRoot's node document, throw a
|
||||
|
|
@ -164,7 +169,10 @@ void DocumentOrShadowRoot::OnSetAdoptedStyleSheets(StyleSheet& aSheet,
|
|||
void DocumentOrShadowRoot::OnDeleteAdoptedStyleSheets(StyleSheet& aSheet,
|
||||
uint32_t aIndex,
|
||||
ErrorResult&) {
|
||||
MOZ_ASSERT(mAdoptedStyleSheets.ElementAt(aIndex) == &aSheet);
|
||||
if (MOZ_UNLIKELY(mAdoptedStyleSheets.ElementAt(aIndex) != &aSheet)) {
|
||||
MOZ_ASSERT_UNREACHABLE("Out of sync proxy");
|
||||
return;
|
||||
}
|
||||
mAdoptedStyleSheets.RemoveElementAt(aIndex);
|
||||
auto existingIndex = mAdoptedStyleSheets.LastIndexOf(&aSheet);
|
||||
if (existingIndex != mAdoptedStyleSheets.NoIndex && existingIndex >= aIndex) {
|
||||
|
|
|
|||
|
|
@ -271,6 +271,7 @@ template <typename char_type>
|
|||
const nsTSubstring<char_type>& aMimeType,
|
||||
nsTSubstring<char_type>& aOutEssence,
|
||||
nsTSubstring<char_type>& aOutCharset) {
|
||||
// https://fetch.spec.whatwg.org/#concept-header-extract-mime-type
|
||||
static char_type kCHARSET[] = {'c', 'h', 'a', 'r', 's', 'e', 't'};
|
||||
static nsTDependentSubstring<char_type> kCharset(kCHARSET, 7);
|
||||
|
||||
|
|
@ -278,8 +279,8 @@ template <typename char_type>
|
|||
nsTAutoString<char_type> prevContentType;
|
||||
nsTAutoString<char_type> prevCharset;
|
||||
|
||||
prevContentType.Assign(aOutEssence);
|
||||
prevCharset.Assign(aOutCharset);
|
||||
aOutEssence.Truncate();
|
||||
aOutCharset.Truncate();
|
||||
|
||||
nsTArray<nsTDependentSubstring<char_type>> mimeTypeParts =
|
||||
SplitMimetype(aMimeType);
|
||||
|
|
@ -292,9 +293,7 @@ template <typename char_type>
|
|||
parsed = Parse(mimeTypeString);
|
||||
|
||||
if (!parsed) {
|
||||
aOutEssence.Truncate();
|
||||
aOutCharset.Truncate();
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
parsed->GetEssence(aOutEssence);
|
||||
|
|
@ -322,6 +321,10 @@ template <typename char_type>
|
|||
}
|
||||
}
|
||||
|
||||
if (aOutEssence.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,12 +44,15 @@ class ContentPermissionRequestParent : public PContentPermissionRequestParent {
|
|||
// @param aIsRequestDelegatedToUnsafeThirdParty see
|
||||
// mIsRequestDelegatedToUnsafeThirdParty.
|
||||
ContentPermissionRequestParent(
|
||||
const nsTArray<PermissionRequest>& aRequests, Element* aElement,
|
||||
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
|
||||
Element* aElement, nsIPrincipal* aPrincipal,
|
||||
nsIPrincipal* aTopLevelPrincipal,
|
||||
const bool aHasValidTransientUserGestureActivation,
|
||||
const bool aIsRequestDelegatedToUnsafeThirdParty);
|
||||
virtual ~ContentPermissionRequestParent();
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
||||
void Init(nsTArray<PermissionRequest>&& aRequests);
|
||||
|
||||
bool IsBeingDestroyed();
|
||||
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
|
@ -64,16 +67,13 @@ class ContentPermissionRequestParent : public PContentPermissionRequestParent {
|
|||
nsTArray<PermissionRequest> mRequests;
|
||||
|
||||
private:
|
||||
// Not MOZ_CAN_RUN_SCRIPT because we can't annotate the thing we override yet.
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
||||
virtual mozilla::ipc::IPCResult Recvprompt() override;
|
||||
virtual mozilla::ipc::IPCResult RecvDestroy() override;
|
||||
virtual void ActorDestroy(ActorDestroyReason why) override;
|
||||
};
|
||||
|
||||
ContentPermissionRequestParent::ContentPermissionRequestParent(
|
||||
const nsTArray<PermissionRequest>& aRequests, Element* aElement,
|
||||
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
|
||||
Element* aElement, nsIPrincipal* aPrincipal,
|
||||
nsIPrincipal* aTopLevelPrincipal,
|
||||
const bool aHasValidTransientUserGestureActivation,
|
||||
const bool aIsRequestDelegatedToUnsafeThirdParty) {
|
||||
MOZ_COUNT_CTOR(ContentPermissionRequestParent);
|
||||
|
|
@ -81,7 +81,6 @@ ContentPermissionRequestParent::ContentPermissionRequestParent(
|
|||
mPrincipal = aPrincipal;
|
||||
mTopLevelPrincipal = aTopLevelPrincipal;
|
||||
mElement = aElement;
|
||||
mRequests = aRequests.Clone();
|
||||
mHasValidTransientUserGestureActivation =
|
||||
aHasValidTransientUserGestureActivation;
|
||||
mIsRequestDelegatedToUnsafeThirdParty = aIsRequestDelegatedToUnsafeThirdParty;
|
||||
|
|
@ -91,13 +90,14 @@ ContentPermissionRequestParent::~ContentPermissionRequestParent() {
|
|||
MOZ_COUNT_DTOR(ContentPermissionRequestParent);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentPermissionRequestParent::Recvprompt() {
|
||||
void ContentPermissionRequestParent::Init(
|
||||
nsTArray<PermissionRequest>&& aRequests) {
|
||||
mRequests = std::move(aRequests);
|
||||
mProxy = new nsContentPermissionRequestProxy(this);
|
||||
if (NS_FAILED(mProxy->Init(mRequests))) {
|
||||
RefPtr<nsContentPermissionRequestProxy> proxy(mProxy);
|
||||
proxy->Cancel();
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentPermissionRequestParent::RecvDestroy() {
|
||||
|
|
@ -239,12 +239,12 @@ nsresult nsContentPermissionUtils::CreatePermissionArray(
|
|||
/* static */
|
||||
PContentPermissionRequestParent*
|
||||
nsContentPermissionUtils::CreateContentPermissionRequestParent(
|
||||
const nsTArray<PermissionRequest>& aRequests, Element* aElement,
|
||||
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
|
||||
Element* aElement, nsIPrincipal* aPrincipal,
|
||||
nsIPrincipal* aTopLevelPrincipal,
|
||||
const bool aHasValidTransientUserGestureActivation,
|
||||
const bool aIsRequestDelegatedToUnsafeThirdParty, const TabId& aTabId) {
|
||||
PContentPermissionRequestParent* parent = new ContentPermissionRequestParent(
|
||||
aRequests, aElement, aPrincipal, aTopLevelPrincipal,
|
||||
aElement, aPrincipal, aTopLevelPrincipal,
|
||||
aHasValidTransientUserGestureActivation,
|
||||
aIsRequestDelegatedToUnsafeThirdParty);
|
||||
ContentPermissionRequestParentMap()[parent] = aTabId;
|
||||
|
|
@ -252,6 +252,14 @@ nsContentPermissionUtils::CreateContentPermissionRequestParent(
|
|||
return parent;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void nsContentPermissionUtils::InitContentPermissionRequestParent(
|
||||
PContentPermissionRequestParent* aActor,
|
||||
nsTArray<PermissionRequest>&& aRequests) {
|
||||
static_cast<ContentPermissionRequestParent*>(aActor)->Init(
|
||||
std::move(aRequests));
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsresult nsContentPermissionUtils::AskPermission(
|
||||
nsIContentPermissionRequest* aRequest, nsPIDOMWindowInner* aWindow) {
|
||||
|
|
@ -301,7 +309,6 @@ nsresult nsContentPermissionUtils::AskPermission(
|
|||
}
|
||||
ContentPermissionRequestChildMap()[req.get()] = child->GetTabId();
|
||||
|
||||
req->Sendprompt();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,11 +63,15 @@ class nsContentPermissionUtils {
|
|||
// @param aIsRequestDelegatedToUnsafeThirdParty see
|
||||
// ContentPermissionRequestParent.
|
||||
static PContentPermissionRequestParent* CreateContentPermissionRequestParent(
|
||||
const nsTArray<PermissionRequest>& aRequests, Element* aElement,
|
||||
nsIPrincipal* aPrincipal, nsIPrincipal* aTopLevelPrincipal,
|
||||
Element* aElement, nsIPrincipal* aPrincipal,
|
||||
nsIPrincipal* aTopLevelPrincipal,
|
||||
const bool aHasValidTransientUserGestureActivation,
|
||||
const bool aIsRequestDelegatedToUnsafeThirdParty, const TabId& aTabId);
|
||||
|
||||
static void InitContentPermissionRequestParent(
|
||||
PContentPermissionRequestParent* aActor,
|
||||
nsTArray<PermissionRequest>&& aRequests);
|
||||
|
||||
static nsresult AskPermission(nsIContentPermissionRequest* aRequest,
|
||||
nsPIDOMWindowInner* aWindow);
|
||||
|
||||
|
|
|
|||
|
|
@ -821,8 +821,8 @@ TEST(MimeTypeParsing, contentTypes1)
|
|||
|
||||
bool parsed = CMimeType::Parse(val, contentType, contentCharset);
|
||||
|
||||
ASSERT_FALSE(parsed);
|
||||
ASSERT_TRUE(contentType.EqualsLiteral(""));
|
||||
ASSERT_TRUE(parsed);
|
||||
ASSERT_TRUE(contentType.EqualsLiteral("text/plain"));
|
||||
ASSERT_TRUE(contentCharset.EqualsLiteral(""));
|
||||
}
|
||||
|
||||
|
|
@ -1074,3 +1074,43 @@ TEST(MimeTypeParsing, contentTypes20)
|
|||
ASSERT_TRUE(contentType.EqualsLiteral("text/plain"));
|
||||
ASSERT_TRUE(contentCharset.EqualsLiteral(""));
|
||||
}
|
||||
|
||||
// U+002F(/) is not a valid HTTP token code point
|
||||
// https://mimesniff.spec.whatwg.org/#http-token-code-point
|
||||
TEST(MimeTypeParsing, invalidSubtype1)
|
||||
{
|
||||
const nsAutoCString val("text/json/");
|
||||
RefPtr<CMimeType> parsed = CMimeType::Parse(val);
|
||||
ASSERT_TRUE(!parsed);
|
||||
}
|
||||
|
||||
TEST(MimeTypeParsing, invalidSubtype2)
|
||||
{
|
||||
const nsAutoCString val("text/json/bad");
|
||||
RefPtr<CMimeType> parsed = CMimeType::Parse(val);
|
||||
ASSERT_TRUE(!parsed);
|
||||
}
|
||||
|
||||
TEST(MimeTypeParsing, EmptyParsing)
|
||||
{
|
||||
constexpr nsLiteralCString val("");
|
||||
nsCString contentType;
|
||||
nsCString contentCharset;
|
||||
bool parsed = CMimeType::Parse(val, contentType, contentCharset);
|
||||
|
||||
ASSERT_FALSE(parsed);
|
||||
ASSERT_TRUE(contentType.EqualsLiteral(""));
|
||||
ASSERT_TRUE(contentCharset.EqualsLiteral(""));
|
||||
}
|
||||
|
||||
TEST(MimeTypeParsing, EmptySubtype)
|
||||
{
|
||||
constexpr nsLiteralCString val("audio/");
|
||||
nsCString contentType;
|
||||
nsCString contentCharset;
|
||||
bool parsed = CMimeType::Parse(val, contentType, contentCharset);
|
||||
|
||||
ASSERT_FALSE(parsed);
|
||||
ASSERT_TRUE(contentType.EqualsLiteral(""));
|
||||
ASSERT_TRUE(contentCharset.EqualsLiteral(""));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -259,39 +259,6 @@ nsTArray<nsCString>& TErrorResult<CleanupPolicy>::CreateErrorMessageHelper(
|
|||
return message->mArgs;
|
||||
}
|
||||
|
||||
template <typename CleanupPolicy>
|
||||
void TErrorResult<CleanupPolicy>::SerializeMessage(
|
||||
IPC::MessageWriter* aWriter) const {
|
||||
using namespace IPC;
|
||||
AssertInOwningThread();
|
||||
MOZ_ASSERT(mUnionState == HasMessage);
|
||||
MOZ_ASSERT(mExtra.mMessage);
|
||||
WriteParam(aWriter, mExtra.mMessage->mArgs);
|
||||
WriteParam(aWriter, mExtra.mMessage->mErrorNumber);
|
||||
}
|
||||
|
||||
template <typename CleanupPolicy>
|
||||
bool TErrorResult<CleanupPolicy>::DeserializeMessage(
|
||||
IPC::MessageReader* aReader) {
|
||||
using namespace IPC;
|
||||
AssertInOwningThread();
|
||||
auto readMessage = MakeUnique<Message>();
|
||||
if (!ReadParam(aReader, &readMessage->mArgs) ||
|
||||
!ReadParam(aReader, &readMessage->mErrorNumber)) {
|
||||
return false;
|
||||
}
|
||||
if (!readMessage->HasCorrectNumberOfArguments()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mUnionState == HasNothing);
|
||||
InitMessage(readMessage.release());
|
||||
#ifdef DEBUG
|
||||
mUnionState = HasMessage;
|
||||
#endif // DEBUG
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename CleanupPolicy>
|
||||
void TErrorResult<CleanupPolicy>::SetPendingExceptionWithMessage(
|
||||
JSContext* aCx, const char* context) {
|
||||
|
|
@ -401,34 +368,106 @@ struct TErrorResult<CleanupPolicy>::DOMExceptionInfo {
|
|||
};
|
||||
|
||||
template <typename CleanupPolicy>
|
||||
void TErrorResult<CleanupPolicy>::SerializeDOMExceptionInfo(
|
||||
void TErrorResult<CleanupPolicy>::SerializeErrorResult(
|
||||
IPC::MessageWriter* aWriter) const {
|
||||
using namespace IPC;
|
||||
AssertInOwningThread();
|
||||
MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
|
||||
MOZ_ASSERT(mExtra.mDOMExceptionInfo);
|
||||
WriteParam(aWriter, mExtra.mDOMExceptionInfo->mMessage);
|
||||
WriteParam(aWriter, mExtra.mDOMExceptionInfo->mRv);
|
||||
|
||||
// It should be the case that mMightHaveUnreportedJSException can only be
|
||||
// true when we're expecting a JS exception. We cannot send such messages
|
||||
// over the IPC channel since there is no sane way of transferring the JS
|
||||
// value over to the other side. Callers should never do that.
|
||||
MOZ_ASSERT(!mMightHaveUnreportedJSException);
|
||||
if (IsJSException() || IsJSContextException()) {
|
||||
MOZ_CRASH(
|
||||
"Cannot serialize an ErrorResult representing a Javascript exception");
|
||||
}
|
||||
|
||||
WriteParam(aWriter, mResult);
|
||||
if (IsErrorWithMessage()) {
|
||||
MOZ_ASSERT(mResult == NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR ||
|
||||
mResult == NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR);
|
||||
MOZ_ASSERT(mUnionState == HasMessage);
|
||||
MOZ_ASSERT(mExtra.mMessage);
|
||||
|
||||
WriteParam(aWriter, mExtra.mMessage->mArgs);
|
||||
WriteParam(aWriter, mExtra.mMessage->mErrorNumber);
|
||||
} else if (IsDOMException()) {
|
||||
MOZ_ASSERT(mResult == NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION);
|
||||
MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
|
||||
MOZ_ASSERT(mExtra.mDOMExceptionInfo);
|
||||
|
||||
WriteParam(aWriter, mExtra.mDOMExceptionInfo->mMessage);
|
||||
WriteParam(aWriter, mExtra.mDOMExceptionInfo->mRv);
|
||||
} else {
|
||||
MOZ_ASSERT(mUnionState == HasNothing);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CleanupPolicy>
|
||||
bool TErrorResult<CleanupPolicy>::DeserializeDOMExceptionInfo(
|
||||
bool TErrorResult<CleanupPolicy>::DeserializeErrorResult(
|
||||
IPC::MessageReader* aReader) {
|
||||
using namespace IPC;
|
||||
AssertInOwningThread();
|
||||
nsCString message;
|
||||
nsresult rv;
|
||||
if (!ReadParam(aReader, &message) || !ReadParam(aReader, &rv)) {
|
||||
|
||||
nsresult result;
|
||||
if (!ReadParam(aReader, &result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mUnionState == HasNothing);
|
||||
MOZ_ASSERT(IsDOMException());
|
||||
InitDOMExceptionInfo(new DOMExceptionInfo(rv, message));
|
||||
switch (result) {
|
||||
case NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION:
|
||||
case NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT:
|
||||
// JS exceptions can not be serialized.
|
||||
return false;
|
||||
|
||||
case NS_ERROR_INTERNAL_ERRORRESULT_TYPEERROR:
|
||||
case NS_ERROR_INTERNAL_ERRORRESULT_RANGEERROR: {
|
||||
nsTArray<nsCString> args;
|
||||
dom::ErrNum errorNumber;
|
||||
if (!ReadParam(aReader, &args) || !ReadParam(aReader, &errorNumber)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetErrorArgCount(errorNumber) != args.Length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (nsCString& arg : args) {
|
||||
if (Utf8ValidUpTo(arg) != arg.Length()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ClearUnionData();
|
||||
|
||||
nsTArray<nsCString>& messageArgsArray =
|
||||
CreateErrorMessageHelper(errorNumber, result);
|
||||
messageArgsArray = std::move(args);
|
||||
MOZ_ASSERT(mExtra.mMessage->HasCorrectNumberOfArguments(),
|
||||
"validated earlier");
|
||||
#ifdef DEBUG
|
||||
mUnionState = HasDOMExceptionInfo;
|
||||
#endif // DEBUG
|
||||
return true;
|
||||
mUnionState = HasMessage;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
case NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION: {
|
||||
nsCString message;
|
||||
nsresult rv;
|
||||
if (!ReadParam(aReader, &message) || !ReadParam(aReader, &rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ThrowDOMException(rv, message);
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
ClearUnionData();
|
||||
AssignErrorCode(result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename CleanupPolicy>
|
||||
|
|
|
|||
|
|
@ -7,11 +7,8 @@
|
|||
#ifndef IPC_ErrorIPCUtils_h
|
||||
#define IPC_ErrorIPCUtils_h
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "ipc/EnumSerializer.h"
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
|
||||
namespace IPC {
|
||||
|
|
@ -24,64 +21,18 @@ struct ParamTraits<mozilla::dom::ErrNum>
|
|||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::ErrorResult> {
|
||||
typedef mozilla::ErrorResult paramType;
|
||||
|
||||
static void Write(MessageWriter* aWriter, const paramType& aParam) {
|
||||
// It should be the case that mMightHaveUnreportedJSException can only be
|
||||
// true when we're expecting a JS exception. We cannot send such messages
|
||||
// over the IPC channel since there is no sane way of transferring the JS
|
||||
// value over to the other side. Callers should never do that.
|
||||
MOZ_ASSERT_IF(aParam.IsJSException(),
|
||||
aParam.mMightHaveUnreportedJSException);
|
||||
if (aParam.IsJSException()
|
||||
#ifdef DEBUG
|
||||
|| aParam.mMightHaveUnreportedJSException
|
||||
#endif
|
||||
) {
|
||||
MOZ_CRASH(
|
||||
"Cannot encode an ErrorResult representing a Javascript exception");
|
||||
}
|
||||
|
||||
WriteParam(aWriter, aParam.mResult);
|
||||
WriteParam(aWriter, aParam.IsErrorWithMessage());
|
||||
WriteParam(aWriter, aParam.IsDOMException());
|
||||
if (aParam.IsErrorWithMessage()) {
|
||||
aParam.SerializeMessage(aWriter);
|
||||
} else if (aParam.IsDOMException()) {
|
||||
aParam.SerializeDOMExceptionInfo(aWriter);
|
||||
}
|
||||
static void Write(MessageWriter* aWriter,
|
||||
const mozilla::ErrorResult& aParam) {
|
||||
aParam.SerializeErrorResult(aWriter);
|
||||
}
|
||||
|
||||
static void Write(MessageWriter* aWriter, paramType&& aParam) {
|
||||
Write(aWriter, static_cast<const paramType&>(aParam));
|
||||
static void Write(MessageWriter* aWriter, mozilla::ErrorResult&& aParam) {
|
||||
aParam.SerializeErrorResult(aWriter);
|
||||
aParam.SuppressException();
|
||||
}
|
||||
|
||||
static bool Read(MessageReader* aReader, paramType* aResult) {
|
||||
paramType readValue;
|
||||
if (!ReadParam(aReader, &readValue.mResult)) {
|
||||
return false;
|
||||
}
|
||||
bool hasMessage = false;
|
||||
if (!ReadParam(aReader, &hasMessage)) {
|
||||
return false;
|
||||
}
|
||||
bool hasDOMExceptionInfo = false;
|
||||
if (!ReadParam(aReader, &hasDOMExceptionInfo)) {
|
||||
return false;
|
||||
}
|
||||
if (hasMessage && hasDOMExceptionInfo) {
|
||||
// Shouldn't have both!
|
||||
return false;
|
||||
}
|
||||
if (hasMessage && !readValue.DeserializeMessage(aReader)) {
|
||||
return false;
|
||||
} else if (hasDOMExceptionInfo &&
|
||||
!readValue.DeserializeDOMExceptionInfo(aReader)) {
|
||||
return false;
|
||||
}
|
||||
*aResult = std::move(readValue);
|
||||
return true;
|
||||
static bool Read(MessageReader* aReader, mozilla::ErrorResult* aResult) {
|
||||
return aResult->DeserializeErrorResult(aReader);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -90,14 +41,11 @@ struct ParamTraits<mozilla::CopyableErrorResult> {
|
|||
typedef mozilla::CopyableErrorResult paramType;
|
||||
|
||||
static void Write(MessageWriter* aWriter, const paramType& aParam) {
|
||||
ParamTraits<mozilla::ErrorResult>::Write(aWriter, aParam);
|
||||
aParam.SerializeErrorResult(aWriter);
|
||||
}
|
||||
|
||||
static bool Read(MessageReader* aReader, paramType* aResult) {
|
||||
// We can't cast *aResult to ErrorResult&, so cheat and just cast
|
||||
// to ErrorResult*.
|
||||
return ParamTraits<mozilla::ErrorResult>::Read(
|
||||
aReader, reinterpret_cast<mozilla::ErrorResult*>(aResult));
|
||||
return aResult->DeserializeErrorResult(aReader);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -401,7 +401,7 @@ class TErrorResult {
|
|||
|
||||
// Check whether the TErrorResult says to just throw whatever is on
|
||||
// the JSContext already.
|
||||
bool IsJSContextException() {
|
||||
bool IsJSContextException() const {
|
||||
return ErrorCode() == NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT;
|
||||
}
|
||||
|
||||
|
|
@ -481,11 +481,10 @@ class TErrorResult {
|
|||
|
||||
friend struct IPC::ParamTraits<TErrorResult>;
|
||||
friend struct IPC::ParamTraits<ErrorResult>;
|
||||
void SerializeMessage(IPC::MessageWriter* aWriter) const;
|
||||
bool DeserializeMessage(IPC::MessageReader* aReader);
|
||||
friend struct IPC::ParamTraits<CopyableErrorResult>;
|
||||
|
||||
void SerializeDOMExceptionInfo(IPC::MessageWriter* aWriter) const;
|
||||
bool DeserializeDOMExceptionInfo(IPC::MessageReader* aReader);
|
||||
void SerializeErrorResult(IPC::MessageWriter* aWriter) const;
|
||||
bool DeserializeErrorResult(IPC::MessageReader* aReader);
|
||||
|
||||
// Helper method that creates a new Message for this TErrorResult,
|
||||
// and returns the arguments array from that Message.
|
||||
|
|
|
|||
|
|
@ -277,6 +277,9 @@ bool ObservableArrayProxyHandler::GetBackingListObject(
|
|||
if (NS_WARN_IF(!newBackingListObj)) {
|
||||
return false;
|
||||
}
|
||||
if (NS_WARN_IF(!JS_SetPrototype(aCx, newBackingListObj, nullptr))) {
|
||||
return false;
|
||||
}
|
||||
slotValue = JS::ObjectValue(*newBackingListObj);
|
||||
js::SetProxyReservedSlot(aProxy, OBSERVABLE_ARRAY_BACKING_LIST_OBJECT_SLOT,
|
||||
slotValue);
|
||||
|
|
|
|||
|
|
@ -1110,10 +1110,6 @@ bool TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec,
|
|||
////
|
||||
|
||||
const auto surfSize = surf->GetSize();
|
||||
if (uint32_t(surfSize.width) < size.x || uint32_t(surfSize.height) < size.y) {
|
||||
gfxCriticalError() << "Source surface size too small for upload.";
|
||||
return false;
|
||||
}
|
||||
|
||||
WebGLTexelFormat srcFormat;
|
||||
uint8_t srcBPP;
|
||||
|
|
@ -1165,6 +1161,12 @@ bool TexUnpackSurface::TexOrSubImage(bool isSubImage, bool needsRespec,
|
|||
const auto& dstUnpacking = dstUnpackingRes.inspect();
|
||||
MOZ_ASSERT(dstUnpacking.metrics.bytesPerRowStride == dstStride);
|
||||
|
||||
if (uint32_t(surfSize.width) < dstUnpacking.metrics.usedPixelsPerRow ||
|
||||
uint32_t(surfSize.height) < dstUnpacking.metrics.totalRows) {
|
||||
gfxCriticalError() << "Source surface size too small for upload.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// -
|
||||
|
||||
const uint8_t* dstBegin = srcBegin;
|
||||
|
|
|
|||
|
|
@ -418,6 +418,11 @@ GlobalKeyListener::WalkHandlersResult GlobalKeyListener::WalkHandlersAndExecute(
|
|||
|
||||
bool GlobalKeyListener::IsReservedKey(WidgetKeyboardEvent* aKeyEvent,
|
||||
KeyEventHandler* aHandler) {
|
||||
// If the event is a reply event, it means that we've already sent the event
|
||||
// to the remote process because of not reserved.
|
||||
if (aKeyEvent->IsHandledInRemoteProcess()) {
|
||||
return false;
|
||||
}
|
||||
ReservedKey reserved = aHandler->GetIsReserved();
|
||||
// reserved="true" means that the key is always reserved. reserved="false"
|
||||
// means that the key is never reserved. Otherwise, we check site-specific
|
||||
|
|
|
|||
|
|
@ -18723,6 +18723,14 @@ mozilla::ipc::IPCResult NormalTransactionOp::RecvContinue(
|
|||
const PreprocessResponse& aResponse) {
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
// mWaitingForContinue is only touched on the owning thread. If it is not
|
||||
// set, either we never sent Preprocess (child is misbehaving) or the op is
|
||||
// still running on the connection thread. Calling NoteContinueReceived()
|
||||
// in either case would race Cleanup() with DoDatabaseWork().
|
||||
if (NS_WARN_IF(!IsWaitingForContinue())) {
|
||||
return IPC_FAIL(this, "Continue received when not waiting for continue");
|
||||
}
|
||||
|
||||
switch (aResponse.type()) {
|
||||
case PreprocessResponse::Tnsresult:
|
||||
SetFailureCode(aResponse.get_nsresult());
|
||||
|
|
|
|||
|
|
@ -280,10 +280,13 @@ IPCResult BrowserBridgeParent::RecvSetEmbedderAccessible(
|
|||
# if defined(ANDROID)
|
||||
MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor());
|
||||
# endif
|
||||
MOZ_ASSERT(aDoc || mEmbedderAccessibleDoc,
|
||||
"Embedder doc shouldn't be cleared if it wasn't set");
|
||||
MOZ_ASSERT(!mEmbedderAccessibleDoc || !aDoc || mEmbedderAccessibleDoc == aDoc,
|
||||
"Embedder doc shouldn't change from one doc to another");
|
||||
if (!aDoc && !mEmbedderAccessibleDoc) {
|
||||
return IPC_FAIL(this, "Embedder doc shouldn't be cleared if it wasn't set");
|
||||
}
|
||||
if (mEmbedderAccessibleDoc && aDoc && mEmbedderAccessibleDoc != aDoc) {
|
||||
return IPC_FAIL(this,
|
||||
"Embedder doc shouldn't change from one doc to another");
|
||||
}
|
||||
if (!aDoc && mEmbedderAccessibleDoc &&
|
||||
!mEmbedderAccessibleDoc->IsShutdown()) {
|
||||
// We're clearing the embedder doc, so remove the pending child doc addition
|
||||
|
|
@ -293,14 +296,22 @@ IPCResult BrowserBridgeParent::RecvSetEmbedderAccessible(
|
|||
mEmbedderAccessibleDoc = static_cast<a11y::DocAccessibleParent*>(aDoc);
|
||||
mEmbedderAccessibleID = aID;
|
||||
if (!aDoc) {
|
||||
MOZ_ASSERT(!aID);
|
||||
if (aID) {
|
||||
return IPC_FAIL(this, "Attempt to clear embedder but id given");
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
MOZ_ASSERT(aID);
|
||||
if (!aID) {
|
||||
return IPC_FAIL(this, "Attempt to set embedder without id");
|
||||
}
|
||||
if (GetDocAccessibleParent()) {
|
||||
// The embedded DocAccessibleParent has already been created. This can
|
||||
// happen if, for example, an iframe is hidden and then shown or
|
||||
// an iframe is reflowed by layout.
|
||||
// happen if, for example, an iframe is hidden and then shown or an iframe
|
||||
// Accessible is re-created. In the case of re-creation, the old iframe
|
||||
// Accessible still exists at this point because this IPDL message is
|
||||
// received *before* we receive the accessibility hide and show events. This
|
||||
// is okay; DocAccessibleParent will store this as a pending OOP child
|
||||
// document and add it when the new OuterDocAccessible arrives.
|
||||
mEmbedderAccessibleDoc->AddChildDoc(this);
|
||||
}
|
||||
return IPC_OK();
|
||||
|
|
|
|||
|
|
@ -2440,6 +2440,10 @@ mozilla::ipc::IPCResult BrowserChild::RecvRealKeyEvent(
|
|||
// we need to clear the flag explicitly here because ParamTraits should
|
||||
// keep checking the flag for avoiding regression.
|
||||
localEvent.mFlags.mNoRemoteProcessDispatch = false;
|
||||
// The parent process won't use the native key bindings of the reply event
|
||||
// anymore. To save the IPC cost, let's clear the edit commands before sending
|
||||
// the event back to the parent process.
|
||||
localEvent.PreventNativeKeyBindings();
|
||||
SendReplyKeyEvent(localEvent, aUUID);
|
||||
|
||||
return IPC_OK();
|
||||
|
|
|
|||
|
|
@ -1291,6 +1291,14 @@ mozilla::ipc::IPCResult BrowserParent::RecvPDocAccessibleConstructor(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
if (auto* prevTopLevel = GetTopLevelDocAccessible()) {
|
||||
// Sometimes, we can get a new top level DocAccessibleParent before the
|
||||
// old one gets destroyed. The old one will die pretty shortly anyway,
|
||||
// so just destroy it now. If we don't do this, GetTopLevelDocAccessible()
|
||||
// might return the wrong document for a short while.
|
||||
prevTopLevel->Destroy();
|
||||
}
|
||||
|
||||
if (aBrowsingContext) {
|
||||
doc->SetBrowsingContext(aBrowsingContext.get_canonical());
|
||||
}
|
||||
|
|
@ -1325,13 +1333,6 @@ mozilla::ipc::IPCResult BrowserParent::RecvPDocAccessibleConstructor(
|
|||
return IPC_FAIL_NO_REASON(this);
|
||||
}
|
||||
|
||||
if (auto* prevTopLevel = GetTopLevelDocAccessible()) {
|
||||
// Sometimes, we can get a new top level DocAccessibleParent before the
|
||||
// old one gets destroyed. The old one will die pretty shortly anyway,
|
||||
// so just destroy it now. If we don't do this, GetTopLevelDocAccessible()
|
||||
// might return the wrong document for a short while.
|
||||
prevTopLevel->Destroy();
|
||||
}
|
||||
doc->SetTopLevel();
|
||||
a11y::DocManager::RemoteDocAdded(doc);
|
||||
# ifdef XP_WIN
|
||||
|
|
@ -2782,7 +2783,10 @@ mozilla::ipc::IPCResult BrowserParent::RecvReplyKeyEvent(
|
|||
NS_WARN_IF(data.mPseudoCharCode != aEvent.mPseudoCharCode) ||
|
||||
NS_WARN_IF(data.mKeyNameIndex != aEvent.mKeyNameIndex) ||
|
||||
NS_WARN_IF(data.mCodeNameIndex != aEvent.mCodeNameIndex) ||
|
||||
NS_WARN_IF(data.mModifiers != aEvent.mModifiers)) {
|
||||
NS_WARN_IF(data.mModifiers != aEvent.mModifiers) ||
|
||||
// The child process should've already cleared the editor commands
|
||||
// because we don't use them.
|
||||
NS_WARN_IF(aEvent.HasEditCommands())) {
|
||||
// Got different event data from what we stored before dispatching an
|
||||
// event with the ID.
|
||||
return Nothing();
|
||||
|
|
@ -3909,13 +3913,18 @@ mozilla::ipc::IPCResult BrowserParent::RecvInvokeDragSession(
|
|||
cookieJarSettings, aSourceWindowContext.GetMaybeDiscarded(),
|
||||
aSourceTopWindowContext.GetMaybeDiscarded());
|
||||
|
||||
if (aVisualDnDData) {
|
||||
const auto checkedSize = CheckedInt<size_t>(aDragRect.height) * aStride;
|
||||
if (checkedSize.isValid() &&
|
||||
aVisualDnDData->Size() >= checkedSize.value()) {
|
||||
if (aVisualDnDData && aDragRect.width >= 0 && aDragRect.height >= 0) {
|
||||
const auto checkedSize = CheckedInt<int32_t>(aDragRect.height) * aStride;
|
||||
const auto computedStride =
|
||||
CheckedInt<int32_t>(aDragRect.width) * gfx::BytesPerPixel(aFormat);
|
||||
const auto checkedStride = CheckedInt<int32_t>(aStride);
|
||||
if (checkedSize.isValid() && checkedSize.value() >= 0 &&
|
||||
aVisualDnDData->Size() >= static_cast<size_t>(checkedSize.value()) &&
|
||||
computedStride.isValid() && checkedStride.isValid() &&
|
||||
computedStride.value() <= checkedStride.value()) {
|
||||
dragStartData->SetVisualization(gfx::CreateDataSourceSurfaceFromData(
|
||||
gfx::IntSize(aDragRect.width, aDragRect.height), aFormat,
|
||||
aVisualDnDData->Data(), aStride));
|
||||
aVisualDnDData->Data(), checkedStride.value()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5130,8 +5130,18 @@ ContentParent::AllocPContentPermissionRequestParent(
|
|||
topPrincipal = principal;
|
||||
}
|
||||
return nsContentPermissionUtils::CreateContentPermissionRequestParent(
|
||||
aRequests, tp->GetOwnerElement(), aPrincipal, topPrincipal,
|
||||
aIsHandlingUserInput, aMaybeUnsafePermissionDelegate, aTabId);
|
||||
tp->GetOwnerElement(), aPrincipal, topPrincipal, aIsHandlingUserInput,
|
||||
aMaybeUnsafePermissionDelegate, aTabId);
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult ContentParent::RecvPContentPermissionRequestConstructor(
|
||||
PContentPermissionRequestParent* aActor,
|
||||
nsTArray<PermissionRequest>&& aRequests, nsIPrincipal* aPrincipal,
|
||||
nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput,
|
||||
const bool& aMaybeUnsafePermissionDelegate, const TabId& tabId) {
|
||||
nsContentPermissionUtils::InitContentPermissionRequestParent(
|
||||
aActor, std::move(aRequests));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
bool ContentParent::DeallocPContentPermissionRequestParent(
|
||||
|
|
|
|||
|
|
@ -538,6 +538,12 @@ class ContentParent final : public PContentParent,
|
|||
nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput,
|
||||
const bool& aMaybeUnsafePermissionDelegate, const TabId& aTabId);
|
||||
|
||||
mozilla::ipc::IPCResult RecvPContentPermissionRequestConstructor(
|
||||
PContentPermissionRequestParent* aActor,
|
||||
nsTArray<PermissionRequest>&& aRequests, nsIPrincipal* aPrincipal,
|
||||
nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput,
|
||||
const bool& aMaybeUnsafePermissionDelegate, const TabId& tabId) override;
|
||||
|
||||
bool DeallocPContentPermissionRequestParent(
|
||||
PContentPermissionRequestParent* actor);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ protocol PContentPermissionRequest
|
|||
manager PContent;
|
||||
|
||||
parent:
|
||||
async prompt();
|
||||
async Destroy();
|
||||
|
||||
child:
|
||||
|
|
|
|||
|
|
@ -6595,6 +6595,12 @@ mozilla::ipc::IPCResult LSRequestBase::RecvCancel() {
|
|||
mozilla::ipc::IPCResult LSRequestBase::RecvFinish() {
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
// A well-behaved content process only sends Finish() after receiving Ready(),
|
||||
// which transitions us to WaitingForFinish.
|
||||
if (NS_WARN_IF(mState != State::WaitingForFinish)) {
|
||||
return IPC_FAIL(this, "Finish received in unexpected state");
|
||||
}
|
||||
|
||||
Finish();
|
||||
|
||||
return IPC_OK();
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "MediaInfo.h"
|
||||
#include "MediaResult.h"
|
||||
#include "PerformanceRecorder.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "YCbCrUtils.h"
|
||||
#include "mozilla/gfx/gfxVars.h"
|
||||
|
|
@ -28,8 +29,12 @@
|
|||
# include "mozilla/gfx/gfxVars.h"
|
||||
#endif
|
||||
|
||||
#define LOG(level, msg, ...) \
|
||||
MOZ_LOG_FMT(sPDMLog, level, "%s: " msg, __func__, ##__VA_ARGS__)
|
||||
namespace mozilla {
|
||||
|
||||
extern LazyLogModule sPDMLog;
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
using layers::PlanarYCbCrData;
|
||||
using layers::PlanarYCbCrImage;
|
||||
|
|
@ -186,6 +191,19 @@ static bool ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane) {
|
|||
|
||||
static MediaResult ValidateBufferAndPicture(
|
||||
const VideoData::YCbCrBuffer& aBuffer, const IntRect& aPicture) {
|
||||
// mChromaSubsampling describes the relationship between plane sizes.
|
||||
if (aBuffer.mChromaSubsampling == ChromaSubsampling::FULL) {
|
||||
MOZ_ASSERT(aBuffer.mPlanes[1].mWidth == aBuffer.mPlanes[0].mWidth);
|
||||
} else {
|
||||
MOZ_ASSERT(aBuffer.mPlanes[1].mWidth ==
|
||||
(aBuffer.mPlanes[0].mWidth + 1) / 2);
|
||||
}
|
||||
if (aBuffer.mChromaSubsampling == ChromaSubsampling::HALF_WIDTH_AND_HEIGHT) {
|
||||
MOZ_ASSERT(aBuffer.mPlanes[1].mHeight ==
|
||||
(aBuffer.mPlanes[0].mHeight + 1) / 2);
|
||||
} else {
|
||||
MOZ_ASSERT(aBuffer.mPlanes[1].mHeight == aBuffer.mPlanes[0].mHeight);
|
||||
}
|
||||
// The following situation should never happen unless there is a bug
|
||||
// in the decoder
|
||||
if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth ||
|
||||
|
|
@ -193,7 +211,6 @@ static MediaResult ValidateBufferAndPicture(
|
|||
return MediaResult(NS_ERROR_INVALID_ARG,
|
||||
"Chroma planes with different sizes");
|
||||
}
|
||||
|
||||
// The following situations could be triggered by invalid input
|
||||
if (aPicture.width <= 0 || aPicture.height <= 0) {
|
||||
return MediaResult(NS_ERROR_INVALID_ARG, "Empty picture rect");
|
||||
|
|
@ -203,7 +220,12 @@ static MediaResult ValidateBufferAndPicture(
|
|||
!ValidatePlane(aBuffer.mPlanes[2])) {
|
||||
return MediaResult(NS_ERROR_INVALID_ARG, "Invalid plane size");
|
||||
}
|
||||
|
||||
// ConstructPlanarYCbCrData() and ConvertI420AlphaToARGB() assume Chroma
|
||||
// planes have equal strides.
|
||||
if (aBuffer.mPlanes[1].mStride != aBuffer.mPlanes[2].mStride) {
|
||||
return MediaResult(NS_ERROR_INVALID_ARG,
|
||||
"Chroma planes with different strides");
|
||||
}
|
||||
// Ensure the picture size specified in the headers can be extracted out of
|
||||
// the frame we've been supplied without indexing out of bounds.
|
||||
CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
|
||||
|
|
@ -291,6 +313,7 @@ PlanarYCbCrData ConstructPlanarYCbCrData(const VideoInfo& aInfo,
|
|||
data.mYSkip = AssertedCast<int32_t>(Y.mSkip);
|
||||
data.mCbChannel = Cb.mData;
|
||||
data.mCrChannel = Cr.mData;
|
||||
MOZ_ASSERT(Cb.mStride == Cr.mStride);
|
||||
data.mCbCrStride = AssertedCast<int32_t>(Cb.mStride);
|
||||
data.mCbSkip = AssertedCast<int32_t>(Cb.mSkip);
|
||||
data.mCrSkip = AssertedCast<int32_t>(Cr.mSkip);
|
||||
|
|
@ -409,6 +432,22 @@ already_AddRefed<VideoData> VideoData::CreateAndCopyData(
|
|||
NS_ERROR(r.Message().get());
|
||||
return nullptr;
|
||||
}
|
||||
if (!ValidatePlane(aAlphaPlane)) {
|
||||
MOZ_LOG_FMT(sPDMLog, LogLevel::Warning, "Invalid alpha plane");
|
||||
return nullptr;
|
||||
}
|
||||
// The alpha plane is expected to be the same size as the luma plane.
|
||||
// See Method 1 at https://wiki.webmproject.org/alpha-channel
|
||||
if (aBuffer.mPlanes[0].mWidth != aAlphaPlane.mWidth ||
|
||||
aBuffer.mPlanes[0].mHeight != aAlphaPlane.mHeight) {
|
||||
MOZ_LOG_FMT(sPDMLog, LogLevel::Warning, "luma and alpha sizes differ");
|
||||
return nullptr;
|
||||
}
|
||||
// ConvertI420AlphaToARGB() expects equal strides for luma and alpha
|
||||
if (aBuffer.mPlanes[0].mStride != aAlphaPlane.mStride) {
|
||||
MOZ_LOG_FMT(sPDMLog, LogLevel::Warning, "luma and alpha strides differ");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<VideoData> v(new VideoData(aOffset, aTime, aDuration, aKeyframe,
|
||||
aTimecode, aInfo.mDisplay, 0));
|
||||
|
|
@ -635,3 +674,4 @@ CryptoScheme StringToCryptoScheme(const nsAString& aString) {
|
|||
}
|
||||
|
||||
} // namespace mozilla
|
||||
#undef LOG
|
||||
|
|
|
|||
|
|
@ -129,6 +129,10 @@ class FakeVideoEncoder : public GMPVideoEncoder {
|
|||
GMPVideoEncoderCallback* callback, int32_t numberOfCores,
|
||||
uint32_t maxPayloadSize) override {
|
||||
callback_ = callback;
|
||||
constexpr uint16_t kFrameDropCadence = 5;
|
||||
frame_drop_cadence_ =
|
||||
codecSettings.mFrameDroppingOn ? kFrameDropCadence : 0;
|
||||
num_frames_since_drop_ = 0;
|
||||
frame_size_ = (maxPayloadSize > 0 && maxPayloadSize < BIG_FRAME)
|
||||
? maxPayloadSize
|
||||
: BIG_FRAME;
|
||||
|
|
@ -146,6 +150,18 @@ class FakeVideoEncoder : public GMPVideoEncoder {
|
|||
|
||||
void SendFrame(GMPVideoi420Frame* inputImage, GMPVideoFrameType frame_type,
|
||||
int nal_type) {
|
||||
if (frame_drop_cadence_ > 0) {
|
||||
const auto frame_drop_id = ++num_frames_since_drop_;
|
||||
num_frames_since_drop_ %= frame_drop_cadence_;
|
||||
GMPLOG(GL_DEBUG,
|
||||
"Frame dropping is on, id="
|
||||
<< frame_drop_id << "/" << frame_drop_cadence_ << ". "
|
||||
<< (frame_drop_id == frame_drop_cadence_ ? "Dropping"
|
||||
: "Keeping"));
|
||||
if (frame_drop_id == frame_drop_cadence_) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Encode this in a frame that looks a little bit like H.264.
|
||||
// Send SPS/PPS/IDR to avoid confusing people
|
||||
// Copy the data. This really should convert this to network byte order.
|
||||
|
|
@ -201,7 +217,7 @@ class FakeVideoEncoder : public GMPVideoEncoder {
|
|||
f->SetCompleteFrame(true);
|
||||
f->SetBufferType(GMP_BufferLength32);
|
||||
|
||||
GMPLOG(GL_DEBUG, "Encoding complete. type= "
|
||||
GMPLOG(GL_DEBUG, "Encoding complete. type="
|
||||
<< f->FrameType()
|
||||
<< " NAL_type=" << (int)eframe.idr_nalu.h264_compat_
|
||||
<< " length=" << f->Size()
|
||||
|
|
@ -271,6 +287,8 @@ class FakeVideoEncoder : public GMPVideoEncoder {
|
|||
|
||||
GMPVideoHost* host_;
|
||||
GMPVideoEncoderCallback* callback_ = nullptr;
|
||||
uint16_t frame_drop_cadence_ = 0;
|
||||
uint16_t num_frames_since_drop_ = 0;
|
||||
uint32_t frame_size_ = BIG_FRAME;
|
||||
uint32_t frames_encoded_ = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@
|
|||
#include <stdio.h>
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsExceptionHandler.h"
|
||||
#include "GMPLog.h"
|
||||
#include "gmp-entrypoints.h"
|
||||
#include "prlink.h"
|
||||
#include "prenv.h"
|
||||
#include "prerror.h"
|
||||
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
|
||||
# include "mozilla/sandboxTarget.h"
|
||||
# include "mozilla/sandboxing/SandboxInitialization.h"
|
||||
# include "mozilla/sandboxing/sandboxLogging.h"
|
||||
# include "nsWindowsHelpers.h"
|
||||
#endif
|
||||
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
|
||||
# include "mozilla/Sandbox.h"
|
||||
|
|
@ -83,18 +83,107 @@ class PassThroughGMPAdapter : public GMPAdapter {
|
|||
PRLibrary* mLib = nullptr;
|
||||
};
|
||||
|
||||
#if defined(XP_WIN) && defined(MOZ_SANDBOX)
|
||||
// This performs the same checks for an AppLocker policy that are performed in
|
||||
// SaferpIsV2PolicyPresent from ntdll.dll, they are used to decide whether an
|
||||
// AppLocker ioctl call is made.
|
||||
static bool IsAppLockerPolicyPresent() {
|
||||
// RuleCount check for policy configured via Local Security Policy.
|
||||
DWORD ruleCount = 0;
|
||||
DWORD ruleCountSize = sizeof(ruleCount);
|
||||
if (RegGetValueW(HKEY_LOCAL_MACHINE,
|
||||
LR"(SYSTEM\CurrentControlSet\Control\Srp\GP)", L"RuleCount",
|
||||
RRF_RT_REG_DWORD, nullptr, &ruleCount,
|
||||
&ruleCountSize) == ERROR_SUCCESS &&
|
||||
ruleCount != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Directory check for policy configured via Mobile Device Management.
|
||||
static constexpr wchar_t appLockerMDMPath[] = LR"(\System32\AppLocker\MDM)";
|
||||
wchar_t path[MAX_PATH + sizeof(appLockerMDMPath) / sizeof(wchar_t)];
|
||||
UINT len = GetSystemWindowsDirectoryW(path, MAX_PATH);
|
||||
if (len != 0 && len < MAX_PATH) {
|
||||
wcscpy(path + len, appLockerMDMPath);
|
||||
return GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void EnsureAppLockerCacheIsWarm(const wchar_t* aWidePath) {
|
||||
// IOCTL to \Device\SrpDevice (\\.\SrpDevice via DosDevices) that triggers
|
||||
// AppLocker to cache the allow/deny decision for the DLL, warming the NTFS
|
||||
// EA cache before the sandbox starts.
|
||||
static constexpr DWORD IOCTL_SRP_VERIFY_DLL = 0x225804;
|
||||
static constexpr wchar_t kSrpDevicePath[] = LR"(\\.\SrpDevice)";
|
||||
|
||||
// Buffer layout: [HANDLE as 8 bytes][USHORT pathBytes][WCHAR path...]
|
||||
// The handle field is always 8 bytes. On x86 the handle is zero-extended.
|
||||
struct SrpIoctlBuffer {
|
||||
uint64_t handle;
|
||||
USHORT pathBytes;
|
||||
WCHAR path[1];
|
||||
};
|
||||
static constexpr DWORD kSrpHeaderSize = offsetof(SrpIoctlBuffer, path);
|
||||
|
||||
UniquePtr<HANDLE, CloseHandleDeleter> fileHandle(CreateFileW(
|
||||
aWidePath, FILE_READ_DATA | FILE_EXECUTE | SYNCHRONIZE,
|
||||
FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, 0, nullptr));
|
||||
if (fileHandle.get() == INVALID_HANDLE_VALUE) {
|
||||
GMP_LOG_WARNING("EnsureAppLockerCacheIsWarm: CreateFileW failed (%lu)",
|
||||
GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
NtPathFromDosPath ntPath(aWidePath);
|
||||
if (!ntPath.IsValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD ioctlSize = kSrpHeaderSize + ntPath.LengthInBytes();
|
||||
auto buf = MakeUnique<uint8_t[]>(ioctlSize);
|
||||
auto* srp = reinterpret_cast<SrpIoctlBuffer*>(buf.get());
|
||||
|
||||
// ULONG_PTR is pointer-sized (4 bytes on x86, 8 on x64). Casting to uint64_t
|
||||
// zero-extends on x86, matching the cdq zero-extension in x86 ntdll.
|
||||
srp->handle =
|
||||
static_cast<uint64_t>(reinterpret_cast<ULONG_PTR>(fileHandle.get()));
|
||||
srp->pathBytes = ntPath.LengthInBytes();
|
||||
if (!ntPath.CopyTo(
|
||||
mozilla::Span(srp->path, ntPath.LengthInBytes() / sizeof(WCHAR)))) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(false, "CopyTo failed: buffer too small");
|
||||
return;
|
||||
}
|
||||
|
||||
UniquePtr<HANDLE, CloseHandleDeleter> srpDevice(
|
||||
CreateFileW(kSrpDevicePath, FILE_READ_DATA,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
nullptr, OPEN_EXISTING, 0, nullptr));
|
||||
if (srpDevice.get() == INVALID_HANDLE_VALUE) {
|
||||
GMP_LOG_WARNING(
|
||||
"EnsureAppLockerCacheIsWarm: opening SrpDevice failed (%lu)",
|
||||
GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD outBuf = 0;
|
||||
DWORD bytesReturned = 0;
|
||||
if (!DeviceIoControl(srpDevice.get(), IOCTL_SRP_VERIFY_DLL, srp, ioctlSize,
|
||||
&outBuf, sizeof(outBuf), &bytesReturned, nullptr)) {
|
||||
GMP_LOG_DEBUG(
|
||||
"EnsureAppLockerCacheIsWarm: DeviceIoControl failed (%lu), "
|
||||
"AppLocker may not be enabled",
|
||||
GetLastError());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool GMPLoader::Load(const char* aUTF8LibPath, uint32_t aUTF8LibPathLen,
|
||||
const GMPPlatformAPI* aPlatformAPI, GMPAdapter* aAdapter) {
|
||||
CrashReporter::AutoRecordAnnotation autoLibPath(
|
||||
CrashReporter::Annotation::GMPLibraryPath,
|
||||
nsDependentCString(aUTF8LibPath));
|
||||
|
||||
if (!getenv("MOZ_DISABLE_GMP_SANDBOX") && mSandboxStarter &&
|
||||
!mSandboxStarter->Start(aUTF8LibPath)) {
|
||||
MOZ_CRASH("Cannot start sandbox!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the GMP.
|
||||
PRLibSpec libSpec;
|
||||
#ifdef XP_WIN
|
||||
|
|
@ -111,6 +200,20 @@ bool GMPLoader::Load(const char* aUTF8LibPath, uint32_t aUTF8LibPathLen,
|
|||
return false;
|
||||
}
|
||||
|
||||
# if defined(MOZ_SANDBOX)
|
||||
if (IsAppLockerPolicyPresent()) {
|
||||
EnsureAppLockerCacheIsWarm(widePath.get());
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
if (!getenv("MOZ_DISABLE_GMP_SANDBOX") && mSandboxStarter &&
|
||||
!mSandboxStarter->Start(aUTF8LibPath)) {
|
||||
MOZ_CRASH("Cannot start sandbox!");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
libSpec.value.pathname_u = widePath.get();
|
||||
libSpec.type = PR_LibSpec_PathnameU;
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
namespace mozilla::gmp {
|
||||
|
||||
class GMPVideoi420FrameImpl;
|
||||
|
||||
enum class GMPSharedMemClass { Decoded, Encoded };
|
||||
|
||||
class GMPSharedMemManager {
|
||||
|
|
@ -27,6 +29,8 @@ class GMPSharedMemManager {
|
|||
virtual bool MgrAllocShmem(size_t aSize, ipc::Shmem* aMem) { return false; }
|
||||
virtual void MgrDeallocShmem(ipc::Shmem& aMem) = 0;
|
||||
|
||||
virtual void MgrDecodedFrameDestroyed(GMPVideoi420FrameImpl* aFrame) {}
|
||||
|
||||
protected:
|
||||
virtual bool MgrIsOnOwningThread() const = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#include "mozilla/Unused.h"
|
||||
#include "GMPPlatform.h"
|
||||
#include "GMPVideoEncodedFrameImpl.h"
|
||||
#include "GMPVideoi420FrameImpl.h"
|
||||
#include "runnable_utils.h"
|
||||
|
||||
namespace mozilla::gmp {
|
||||
|
|
@ -67,9 +66,26 @@ void GMPVideoEncoderChild::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
|
|||
MOZ_CRASH("Encoded without any frame data!");
|
||||
}
|
||||
|
||||
mLatestEncodedTimestamp = frameData.mTimestamp();
|
||||
|
||||
aEncodedFrame->Destroy();
|
||||
}
|
||||
|
||||
void GMPVideoEncoderChild::MgrDecodedFrameDestroyed(
|
||||
GMPVideoi420FrameImpl* aFrame) {
|
||||
if (NS_WARN_IF(!mPlugin)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The OpenH264 encoder destroys the input frame if it has skipped encoding
|
||||
// it. When it has encoded it, it calls the Encoded() callback before
|
||||
// destroying the frame.
|
||||
MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
|
||||
if (aFrame->Timestamp() > mLatestEncodedTimestamp) {
|
||||
(void)SendDroppedFrame(aFrame->Timestamp());
|
||||
}
|
||||
}
|
||||
|
||||
void GMPVideoEncoderChild::Error(GMPErr aError) {
|
||||
if (NS_WARN_IF(!mPlugin)) {
|
||||
return;
|
||||
|
|
@ -116,8 +132,10 @@ mozilla::ipc::IPCResult GMPVideoEncoderChild::RecvEncode(
|
|||
return IPC_FAIL(this, "!mVideoDecoder");
|
||||
}
|
||||
|
||||
// The `this` destroyed callback outlives the frame, because `mVideoEncoder`
|
||||
// is responsible for destroying the frame, and we outlive `mVideoEncoder`.
|
||||
auto* f = new GMPVideoi420FrameImpl(aInputFrame, std::move(aInputShmem),
|
||||
&mVideoHost);
|
||||
&mVideoHost, HostReportPolicy::Destroyed);
|
||||
|
||||
// Ignore any return code. It is OK for this to fail without killing the
|
||||
// process.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/gmp/PGMPVideoEncoderChild.h"
|
||||
#include "gmp-video-encode.h"
|
||||
#include "GMPSharedMemManager.h"
|
||||
#include "GMPVideoi420FrameImpl.h"
|
||||
#include "GMPVideoHost.h"
|
||||
|
||||
namespace mozilla::gmp {
|
||||
|
|
@ -39,6 +40,7 @@ class GMPVideoEncoderChild final : public PGMPVideoEncoderChild,
|
|||
|
||||
// GMPSharedMemManager
|
||||
void MgrDeallocShmem(Shmem& aMem) override { DeallocShmem(aMem); }
|
||||
void MgrDecodedFrameDestroyed(GMPVideoi420FrameImpl* aFrame) override;
|
||||
|
||||
protected:
|
||||
bool MgrIsOnOwningThread() const override;
|
||||
|
|
@ -66,6 +68,7 @@ class GMPVideoEncoderChild final : public PGMPVideoEncoderChild,
|
|||
GMPContentChild* mPlugin;
|
||||
GMPVideoEncoder* mVideoEncoder;
|
||||
GMPVideoHostImpl mVideoHost;
|
||||
uint64_t mLatestEncodedTimestamp = 0;
|
||||
};
|
||||
|
||||
} // namespace mozilla::gmp
|
||||
|
|
|
|||
|
|
@ -271,6 +271,14 @@ mozilla::ipc::IPCResult GMPVideoEncoderParent::RecvEncodedData(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult GMPVideoEncoderParent::RecvDroppedFrame(
|
||||
const uint64_t& aTimestamp) {
|
||||
if (mCallback) {
|
||||
mCallback->Dropped(aTimestamp);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult GMPVideoEncoderParent::RecvError(const GMPErr& aError) {
|
||||
if (mCallback) {
|
||||
mCallback->Error(aError);
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ class GMPVideoEncoderParent final : public GMPVideoEncoderProxy,
|
|||
const GMPVideoEncodedFrameData& aEncodedFrame,
|
||||
nsTArray<uint8_t>&& aEncodedData,
|
||||
nsTArray<uint8_t>&& aCodecSpecificInfo) override;
|
||||
mozilla::ipc::IPCResult RecvDroppedFrame(const uint64_t& aTimestamp) override;
|
||||
mozilla::ipc::IPCResult RecvError(const GMPErr& aError) override;
|
||||
mozilla::ipc::IPCResult RecvShutdown() override;
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ class GMPVideoEncoderCallbackProxy : public GMPCallbackBase {
|
|||
virtual ~GMPVideoEncoderCallbackProxy() = default;
|
||||
virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
|
||||
const nsTArray<uint8_t>& aCodecSpecificInfo) = 0;
|
||||
virtual void Dropped(uint64_t aTimestamp) = 0;
|
||||
virtual void Error(GMPErr aError) = 0;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -93,6 +93,9 @@ void GMPVideoHostImpl::DecodedFrameCreated(
|
|||
|
||||
void GMPVideoHostImpl::DecodedFrameDestroyed(GMPVideoi420FrameImpl* aFrame) {
|
||||
MOZ_ALWAYS_TRUE(mDecodedFrames.RemoveElement(aFrame));
|
||||
if (mSharedMemMgr && aFrame->mReportPolicy == HostReportPolicy::Destroyed) {
|
||||
mSharedMemMgr->MgrDecodedFrameDestroyed(aFrame);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla::gmp
|
||||
|
|
|
|||
|
|
@ -38,16 +38,25 @@ void GMPVideoi420FrameImpl::GMPFramePlane::Copy(uint8_t* aDst,
|
|||
}
|
||||
}
|
||||
|
||||
GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(GMPVideoHostImpl* aHost)
|
||||
: mHost(aHost), mWidth(0), mHeight(0), mTimestamp(0ll), mDuration(0ll) {
|
||||
GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(
|
||||
GMPVideoHostImpl* aHost,
|
||||
HostReportPolicy aReportPolicy /*= HostReportPolicy::None*/)
|
||||
: mReportPolicy(aReportPolicy),
|
||||
mHost(aHost),
|
||||
mWidth(0),
|
||||
mHeight(0),
|
||||
mTimestamp(0ll),
|
||||
mDuration(0ll) {
|
||||
MOZ_ASSERT(aHost);
|
||||
aHost->DecodedFrameCreated(this);
|
||||
}
|
||||
|
||||
GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(
|
||||
const GMPVideoi420FrameData& aFrameData, ipc::Shmem&& aShmemBuffer,
|
||||
GMPVideoHostImpl* aHost)
|
||||
: mHost(aHost),
|
||||
GMPVideoHostImpl* aHost,
|
||||
HostReportPolicy aReportPolicy /*= HostReportPolicy::None*/)
|
||||
: mReportPolicy(aReportPolicy),
|
||||
mHost(aHost),
|
||||
mShmemBuffer(std::move(aShmemBuffer)),
|
||||
mYPlane(aFrameData.mYPlane()),
|
||||
mUPlane(aFrameData.mUPlane()),
|
||||
|
|
@ -63,8 +72,10 @@ GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(
|
|||
|
||||
GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(
|
||||
const GMPVideoi420FrameData& aFrameData, nsTArray<uint8_t>&& aArrayBuffer,
|
||||
GMPVideoHostImpl* aHost)
|
||||
: mHost(aHost),
|
||||
GMPVideoHostImpl* aHost,
|
||||
HostReportPolicy aReportPolicy /*= HostReportPolicy::None*/)
|
||||
: mReportPolicy(aReportPolicy),
|
||||
mHost(aHost),
|
||||
mArrayBuffer(std::move(aArrayBuffer)),
|
||||
mYPlane(aFrameData.mYPlane()),
|
||||
mUPlane(aFrameData.mUPlane()),
|
||||
|
|
@ -146,30 +157,76 @@ bool GMPVideoi420FrameImpl::CheckFrameData(
|
|||
// if so. Note: Size() greater than expected is also an error, but with no
|
||||
// negative consequences
|
||||
int32_t half_width = (aFrameData.mWidth() + 1) / 2;
|
||||
if ((aFrameData.mYPlane().mStride() <= 0) ||
|
||||
(aFrameData.mYPlane().mSize() <= 0) ||
|
||||
(aFrameData.mYPlane().mOffset() < 0) ||
|
||||
(aFrameData.mUPlane().mStride() <= 0) ||
|
||||
(aFrameData.mUPlane().mSize() <= 0) ||
|
||||
(aFrameData.mUPlane().mOffset() <
|
||||
aFrameData.mYPlane().mOffset() + aFrameData.mYPlane().mSize()) ||
|
||||
(aFrameData.mVPlane().mStride() <= 0) ||
|
||||
(aFrameData.mVPlane().mSize() <= 0) ||
|
||||
(aFrameData.mVPlane().mOffset() <
|
||||
aFrameData.mUPlane().mOffset() + aFrameData.mUPlane().mSize()) ||
|
||||
(aBufferSize < static_cast<size_t>(aFrameData.mVPlane().mOffset()) +
|
||||
static_cast<size_t>(aFrameData.mVPlane().mSize())) ||
|
||||
(aFrameData.mYPlane().mStride() < aFrameData.mWidth()) ||
|
||||
(aFrameData.mUPlane().mStride() < half_width) ||
|
||||
(aFrameData.mVPlane().mStride() < half_width) ||
|
||||
(aFrameData.mYPlane().mSize() <
|
||||
aFrameData.mYPlane().mStride() * aFrameData.mHeight()) ||
|
||||
(aFrameData.mUPlane().mSize() <
|
||||
aFrameData.mUPlane().mStride() * ((aFrameData.mHeight() + 1) / 2)) ||
|
||||
(aFrameData.mVPlane().mSize() <
|
||||
aFrameData.mVPlane().mStride() * ((aFrameData.mHeight() + 1) / 2))) {
|
||||
int32_t half_height = (aFrameData.mHeight() + 1) / 2;
|
||||
|
||||
// Check for negative offsets
|
||||
if ((aFrameData.mYPlane().mOffset() < 0) ||
|
||||
(aFrameData.mUPlane().mOffset() < 0) ||
|
||||
(aFrameData.mVPlane().mOffset() < 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for non-positive strides and sizes
|
||||
if ((aFrameData.mYPlane().mStride() <= 0) ||
|
||||
(aFrameData.mYPlane().mSize() <= 0) ||
|
||||
(aFrameData.mUPlane().mStride() <= 0) ||
|
||||
(aFrameData.mUPlane().mSize() <= 0) ||
|
||||
(aFrameData.mVPlane().mStride() <= 0) ||
|
||||
(aFrameData.mVPlane().mSize() <= 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check stride requirements (must be at least as wide as the data)
|
||||
if ((aFrameData.mYPlane().mStride() < aFrameData.mWidth()) ||
|
||||
(aFrameData.mUPlane().mStride() < half_width) ||
|
||||
(aFrameData.mVPlane().mStride() < half_width)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate plane end calculations
|
||||
auto y_plane_end = CheckedInt<int32_t>(aFrameData.mYPlane().mOffset()) +
|
||||
aFrameData.mYPlane().mSize();
|
||||
auto u_plane_end = CheckedInt<int32_t>(aFrameData.mUPlane().mOffset()) +
|
||||
aFrameData.mUPlane().mSize();
|
||||
auto v_plane_end = CheckedInt<int32_t>(aFrameData.mVPlane().mOffset()) +
|
||||
aFrameData.mVPlane().mSize();
|
||||
|
||||
if (!y_plane_end.isValid() || !u_plane_end.isValid() ||
|
||||
!v_plane_end.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that planes don't overlap
|
||||
if ((aFrameData.mUPlane().mOffset() < y_plane_end.value()) ||
|
||||
(aFrameData.mVPlane().mOffset() < u_plane_end.value())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check buffer size
|
||||
if (aBufferSize < static_cast<size_t>(v_plane_end.value())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate size calculations
|
||||
auto y_expected_size = CheckedInt<int32_t>(aFrameData.mYPlane().mStride()) *
|
||||
aFrameData.mHeight();
|
||||
auto u_expected_size =
|
||||
CheckedInt<int32_t>(aFrameData.mUPlane().mStride()) * half_height;
|
||||
auto v_expected_size =
|
||||
CheckedInt<int32_t>(aFrameData.mVPlane().mStride()) * half_height;
|
||||
|
||||
if (!y_expected_size.isValid() || !u_expected_size.isValid() ||
|
||||
!v_expected_size.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that allocated sizes are sufficient
|
||||
if ((aFrameData.mYPlane().mSize() < y_expected_size.value()) ||
|
||||
(aFrameData.mUPlane().mSize() < u_expected_size.value()) ||
|
||||
(aFrameData.mVPlane().mSize() < v_expected_size.value())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,14 +17,24 @@ class GMPPlaneData;
|
|||
class GMPVideoi420FrameData;
|
||||
class GMPVideoHostImpl;
|
||||
|
||||
class GMPVideoi420FrameImpl final : public GMPVideoi420Frame {
|
||||
enum class HostReportPolicy : uint8_t {
|
||||
None,
|
||||
Destroyed,
|
||||
};
|
||||
|
||||
class GMPVideoi420FrameImpl : public GMPVideoi420Frame {
|
||||
public:
|
||||
explicit GMPVideoi420FrameImpl(GMPVideoHostImpl* aHost);
|
||||
GMPVideoi420FrameImpl(const GMPVideoi420FrameData& aFrameData,
|
||||
ipc::Shmem&& aShmemBuffer, GMPVideoHostImpl* aHost);
|
||||
GMPVideoi420FrameImpl(const GMPVideoi420FrameData& aFrameData,
|
||||
nsTArray<uint8_t>&& aArrayBuffer,
|
||||
GMPVideoHostImpl* aHost);
|
||||
explicit GMPVideoi420FrameImpl(
|
||||
GMPVideoHostImpl* aHost,
|
||||
HostReportPolicy aReportPolicy = HostReportPolicy::None);
|
||||
GMPVideoi420FrameImpl(
|
||||
const GMPVideoi420FrameData& aFrameData, ipc::Shmem&& aShmemBuffer,
|
||||
GMPVideoHostImpl* aHost,
|
||||
HostReportPolicy aReportPolicy = HostReportPolicy::None);
|
||||
GMPVideoi420FrameImpl(
|
||||
const GMPVideoi420FrameData& aFrameData, nsTArray<uint8_t>&& aArrayBuffer,
|
||||
GMPVideoHostImpl* aHost,
|
||||
HostReportPolicy aReportPolicy = HostReportPolicy::None);
|
||||
virtual ~GMPVideoi420FrameImpl();
|
||||
|
||||
// This is called during a normal destroy sequence, which is
|
||||
|
|
@ -75,7 +85,7 @@ class GMPVideoi420FrameImpl final : public GMPVideoi420Frame {
|
|||
const uint8_t* Buffer() const;
|
||||
int32_t AllocatedSize() const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
struct GMPFramePlane {
|
||||
explicit GMPFramePlane(const GMPPlaneData& aPlaneData);
|
||||
GMPFramePlane() = default;
|
||||
|
|
@ -98,6 +108,10 @@ class GMPVideoi420FrameImpl final : public GMPVideoi420Frame {
|
|||
GMPErr MaybeResize(int32_t aNewSize);
|
||||
void DestroyBuffer();
|
||||
|
||||
public:
|
||||
const HostReportPolicy mReportPolicy;
|
||||
|
||||
protected:
|
||||
GMPVideoHostImpl* mHost;
|
||||
nsTArray<uint8_t> mArrayBuffer;
|
||||
ipc::Shmem mShmemBuffer;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ parent:
|
|||
async EncodedData(GMPVideoEncodedFrameData aEncodedFrame,
|
||||
uint8_t[] aEncodedData,
|
||||
uint8_t[] aCodecSpecificInfo);
|
||||
async DroppedFrame(uint64_t aTimestamp);
|
||||
async Error(GMPErr aErr);
|
||||
async Shutdown();
|
||||
};
|
||||
|
|
|
|||
279
icecat/dom/media/gtest/TestWebrtcGmpCodec.cpp
Normal file
279
icecat/dom/media/gtest/TestWebrtcGmpCodec.cpp
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "api/video/i420_buffer.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "libwebrtcglue/WebrtcGmpVideoCodec.h"
|
||||
#include "media/base/media_constants.h"
|
||||
#include "mozilla/gtest/WaitFor.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
using testing::_;
|
||||
using testing::AtLeast;
|
||||
using testing::Eq;
|
||||
using testing::Ge;
|
||||
using testing::Gt;
|
||||
using testing::InSequence;
|
||||
using testing::Property;
|
||||
using testing::Test;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct TestWebrtcGmpVideoEncoder : public Test {
|
||||
nsCOMPtr<nsIThread> mGmpThread;
|
||||
RefPtr<WebrtcGmpVideoEncoder> mEncoder;
|
||||
webrtc::VideoCodec mCodecSettings;
|
||||
webrtc::VideoEncoder::Settings mSettings = {
|
||||
webrtc::VideoEncoder::Capabilities(/*loss_notification=*/true),
|
||||
/*number_of_cores=*/1, /*max_payload_size=*/0};
|
||||
|
||||
void SetUp() override {
|
||||
mEncoder = MakeRefPtr<WebrtcGmpVideoEncoder>(
|
||||
webrtc::SdpVideoFormat(cricket::kH264CodecName), "dummy");
|
||||
mCodecSettings.codecType = webrtc::VideoCodecType::kVideoCodecH264;
|
||||
mCodecSettings.numberOfSimulcastStreams = 1;
|
||||
mCodecSettings.simulcastStream[0].active = true;
|
||||
mCodecSettings.simulcastStream[0].numberOfTemporalLayers = 1;
|
||||
mCodecSettings.width = 640;
|
||||
mCodecSettings.height = 480;
|
||||
mCodecSettings.maxFramerate = 60;
|
||||
mCodecSettings.minBitrate = 50;
|
||||
mCodecSettings.startBitrate = 200;
|
||||
mCodecSettings.maxBitrate = 1000;
|
||||
|
||||
nsCOMPtr<mozIGeckoMediaPluginService> mps =
|
||||
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
|
||||
ASSERT_TRUE(mps);
|
||||
mps->GetThread(getter_AddRefs(mGmpThread));
|
||||
ASSERT_TRUE(mGmpThread);
|
||||
}
|
||||
|
||||
void TearDown() override { mEncoder = nullptr; }
|
||||
};
|
||||
|
||||
struct MockEncodedImageCallback : public webrtc::EncodedImageCallback {
|
||||
MOCK_METHOD(Result, OnEncodedImage,
|
||||
(const webrtc::EncodedImage&, const webrtc::CodecSpecificInfo*),
|
||||
(override));
|
||||
MOCK_METHOD(void, OnDroppedFrame, (DropReason), (override));
|
||||
};
|
||||
|
||||
auto CreateBlackFrame(int width, int height) {
|
||||
auto buffer = webrtc::I420Buffer::Create(width, height);
|
||||
webrtc::I420Buffer::SetBlack(buffer.get());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
TEST_F(TestWebrtcGmpVideoEncoder, EmptyLifecycle) {}
|
||||
|
||||
TEST_F(TestWebrtcGmpVideoEncoder, InitEncode) {
|
||||
mEncoder->InitEncode(&mCodecSettings, mSettings);
|
||||
WaitFor(*mEncoder->InitPluginEvent());
|
||||
}
|
||||
|
||||
TEST_F(TestWebrtcGmpVideoEncoder, Encode) {
|
||||
using Result = webrtc::EncodedImageCallback::Result;
|
||||
mEncoder->InitEncode(&mCodecSettings, mSettings);
|
||||
WaitFor(*mEncoder->InitPluginEvent());
|
||||
|
||||
MozPromiseHolder<GenericPromise> doneHolder;
|
||||
RefPtr donePromise = doneHolder.Ensure(__func__);
|
||||
MockEncodedImageCallback callback;
|
||||
constexpr uint32_t rtp_time = 55;
|
||||
EXPECT_CALL(
|
||||
callback,
|
||||
OnEncodedImage(
|
||||
Property(&webrtc::EncodedImage::RtpTimestamp, Eq(rtp_time)), _))
|
||||
.WillOnce([&] {
|
||||
doneHolder.Resolve(true, "TestWebrtcGmpVideoEncoder::Encode");
|
||||
return Result(Result::OK);
|
||||
});
|
||||
mEncoder->RegisterEncodeCompleteCallback(&callback);
|
||||
std::vector<webrtc::VideoFrameType> types = {
|
||||
webrtc::VideoFrameType::kVideoFrameKey};
|
||||
EXPECT_EQ(
|
||||
mEncoder->Encode(webrtc::VideoFrame::Builder()
|
||||
.set_rtp_timestamp(rtp_time)
|
||||
.set_video_frame_buffer(CreateBlackFrame(
|
||||
mCodecSettings.width, mCodecSettings.height))
|
||||
.build(),
|
||||
&types),
|
||||
WEBRTC_VIDEO_CODEC_OK);
|
||||
EXPECT_EQ(WaitForResolve(donePromise), true);
|
||||
}
|
||||
|
||||
TEST_F(TestWebrtcGmpVideoEncoder, BackPressure) {
|
||||
using Result = webrtc::EncodedImageCallback::Result;
|
||||
mEncoder->InitEncode(&mCodecSettings, mSettings);
|
||||
WaitFor(*mEncoder->InitPluginEvent());
|
||||
|
||||
MozPromiseHolder<GenericPromise> doneHolder;
|
||||
RefPtr donePromise = doneHolder.Ensure(__func__);
|
||||
MockEncodedImageCallback callback;
|
||||
constexpr uint32_t rtpTime = 55;
|
||||
constexpr size_t iterations = 1000;
|
||||
Atomic<uint32_t> lastRtpTime{};
|
||||
Atomic<size_t> eventCount{};
|
||||
const auto countIteration = [&] {
|
||||
size_t c = ++eventCount;
|
||||
EXPECT_LE(c, iterations);
|
||||
if (c == iterations) {
|
||||
doneHolder.Resolve(true, "TestWebrtcGmpVideoEncoder::BackPressure");
|
||||
}
|
||||
};
|
||||
EXPECT_CALL(
|
||||
callback,
|
||||
OnEncodedImage(
|
||||
Property(&webrtc::EncodedImage::RtpTimestamp,
|
||||
testing::AllOf(Ge(rtpTime), Gt<uint32_t>(lastRtpTime))),
|
||||
_))
|
||||
.Times(AtLeast(1))
|
||||
.WillRepeatedly([&](const auto& aImage, const auto*) {
|
||||
lastRtpTime = aImage.RtpTimestamp();
|
||||
countIteration();
|
||||
return Result(Result::OK);
|
||||
});
|
||||
EXPECT_CALL(
|
||||
callback,
|
||||
OnDroppedFrame(MockEncodedImageCallback::DropReason::kDroppedByEncoder))
|
||||
.Times(AtLeast(iterations / 10))
|
||||
.WillRepeatedly(countIteration);
|
||||
mEncoder->RegisterEncodeCompleteCallback(&callback);
|
||||
std::vector<webrtc::VideoFrameType> types = {
|
||||
webrtc::VideoFrameType::kVideoFrameKey};
|
||||
EXPECT_EQ(
|
||||
mEncoder->Encode(webrtc::VideoFrame::Builder()
|
||||
.set_rtp_timestamp(rtpTime)
|
||||
.set_video_frame_buffer(CreateBlackFrame(
|
||||
mCodecSettings.width, mCodecSettings.height))
|
||||
.build(),
|
||||
&types),
|
||||
WEBRTC_VIDEO_CODEC_OK);
|
||||
for (size_t i = 1; i < iterations; ++i) {
|
||||
mEncoder->Encode(webrtc::VideoFrame::Builder()
|
||||
.set_rtp_timestamp(rtpTime + i)
|
||||
.set_video_frame_buffer(CreateBlackFrame(
|
||||
mCodecSettings.width, mCodecSettings.height))
|
||||
.build(),
|
||||
&types);
|
||||
}
|
||||
EXPECT_EQ(WaitForResolve(donePromise), true);
|
||||
EXPECT_EQ(eventCount, iterations);
|
||||
}
|
||||
|
||||
TEST_F(TestWebrtcGmpVideoEncoder, ReUse) {
|
||||
using Result = webrtc::EncodedImageCallback::Result;
|
||||
mEncoder->InitEncode(&mCodecSettings, mSettings);
|
||||
WaitFor(*mEncoder->InitPluginEvent());
|
||||
|
||||
MozPromiseHolder<GenericPromise> doneHolder;
|
||||
RefPtr donePromise = doneHolder.Ensure(__func__);
|
||||
MockEncodedImageCallback callback;
|
||||
constexpr uint32_t rtpTime = 55;
|
||||
constexpr uint32_t rtpTime2 = rtpTime * 2;
|
||||
EXPECT_CALL(callback,
|
||||
OnEncodedImage(
|
||||
Property(&webrtc::EncodedImage::RtpTimestamp, rtpTime2), _))
|
||||
.WillOnce([&] {
|
||||
doneHolder.Resolve(true, "TestWebrtcGmpVideoEncoder::ReUse");
|
||||
return Result(Result::OK);
|
||||
});
|
||||
mEncoder->RegisterEncodeCompleteCallback(&callback);
|
||||
|
||||
// Block the GMP thread until after Shutdown() as to avoid racing between the
|
||||
// first encoded callback and the Shutdown() call.
|
||||
Monitor mon(__func__);
|
||||
bool block = true;
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
mGmpThread->Dispatch(NS_NewRunnableFunction(__func__, [&] {
|
||||
MonitorAutoLock lock(mon);
|
||||
while (block) {
|
||||
lock.Wait();
|
||||
}
|
||||
})));
|
||||
std::vector<webrtc::VideoFrameType> types = {
|
||||
webrtc::VideoFrameType::kVideoFrameKey};
|
||||
EXPECT_EQ(
|
||||
mEncoder->Encode(webrtc::VideoFrame::Builder()
|
||||
.set_rtp_timestamp(rtpTime)
|
||||
.set_video_frame_buffer(CreateBlackFrame(
|
||||
mCodecSettings.width, mCodecSettings.height))
|
||||
.build(),
|
||||
&types),
|
||||
WEBRTC_VIDEO_CODEC_OK);
|
||||
|
||||
// Shutdown mid-encode, then re-init and encode again.
|
||||
mEncoder->Shutdown();
|
||||
{
|
||||
MonitorAutoLock lock(mon);
|
||||
block = false;
|
||||
lock.Notify();
|
||||
}
|
||||
mEncoder->InitEncode(&mCodecSettings, mSettings);
|
||||
WaitFor(*mEncoder->InitPluginEvent());
|
||||
mEncoder->RegisterEncodeCompleteCallback(&callback);
|
||||
|
||||
EXPECT_EQ(
|
||||
mEncoder->Encode(webrtc::VideoFrame::Builder()
|
||||
.set_rtp_timestamp(rtpTime2)
|
||||
.set_video_frame_buffer(CreateBlackFrame(
|
||||
mCodecSettings.width, mCodecSettings.height))
|
||||
.build(),
|
||||
&types),
|
||||
WEBRTC_VIDEO_CODEC_OK);
|
||||
EXPECT_EQ(WaitForResolve(donePromise), true);
|
||||
}
|
||||
|
||||
TEST_F(TestWebrtcGmpVideoEncoder, TrackedFrameDrops) {
|
||||
using Result = webrtc::EncodedImageCallback::Result;
|
||||
// Tell the fakeopenh264 plugin to drop some allocated input frames without
|
||||
// telling us. It will drop every fifth input frame. This shall get tracked
|
||||
// as frame drops.
|
||||
mCodecSettings.SetFrameDropEnabled(true);
|
||||
mEncoder->InitEncode(&mCodecSettings, mSettings);
|
||||
WaitFor(*mEncoder->InitPluginEvent());
|
||||
|
||||
Monitor m(__func__);
|
||||
size_t numEvents = 0;
|
||||
const auto handleEvent = ([&] {
|
||||
MonitorAutoLock lock(m);
|
||||
++numEvents;
|
||||
lock.Notify();
|
||||
});
|
||||
MockEncodedImageCallback callback;
|
||||
{
|
||||
InSequence s;
|
||||
EXPECT_CALL(callback, OnEncodedImage(_, _)).Times(4).WillRepeatedly([&] {
|
||||
handleEvent();
|
||||
return Result(Result::OK);
|
||||
});
|
||||
EXPECT_CALL(
|
||||
callback,
|
||||
OnDroppedFrame(MockEncodedImageCallback::DropReason::kDroppedByEncoder))
|
||||
.WillOnce(handleEvent);
|
||||
}
|
||||
mEncoder->RegisterEncodeCompleteCallback(&callback);
|
||||
|
||||
constexpr uint32_t ntpTime = 55;
|
||||
std::vector<webrtc::VideoFrameType> types = {
|
||||
webrtc::VideoFrameType::kVideoFrameKey};
|
||||
for (uint8_t i = 0; i < 5; ++i) {
|
||||
EXPECT_EQ(
|
||||
mEncoder->Encode(webrtc::VideoFrame::Builder()
|
||||
.set_ntp_time_ms(ntpTime * (i + 1))
|
||||
.set_video_frame_buffer(CreateBlackFrame(
|
||||
mCodecSettings.width, mCodecSettings.height))
|
||||
.build(),
|
||||
&types),
|
||||
WEBRTC_VIDEO_CODEC_OK);
|
||||
MonitorAutoLock lock(m);
|
||||
while (numEvents <= i) {
|
||||
lock.Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace mozilla
|
||||
|
|
@ -92,6 +92,7 @@ if CONFIG["MOZ_WEBRTC"] and CONFIG["OS_TARGET"] != "Android":
|
|||
"TestAudioDeviceEnumerator.cpp",
|
||||
"TestPacer.cpp",
|
||||
"TestVideoFrameConverter.cpp",
|
||||
"TestWebrtcGmpCodec.cpp",
|
||||
]
|
||||
|
||||
TEST_HARNESS_FILES.gtest += [
|
||||
|
|
|
|||
|
|
@ -187,13 +187,17 @@ RefPtr<MediaDataDecoder::DecodePromise> VPXDecoder::ProcessDecode(
|
|||
|
||||
if (img->fmt == VPX_IMG_FMT_I420) {
|
||||
b.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
|
||||
|
||||
MOZ_ASSERT(img->y_chroma_shift == 1);
|
||||
b.mPlanes[1].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
|
||||
MOZ_ASSERT(img->x_chroma_shift == 1);
|
||||
b.mPlanes[1].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
|
||||
|
||||
b.mPlanes[2].mHeight = (img->d_h + 1) >> img->y_chroma_shift;
|
||||
b.mPlanes[2].mWidth = (img->d_w + 1) >> img->x_chroma_shift;
|
||||
} else if (img->fmt == VPX_IMG_FMT_I444) {
|
||||
MOZ_ASSERT(b.mChromaSubsampling == gfx::ChromaSubsampling::FULL);
|
||||
MOZ_ASSERT(img->y_chroma_shift == 0);
|
||||
MOZ_ASSERT(img->x_chroma_shift == 0);
|
||||
b.mPlanes[1].mHeight = img->d_h;
|
||||
b.mPlanes[1].mWidth = img->d_w;
|
||||
|
||||
|
|
|
|||
|
|
@ -427,6 +427,24 @@ void GMPVideoEncoder::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
|
|||
}
|
||||
}
|
||||
|
||||
void GMPVideoEncoder::Dropped(uint64_t aTimestamp) {
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
RefPtr<EncodePromise::Private> promise;
|
||||
if (!mPendingEncodes.Remove(aTimestamp, getter_AddRefs(promise))) {
|
||||
GMP_LOG_WARNING(
|
||||
"[%p] GMPVideoEncoder::Dropped -- no frame matching timestamp %" PRIu64,
|
||||
this, aTimestamp);
|
||||
return;
|
||||
}
|
||||
|
||||
promise->Resolve(EncodedData(), __func__);
|
||||
|
||||
if (mPendingEncodes.IsEmpty()) {
|
||||
mDrainPromise.ResolveIfExists(EncodedData(), __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void GMPVideoEncoder::Teardown(const MediaResult& aResult,
|
||||
StaticString aCallSite) {
|
||||
GMP_LOG_DEBUG("[%p] GMPVideoEncoder::Teardown", this);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class GMPVideoEncoder final : public MediaDataEncoder,
|
|||
|
||||
void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
|
||||
const nsTArray<uint8_t>& aCodecSpecificInfo) override;
|
||||
void Dropped(uint64_t aTimestamp) override;
|
||||
void Error(GMPErr aError) override;
|
||||
void Terminated() override;
|
||||
|
||||
|
|
|
|||
|
|
@ -568,7 +568,7 @@ void AudioData::CopyTo(const AllowSharedBufferSource& aDestination,
|
|||
uint32_t bytesPerSample = BytesPerSamples(destFormat.value());
|
||||
CheckedInt<uint32_t> copyLength = bytesPerSample;
|
||||
copyLength *= copyElementCount;
|
||||
if (copyLength.value() > destLength) {
|
||||
if (!copyLength.isValid() || copyLength.value() > destLength) {
|
||||
auto msg = nsFmtCString(FMT_STRING("destination buffer of length {} too "
|
||||
"small for copying {} elements"),
|
||||
destLength, bytesPerSample * copyElementCount);
|
||||
|
|
|
|||
|
|
@ -1905,17 +1905,17 @@ nsresult JsepSessionImpl::ValidateRemoteDescription(const Sdp& description) {
|
|||
const SdpMediaSection& oldMsection =
|
||||
mCurrentRemoteDescription->GetMediaSection(i);
|
||||
|
||||
if (mSdpHelper.MsectionIsDisabled(newMsection) ||
|
||||
mSdpHelper.MsectionIsDisabled(oldMsection)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (oldMsection.GetMediaType() != newMsection.GetMediaType()) {
|
||||
JSEP_SET_ERROR("Remote description changes the media type of m-line "
|
||||
<< i);
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (mSdpHelper.MsectionIsDisabled(newMsection) ||
|
||||
mSdpHelper.MsectionIsDisabled(oldMsection)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool differ = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection);
|
||||
|
||||
if (mIsPendingOfferer.isSome() && *mIsPendingOfferer && differ &&
|
||||
|
|
|
|||
|
|
@ -5,10 +5,14 @@
|
|||
#include "jsep/JsepTrack.h"
|
||||
#include "jsep/JsepCodecDescription.h"
|
||||
#include "jsep/JsepTrackEncoding.h"
|
||||
#include "transport/logging.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
MOZ_MTLOG_MODULE("jsep")
|
||||
|
||||
void JsepTrack::GetNegotiatedPayloadTypes(
|
||||
std::vector<uint16_t>* payloadTypes) const {
|
||||
if (!mNegotiatedDetails) {
|
||||
|
|
@ -32,6 +36,8 @@ void JsepTrack::GetPayloadTypes(
|
|||
for (const auto& codec : codecs) {
|
||||
uint16_t pt;
|
||||
if (!codec->GetPtAsInt(&pt)) {
|
||||
MOZ_MTLOG(ML_ERROR, "Codec " << codec->mName
|
||||
<< " does not have a valid payload type");
|
||||
MOZ_ASSERT(false);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -52,14 +58,47 @@ void JsepTrack::EnsureSsrcs(SsrcGenerator& ssrcGenerator, size_t aNumber) {
|
|||
uint32_t ssrc, rtxSsrc;
|
||||
if (!ssrcGenerator.GenerateSsrc(&ssrc) ||
|
||||
!ssrcGenerator.GenerateSsrc(&rtxSsrc)) {
|
||||
MOZ_MTLOG(ML_ERROR, "Unable to generate SSRC");
|
||||
return;
|
||||
}
|
||||
mSsrcs.push_back(ssrc);
|
||||
mSsrcToRtxSsrc[ssrc] = rtxSsrc;
|
||||
MOZ_ASSERT(mSsrcs.size() == mSsrcToRtxSsrc.size());
|
||||
if (mSsrcs.size() != mSsrcToRtxSsrc.size()) {
|
||||
MOZ_MTLOG(ML_ERROR,
|
||||
"[" << mTrackId
|
||||
<< "]: mSsrcToRtxSsrc has different size than mSsrcs.");
|
||||
MOZ_ASSERT(false);
|
||||
mSsrcs.clear();
|
||||
mSsrcToRtxSsrc.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint32_t> JsepTrack::GetRtxSsrcs() const {
|
||||
std::vector<uint32_t> result;
|
||||
if (mRtxIsAllowed &&
|
||||
Preferences::GetBool("media.peerconnection.video.use_rtx", false) &&
|
||||
!mSsrcToRtxSsrc.empty()) {
|
||||
if (mSsrcToRtxSsrc.size() != mSsrcs.size()) {
|
||||
MOZ_MTLOG(ML_ERROR,
|
||||
"[" << mTrackId
|
||||
<< "]: mSsrcToRtxSsrc has different size than mSsrcs.");
|
||||
return {};
|
||||
}
|
||||
for (const auto ssrc : mSsrcs) {
|
||||
auto it = mSsrcToRtxSsrc.find(ssrc);
|
||||
if (it != mSsrcToRtxSsrc.end()) {
|
||||
result.push_back(it->second);
|
||||
} else {
|
||||
MOZ_MTLOG(ML_ERROR,
|
||||
"[" << mTrackId << "]: No RTX SSRC found for SSRC " << ssrc);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void JsepTrack::PopulateCodecs(
|
||||
const std::vector<UniquePtr<JsepCodecDescription>>& prototype,
|
||||
bool aUsePreferredCodecsOrder) {
|
||||
|
|
@ -117,7 +156,10 @@ void JsepTrack::AddToAnswer(const SdpMediaSection& offer,
|
|||
}
|
||||
|
||||
void JsepTrack::SetRids(const std::vector<std::string>& aRids) {
|
||||
MOZ_ASSERT(!aRids.empty());
|
||||
if (!aRids.size()) {
|
||||
MOZ_MTLOG(ML_ERROR, "cannot set empty rids");
|
||||
return;
|
||||
}
|
||||
if (!mRids.empty()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -134,9 +176,17 @@ void JsepTrack::SetMaxEncodings(size_t aMax) {
|
|||
void JsepTrack::RecvTrackSetRemote(const Sdp& aSdp,
|
||||
const SdpMediaSection& aMsection) {
|
||||
mInHaveRemote = true;
|
||||
MOZ_ASSERT(mDirection == sdp::kRecv);
|
||||
MOZ_ASSERT(aMsection.GetMediaType() !=
|
||||
SdpMediaSection::MediaType::kApplication);
|
||||
if (mDirection != sdp::kRecv) {
|
||||
MOZ_MTLOG(ML_ERROR, "RecvTrackSetRemote called on non-receive track");
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
if (aMsection.GetMediaType() == SdpMediaSection::kApplication) {
|
||||
MOZ_MTLOG(ML_ERROR,
|
||||
"RecvTrackSetRemote called on application media section");
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
std::string error;
|
||||
SdpHelper helper(&error);
|
||||
|
||||
|
|
@ -187,7 +237,11 @@ void JsepTrack::RecvTrackSetRemote(const Sdp& aSdp,
|
|||
}
|
||||
|
||||
void JsepTrack::RecvTrackSetLocal(const SdpMediaSection& aMsection) {
|
||||
MOZ_ASSERT(mDirection == sdp::kRecv);
|
||||
if (mDirection != sdp::kRecv) {
|
||||
MOZ_MTLOG(ML_ERROR, "RecvTrackSetLocal called on non-receive track");
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Should more stuff live in here? Anything that needs to happen when we
|
||||
// decide we're ready to receive packets should probably go in here.
|
||||
|
|
@ -259,9 +313,16 @@ void JsepTrack::SendTrackSetRemote(SsrcGenerator& aSsrcGenerator,
|
|||
void JsepTrack::AddToMsection(
|
||||
const std::vector<UniquePtr<JsepCodecDescription>>& codecs,
|
||||
SdpMediaSection* msection) const {
|
||||
MOZ_ASSERT(msection->GetMediaType() == mType);
|
||||
MOZ_ASSERT(!codecs.empty());
|
||||
|
||||
if (msection->GetMediaType() != mType) {
|
||||
MOZ_MTLOG(ML_ERROR, "AddToMsection called on wrong media section type");
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
if (codecs.empty()) {
|
||||
MOZ_MTLOG(ML_ERROR, "AddToMsection called with empty codecs");
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
for (const auto& codec : codecs) {
|
||||
codec->AddToMediaSection(*msection);
|
||||
}
|
||||
|
|
@ -279,8 +340,16 @@ void JsepTrack::AddToMsection(
|
|||
}
|
||||
|
||||
void JsepTrack::UpdateSsrcs(SsrcGenerator& ssrcGenerator, size_t encodings) {
|
||||
MOZ_ASSERT(mDirection == sdp::kSend);
|
||||
MOZ_ASSERT(mType != SdpMediaSection::kApplication);
|
||||
if (mDirection != sdp::kSend) {
|
||||
MOZ_MTLOG(ML_ERROR, "UpdateSsrcs called on non-send track");
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
if (mType == SdpMediaSection::kApplication) {
|
||||
MOZ_MTLOG(ML_ERROR, "UpdateSsrcs called on application media section");
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
size_t numSsrcs = std::max<size_t>(encodings, 1U);
|
||||
|
||||
EnsureSsrcs(ssrcGenerator, numSsrcs);
|
||||
|
|
@ -288,8 +357,10 @@ void JsepTrack::UpdateSsrcs(SsrcGenerator& ssrcGenerator, size_t encodings) {
|
|||
if (mNegotiatedDetails && mNegotiatedDetails->GetEncodingCount() > numSsrcs) {
|
||||
mNegotiatedDetails->TruncateEncodings(numSsrcs);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mSsrcs.empty());
|
||||
if (mSsrcs.empty()) {
|
||||
MOZ_MTLOG(ML_ERROR, "UpdateSsrcs resulted in empty mSsrcs");
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void JsepTrack::PruneSsrcs(size_t aNumSsrcs) {
|
||||
|
|
@ -352,7 +423,12 @@ void JsepTrack::AddToMsection(const std::vector<std::string>& aRids,
|
|||
UpdateSsrcs(ssrcGenerator, aRids.size());
|
||||
|
||||
if (requireRtxSsrcs) {
|
||||
MOZ_ASSERT(mSsrcs.size() == mSsrcToRtxSsrc.size());
|
||||
if (mSsrcs.size() != mSsrcToRtxSsrc.size()) {
|
||||
MOZ_MTLOG(ML_ERROR,
|
||||
"[" << mTrackId
|
||||
<< "]: mSsrcToRtxSsrc has different size than mSsrcs.");
|
||||
return;
|
||||
}
|
||||
std::vector<uint32_t> allSsrcs;
|
||||
UniquePtr<SdpSsrcGroupAttributeList> group(new SdpSsrcGroupAttributeList);
|
||||
for (const auto& ssrc : mSsrcs) {
|
||||
|
|
|
|||
|
|
@ -166,20 +166,7 @@ class JsepTrack {
|
|||
|
||||
virtual const std::vector<uint32_t>& GetSsrcs() const { return mSsrcs; }
|
||||
|
||||
virtual std::vector<uint32_t> GetRtxSsrcs() const {
|
||||
std::vector<uint32_t> result;
|
||||
if (mRtxIsAllowed &&
|
||||
Preferences::GetBool("media.peerconnection.video.use_rtx", false) &&
|
||||
!mSsrcToRtxSsrc.empty()) {
|
||||
MOZ_ASSERT(mSsrcToRtxSsrc.size() == mSsrcs.size());
|
||||
for (const auto ssrc : mSsrcs) {
|
||||
auto it = mSsrcToRtxSsrc.find(ssrc);
|
||||
MOZ_ASSERT(it != mSsrcToRtxSsrc.end());
|
||||
result.push_back(it->second);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
virtual std::vector<uint32_t> GetRtxSsrcs() const;
|
||||
|
||||
virtual void EnsureSsrcs(SsrcGenerator& ssrcGenerator, size_t aNumber);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ LOCAL_INCLUDES += [
|
|||
"/third_party/sipcc",
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += ["JsepSessionImpl.cpp", "JsepTrack.cpp", "SsrcGenerator.cpp"]
|
||||
UNIFIED_SOURCES += ["JsepSessionImpl.cpp", "SsrcGenerator.cpp"]
|
||||
|
||||
# Can not be built in unified build because of MOZ_MTLOG
|
||||
SOURCES += ["JsepTrack.cpp"]
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
|
|
|
|||
|
|
@ -257,6 +257,7 @@ void WebrtcGmpVideoEncoder::Close_g() {
|
|||
mGMP = nullptr;
|
||||
mHost = nullptr;
|
||||
mInitting = false;
|
||||
mInputImageMap.Clear();
|
||||
|
||||
if (mCachedPluginId) {
|
||||
mReleasePluginEvent.Notify(*mCachedPluginId);
|
||||
|
|
@ -340,6 +341,14 @@ void WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange(uint32_t aWidth,
|
|||
void WebrtcGmpVideoEncoder::Encode_g(
|
||||
const webrtc::VideoFrame& aInputImage,
|
||||
std::vector<webrtc::VideoFrameType> aFrameTypes) {
|
||||
auto reportDroppedOnExit = MakeScopeExit([&] {
|
||||
MutexAutoLock lock(mCallbackMutex);
|
||||
if (mCallback) {
|
||||
mCallback->OnDroppedFrame(
|
||||
webrtc::EncodedImageCallback::DropReason::kDroppedByEncoder);
|
||||
}
|
||||
});
|
||||
|
||||
if (!mGMP) {
|
||||
// destroyed via Terminate(), failed to init, or just not initted yet
|
||||
GMP_LOG_DEBUG("GMP Encode: not initted yet");
|
||||
|
|
@ -347,6 +356,13 @@ void WebrtcGmpVideoEncoder::Encode_g(
|
|||
}
|
||||
MOZ_ASSERT(mHost);
|
||||
|
||||
if (mInputImageMap.Length() >= kMaxImagesInFlight) {
|
||||
GMP_LOG_WARNING(
|
||||
"GMP Encode: Max number of frames already in flight. Dropping this "
|
||||
"one.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (static_cast<uint32_t>(aInputImage.width()) != mCodecParams.mWidth ||
|
||||
static_cast<uint32_t>(aInputImage.height()) != mCodecParams.mHeight) {
|
||||
GMP_LOG_DEBUG("GMP Encode: resolution change from %ux%u to %dx%d",
|
||||
|
|
@ -387,7 +403,9 @@ void WebrtcGmpVideoEncoder::Encode_g(
|
|||
GMP_LOG_DEBUG("GMP Encode: failed to create frame");
|
||||
return;
|
||||
}
|
||||
frame->SetTimestamp(AssertedCast<uint64_t>(aInputImage.ntp_time_ms() * 1000));
|
||||
const auto gmpTimestamp =
|
||||
AssertedCast<uint64_t>(aInputImage.ntp_time_ms() * 1000);
|
||||
frame->SetTimestamp(gmpTimestamp);
|
||||
|
||||
GMPCodecSpecificInfo info{};
|
||||
info.mCodecType = kGMPVideoCodecH264;
|
||||
|
|
@ -421,18 +439,23 @@ void WebrtcGmpVideoEncoder::Encode_g(
|
|||
MOZ_RELEASE_ASSERT(mInputImageMap.IsEmpty() ||
|
||||
mInputImageMap.LastElement().ntp_timestamp_ms <
|
||||
aInputImage.ntp_time_ms());
|
||||
mInputImageMap.AppendElement(
|
||||
InputImageData{.gmp_timestamp_us = frame->Timestamp(),
|
||||
.ntp_timestamp_ms = aInputImage.ntp_time_ms(),
|
||||
.timestamp_us = aInputImage.timestamp_us(),
|
||||
.rtp_timestamp = aInputImage.rtp_timestamp(),
|
||||
.frame_config = frameConfigs[0]});
|
||||
|
||||
GMP_LOG_DEBUG("GMP Encode: %" PRIu64, (frame->Timestamp()));
|
||||
err = mGMP->Encode(std::move(frame), codecSpecificInfo, gmp_frame_types);
|
||||
if (err != GMPNoErr) {
|
||||
GMP_LOG_DEBUG("GMP Encode: failed to encode frame");
|
||||
return;
|
||||
}
|
||||
|
||||
// Once in mInputImageMap, frame drops are reported by GMP callbacks
|
||||
// (Encoded/Dropped).
|
||||
reportDroppedOnExit.release();
|
||||
mInputImageMap.AppendElement(
|
||||
InputImageData{.gmp_timestamp_us = gmpTimestamp,
|
||||
.ntp_timestamp_ms = aInputImage.ntp_time_ms(),
|
||||
.timestamp_us = aInputImage.timestamp_us(),
|
||||
.rtp_timestamp = aInputImage.rtp_timestamp(),
|
||||
.frame_config = frameConfigs[0]});
|
||||
}
|
||||
|
||||
int32_t WebrtcGmpVideoEncoder::RegisterEncodeCompleteCallback(
|
||||
|
|
@ -517,6 +540,7 @@ void WebrtcGmpVideoEncoder::Terminated() {
|
|||
mGMP = nullptr;
|
||||
mHost = nullptr;
|
||||
mInitting = false;
|
||||
mInputImageMap.Clear();
|
||||
|
||||
if (gmp) {
|
||||
// Do this last, since this could cause us to be destroyed
|
||||
|
|
@ -526,20 +550,23 @@ void WebrtcGmpVideoEncoder::Terminated() {
|
|||
// Could now notify that it's dead
|
||||
}
|
||||
|
||||
static int32_t GmpTimestampComparator(const InputImageData& aA,
|
||||
const InputImageData& aB) {
|
||||
const auto& a = aA.gmp_timestamp_us;
|
||||
const auto& b = aB.gmp_timestamp_us;
|
||||
return a < b ? -1 : a != b;
|
||||
}
|
||||
|
||||
void WebrtcGmpVideoEncoder::Encoded(
|
||||
GMPVideoEncodedFrame* aEncodedFrame,
|
||||
const nsTArray<uint8_t>& aCodecSpecificInfo) {
|
||||
MOZ_ASSERT(mGMPThread->IsOnCurrentThread());
|
||||
Maybe<InputImageData> data;
|
||||
auto gmp_timestamp_comparator = [](const InputImageData& aA,
|
||||
const InputImageData& aB) -> int32_t {
|
||||
const auto& a = aA.gmp_timestamp_us;
|
||||
const auto& b = aB.gmp_timestamp_us;
|
||||
return a < b ? -1 : a != b;
|
||||
};
|
||||
MOZ_ASSERT(!mInputImageMap.IsEmpty());
|
||||
MOZ_ASSERT(mInputImageMap.Length() <= kMaxImagesInFlight);
|
||||
size_t nextIdx = mInputImageMap.IndexOfFirstElementGt(
|
||||
InputImageData{.gmp_timestamp_us = aEncodedFrame->TimeStamp()},
|
||||
gmp_timestamp_comparator);
|
||||
GmpTimestampComparator);
|
||||
const size_t numToRemove = nextIdx;
|
||||
size_t numFramesDropped = numToRemove;
|
||||
MOZ_ASSERT(nextIdx != 0);
|
||||
|
|
@ -676,6 +703,34 @@ void WebrtcGmpVideoEncoder::Encoded(
|
|||
mCallback->OnEncodedImage(unit, &info);
|
||||
}
|
||||
|
||||
void WebrtcGmpVideoEncoder::Dropped(uint64_t aTimestamp) {
|
||||
MOZ_ASSERT(mGMPThread->IsOnCurrentThread());
|
||||
MOZ_ASSERT(!mInputImageMap.IsEmpty());
|
||||
MOZ_ASSERT(mInputImageMap.Length() <= kMaxImagesInFlight);
|
||||
|
||||
size_t nextIdx = mInputImageMap.IndexOfFirstElementGt(
|
||||
InputImageData{.gmp_timestamp_us = aTimestamp}, GmpTimestampComparator);
|
||||
const size_t numDropped = nextIdx;
|
||||
MOZ_ASSERT(nextIdx != 0);
|
||||
MOZ_ASSERT(mInputImageMap.ElementAt(nextIdx - 1).gmp_timestamp_us ==
|
||||
aTimestamp);
|
||||
mInputImageMap.RemoveElementsAt(0, numDropped);
|
||||
|
||||
GMP_LOG_DEBUG("GMP Dropped: %" PRIu64
|
||||
" dropped by encoder. Reporting %u frames dropped.",
|
||||
aTimestamp, static_cast<uint32_t>(numDropped));
|
||||
|
||||
MutexAutoLock lock(mCallbackMutex);
|
||||
if (!mCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < numDropped; ++i) {
|
||||
mCallback->OnDroppedFrame(
|
||||
webrtc::EncodedImageCallback::DropReason::kDroppedByEncoder);
|
||||
}
|
||||
}
|
||||
|
||||
// Decoder.
|
||||
WebrtcGmpVideoDecoder::WebrtcGmpVideoDecoder(std::string aPCHandle,
|
||||
TrackingId aTrackingId)
|
||||
|
|
|
|||
|
|
@ -204,6 +204,8 @@ class WebrtcGmpVideoEncoder final : public GMPVideoEncoderCallbackProxy,
|
|||
void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
|
||||
const nsTArray<uint8_t>& aCodecSpecificInfo) override;
|
||||
|
||||
void Dropped(uint64_t aTimestamp) override;
|
||||
|
||||
void Error(GMPErr aError) override {}
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,17 @@
|
|||
|
||||
#include <cstring>
|
||||
|
||||
#include "ipc/EnumSerializer.h"
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "ipc/IPCMessageUtilsSpecializations.h"
|
||||
|
||||
namespace IPC {
|
||||
template <>
|
||||
struct ParamTraits<mozilla::MediaPacket::Type>
|
||||
: public ContiguousEnumSerializerInclusive<
|
||||
mozilla::MediaPacket::Type, mozilla::MediaPacket::UNCLASSIFIED,
|
||||
mozilla::MediaPacket::SCTP> {};
|
||||
} // namespace IPC
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
|
@ -31,66 +41,52 @@ MediaPacket MediaPacket::Clone() const {
|
|||
}
|
||||
|
||||
void MediaPacket::Serialize(IPC::MessageWriter* aWriter) const {
|
||||
aWriter->WriteUInt32(len_);
|
||||
aWriter->WriteUInt32(capacity_);
|
||||
WriteParam(aWriter, len_);
|
||||
WriteParam(aWriter, capacity_);
|
||||
WriteParam(aWriter, encrypted_len_);
|
||||
WriteParam(aWriter, sdp_level_);
|
||||
WriteParam(aWriter, type_);
|
||||
|
||||
if (len_) {
|
||||
aWriter->WriteBytes(data_.get(), len_);
|
||||
}
|
||||
aWriter->WriteUInt32(encrypted_len_);
|
||||
if (encrypted_len_) {
|
||||
aWriter->WriteBytes(encrypted_data_.get(), encrypted_len_);
|
||||
}
|
||||
aWriter->WriteInt32(sdp_level_.isSome() ? *sdp_level_ : -1);
|
||||
aWriter->WriteInt32(type_);
|
||||
}
|
||||
|
||||
bool MediaPacket::Deserialize(IPC::MessageReader* aReader) {
|
||||
Reset();
|
||||
uint32_t len;
|
||||
if (!aReader->ReadUInt32(&len)) {
|
||||
if (!ReadParam(aReader, &len_) || !ReadParam(aReader, &capacity_) ||
|
||||
!ReadParam(aReader, &encrypted_len_) ||
|
||||
!ReadParam(aReader, &sdp_level_) || !ReadParam(aReader, &type_)) {
|
||||
return false;
|
||||
}
|
||||
uint32_t capacity;
|
||||
if (!aReader->ReadUInt32(&capacity)) {
|
||||
|
||||
if (capacity_ < len_) {
|
||||
return false;
|
||||
}
|
||||
if (len) {
|
||||
MOZ_RELEASE_ASSERT(capacity >= len);
|
||||
UniquePtr<uint8_t[]> data(new uint8_t[capacity]);
|
||||
if (!aReader->ReadBytesInto(data.get(), len)) {
|
||||
|
||||
// Kinda arbitrary, but we want some sort of ceiling here.
|
||||
if ((capacity_ > 1024 * 1024) || (encrypted_len_ > 1024 * 1024)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (capacity_) {
|
||||
data_.reset(new uint8_t[capacity_]);
|
||||
if (len_) {
|
||||
if (!aReader->ReadBytesInto(data_.get(), len_)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (encrypted_len_) {
|
||||
encrypted_data_.reset(new uint8_t[encrypted_len_]);
|
||||
if (!aReader->ReadBytesInto(encrypted_data_.get(), encrypted_len_)) {
|
||||
return false;
|
||||
}
|
||||
data_ = std::move(data);
|
||||
len_ = len;
|
||||
capacity_ = capacity;
|
||||
}
|
||||
|
||||
if (!aReader->ReadUInt32(&len)) {
|
||||
return false;
|
||||
}
|
||||
if (len) {
|
||||
UniquePtr<uint8_t[]> data(new uint8_t[len]);
|
||||
if (!aReader->ReadBytesInto(data.get(), len)) {
|
||||
return false;
|
||||
}
|
||||
encrypted_data_ = std::move(data);
|
||||
encrypted_len_ = len;
|
||||
}
|
||||
|
||||
int32_t sdp_level;
|
||||
if (!aReader->ReadInt32(&sdp_level)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sdp_level >= 0) {
|
||||
sdp_level_ = Some(sdp_level);
|
||||
}
|
||||
|
||||
int32_t type;
|
||||
if (!aReader->ReadInt32(&type)) {
|
||||
return false;
|
||||
}
|
||||
type_ = static_cast<Type>(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,19 @@ nsresult NrIceStunAddr::Deserialize(const char* buffer, size_t buffer_size) {
|
|||
nr_local_addr* from_addr =
|
||||
const_cast<nr_local_addr*>((const nr_local_addr*)buffer);
|
||||
|
||||
// Just in case
|
||||
constexpr size_t ifname_size =
|
||||
sizeof(from_addr->addr.ifname) / sizeof(from_addr->addr.ifname[0]);
|
||||
constexpr size_t as_string_size =
|
||||
sizeof(from_addr->addr.as_string) / sizeof(from_addr->addr.as_string[0]);
|
||||
constexpr size_t fqdn_size =
|
||||
sizeof(from_addr->addr.fqdn) / sizeof(from_addr->addr.fqdn[0]);
|
||||
from_addr->addr.ifname[ifname_size - 1] = '\0';
|
||||
from_addr->addr.as_string[as_string_size - 1] = '\0';
|
||||
from_addr->addr.fqdn[fqdn_size - 1] = '\0';
|
||||
from_addr->addr.is_proxied = !!from_addr->addr.is_proxied;
|
||||
from_addr->addr.tls = !!from_addr->addr.tls;
|
||||
|
||||
// At this point, from_addr->addr.addr is invalid (null), but will
|
||||
// be fixed by nr_local_addr_copy.
|
||||
if (nr_local_addr_copy(localAddr_, from_addr)) {
|
||||
|
|
|
|||
|
|
@ -334,6 +334,10 @@ RefPtr<GenericPromise> RemoteWorkerController::SetServiceWorkerSkipWaitingFlag()
|
|||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(mObserver);
|
||||
|
||||
if (!mIsServiceWorker) {
|
||||
return GenericPromise::CreateAndResolve(false, __func__);
|
||||
}
|
||||
|
||||
RefPtr<GenericPromise::Private> promise =
|
||||
new GenericPromise::Private(__func__);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue