import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1236120: [ffmpeg] Use demuxed dimensions to determine picture size and offset. r=gerald (f336c84d0d)
- Bug 851530: Part 1 - Added support for decoding uLaw and aLaw enconded wave files. r=jya (72683b69f2)
- Bug 851530: Part 2 - Added test cases for uLaw and aLaw wave files. r=jya (ddf431bd28)
- Bug 524109 - Added support for 24 bit wav files. r=cpearce (ebfac16a20)
- Bug 864780 - Changed handling of the format chunk to skip any extension. r=cpearce (40903839a8)
- Bug 1229742: P1. Only clear EOS flag if we have new data. r=gerald (83e69375e4)
- Bug 1229742: P2. Don't reject data promise if new data is pending. r=gerald (89d8222809)
- Bug 1237809: P2. Detect change of display size. r=cpearce (ec529e2732)
- Bug 1237809: [h264] P1. Ensure correct video dimensions are passed to the decoder. r=cpearce (eb241cad10)
- Bug 1237809: P3. Ensure element dimensions are up to date. r=jwwang (541e6e74d7)
- Bug 1244639: P1. Don't assume MP3 decoding always starts at 0. r=cpearce (b62c27bd04)
- Bug 1244639: P2. Don't clamp audio time to seek time if there's no video track. r=cpearce (4720bacc24)
- Bug 1205927 - Part 1: [MediaEncoder] Support *.3g2 with EVRC audio format. r=ayang (bbd8aff9d9)
- Bug 1205927 - Part 2: Add audio-capture:3gpp2 perimission for certificated and privileged application. r=ayang (63b337e2ab)
- Bug 1198157 - Call |NotifyEndOfStream| if the encoder can't been initialized after 30 seconds. r=jwwang (2c21203d36)
- minor NL (14d757753b)
- Bug 1182426 - Add some asserts to VP8TrackEncoder for sanity. r=roc (79ff4869ca)
- Bug 1154213 - Handle timestamps of video/webm vorbis track encoding. r=mreavy, r=rjesup (a9897e4873)
- Bug 1137151: Marked destructor of |MuxerOperation| as protected, r=sotaro (94fdcf6457)
- Bug 1210232 - Let MP4Decoder handle 3GPP files on B2G. r=cpearce (95a77023ad)
- Bug 1227790 - Update GMP API to include new MediaKeyStatus types. r=jwwang (1a6933f2df)
- bits of Bug 1186375 - Add GMP EME (6b99660146)
- Bug 1221825: Fix logging arguments. r=cpearce (5261e34713)
- Bug 1244442 - Warn about Proxy.create and Proxy.createFunction. r=Waldo (718aa94f5b)
- fix spaces (720e2114f7)
- re-apply Bug 1231224 part 7 - Fix some more places to handle OOM. r=jonco (a4af46894b)
- Bug 1246122 - Don't crash in InvokeInterruptCallback if there are no JS scripts on the stack. r=shu (9702df89bc)
- Bug 1246607: Recover from OOM in AddClearDefiniteGetterSetterForPrototypeChain; r=jandem (d83c6c6c9b)
- Bug 1236546 - Don't deoptimize in ObjectGroup::defaultNewGroup when we have a null proto. r=bhackett (1c2ecc3d09)
- Bug 1249588 - Remove unnecessary type information from RegExpObject. r=jandem (9126e17d94)
- Bug 1245965 - Fix an OOM in ObjectGroup::newPlainObject; r=till (5192c25b53)
- Bug 1240527: Fix tracing of RegExpStaticsObject; r=nbp (b37f2167a5)
- Bug 1248094 - Followup to fix a typo; r=fitzgen (8afec429d9)
- Bug 1248726 - Simplify PCLocationMap even further; r=fitzgen (39f0b54a04)
- Bug 1241311 - Pre-tenure SavedFrame objects. r=terrence (b703f3d78e)
- Bug 1241249 - Add an SPS pseudo entry for JS stack capturing; r=shu (c2ae4ee5c2)
- Bug 1247299 - Force SavedFrame columns to be 0 in JS_MORE_DETERMINISTIC builds; r=sfink (09b9038448)
- Bug 1241701 - Add about:memory reporting for js::SavedStacks::pcLocationMap. r=njn (b663d911fc)
- Bug 1166234 - Throw on accessing optimized out values when using Debugger.Frame.prototype.eval. (r=jimb) (19b43b137b)
- Bug 1232655 - Fix DebugScopeProxy::has to not lookup .this on non-function scopes. r=shu (3959e98752)
- Bug 1216261 - Fix OOM handling of DebugScopes. (r=jonco) (0f8b856ee6)
- reorder after mispatch (c292050275)
- Bug 1235656 - Followup: Allow extended functions with guessed atoms in self-hosted code. (rs=arai) (a67286cd52)
- Bug 1245048: Check call to GetPrototype; r=till (35dbbdc025)
- Bug 1132630 - Renumber steps in Function.prototype.bind. r=till (9f11a5a086)
- Bug 1246131 - Provide 'dbg(msg)' debug printing utility function for self-hosted code. r=jandem (f436eeb481)
- Bug 1246131 - Part 2: Let opt builds compile again, even on a CLOSED TREE. r=bustage (8483b77541)
- Bug 1247934 - Handle receiving unboxed exports array from self hosted module code r=shu (fb9c296909)
- Bug 1246134 - Fix loading of external self-hosted JS using MOZ_SELFHOSTEDJS. r=efaust (c96059b40a)
- Bug 1220502 - ignore not visible text nodes for tree update, r=tbsaunde, roc (eed078abc6)
- Bug 1242989 - keep content insertions in a hash, r=tbsaunde (d58fc948a6)
- bug 1228400 - null check tabChild before notifying the parent process about new child documents r=davidb (09512e6287)
- Bug 1239051 - Labels should expose labeled controllers action. r=tbsaunde (c0d4d801a9)
- bug 1243077 - make xpcAccessible::GetFirstChild() work with proxies r=davidb (f539fafe93)
- bug 1243077 - make xpcAccessible::GetLastChild() work with proxied accessibles r=davidb (13716f7cc0)
- bug 1243077 - implement xpcAccessible::GetChildCount() for proxied accessibles (b8f4598834)
- bug 1243077 - make xpcAccessible::GetChildAt() work with proxied accessibles r=davidb (cce0924f7b)
- bug 1243077 - make xpcAccessible::GetChildren() work with proxied accessibles r=davidb (a9ec2b1588)
- bug 1243077 - remove an unnecessary AddRef() from xpcAccessible::GetChildren() (89a58ac2e0)
- bug 1243077 - support proxied accessibles in xpcAccessible::GetRole() r=davidb (2d2a2926c7)
- Bug 1246768 - part 1: argument conversion for Atomics.isLockFree in runtime. r=bbouvier (17f3498b84)
- Bug 1246750 - fix argument ordering to futexWakeOrRequeue + test cases. r=bbouvier (31825e7096)
- Bug 1238911 - initialize canWait with false from constructor, avoid using the variable without initialization. r=lhansen (f4657b3950)
- Bug 1235373 - Add an assert to check validity of pointers: mElement->GetPrimaryFrame() and frame. r=surkov (18023f9238)
- Bug 1241534 - Use TraceRoot for InterpreterFrame fields. r=terrence (98996dc497)
- Bug 1246112 - Fix a bogus assert in InterpreterFrame::initExecuteFrame. r=su (2093ba8a44)
- Bug 1243241 - Make RDTSC monotonic. r=jandem (1450a97a94)
- Bug 1243242 - Don't make structured cloning O(n**2) in the size of the transferables array. r=sfink (aa38dee282)
This commit is contained in:
2023-12-04 22:00:13 +08:00
parent 5d8f4a0ceb
commit 605fde2bb1
102 changed files with 1395 additions and 443 deletions
+39 -67
View File
@@ -52,7 +52,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationController)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHangingChildDocuments)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentInsertions)
for (auto it = tmp->mContentInsertions.ConstIter(); !it.Done(); it.Next()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContentInsertions key");
cb.NoteXPCOMChild(it.Key());
nsTArray<nsCOMPtr<nsIContent>>* list = it.UserData();
for (uint32_t i = 0; i < list->Length(); i++) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
"mContentInsertions value item");
cb.NoteXPCOMChild(list->ElementAt(i));
}
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvents)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelocations)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@@ -103,10 +112,23 @@ NotificationController::ScheduleContentInsertion(Accessible* aContainer,
nsIContent* aStartChildNode,
nsIContent* aEndChildNode)
{
RefPtr<ContentInsertion> insertion = new ContentInsertion(mDocument,
aContainer);
if (insertion && insertion->InitChildList(aStartChildNode, aEndChildNode) &&
mContentInsertions.AppendElement(insertion)) {
nsTArray<nsCOMPtr<nsIContent>>* list =
mContentInsertions.LookupOrAdd(aContainer);
bool needsProcessing = false;
nsIContent* node = aStartChildNode;
while (node != aEndChildNode) {
// Notification triggers for content insertion even if no content was
// actually inserted, check if the given content has a frame to discard
// this case early.
if (node->GetPrimaryFrame()) {
if (list->AppendElement(node))
needsProcessing = true;
}
node = node->GetNextSibling();
}
if (needsProcessing) {
ScheduleProcessing();
}
}
@@ -130,7 +152,7 @@ NotificationController::IsUpdatePending()
{
return mPresShell->IsLayoutFlushObserver() ||
mObservingState == eRefreshProcessingForUpdate ||
mContentInsertions.Length() != 0 || mNotifications.Length() != 0 ||
mContentInsertions.Count() != 0 || mNotifications.Length() != 0 ||
mTextHash.Count() != 0 ||
!mDocument->HasLoadState(DocAccessible::eTreeConstructed);
}
@@ -178,7 +200,7 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
mDocument->DoInitialUpdate();
NS_ASSERTION(mContentInsertions.Length() == 0,
NS_ASSERTION(mContentInsertions.Count() == 0,
"Pending content insertions while initial accessible tree isn't created!");
}
@@ -196,15 +218,13 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
// document accessible.
// Process only currently queued content inserted notifications.
nsTArray<RefPtr<ContentInsertion> > contentInsertions;
contentInsertions.SwapElements(mContentInsertions);
uint32_t insertionCount = contentInsertions.Length();
for (uint32_t idx = 0; idx < insertionCount; idx++) {
contentInsertions[idx]->Process();
if (!mDocument)
for (auto iter = mContentInsertions.ConstIter(); !iter.Done(); iter.Next()) {
mDocument->ProcessContentInserted(iter.Key(), iter.UserData());
if (!mDocument) {
return;
}
}
mContentInsertions.Clear();
// Process rendered text change notifications.
for (auto iter = mTextHash.Iter(); !iter.Done(); iter.Next()) {
@@ -390,8 +410,10 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
childDoc->SetIPCDoc(ipcDoc);
nsCOMPtr<nsITabChild> tabChild =
do_GetInterface(mDocument->DocumentNode()->GetDocShell());
static_cast<TabChild*>(tabChild.get())->
SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id);
if (tabChild) {
static_cast<TabChild*>(tabChild.get())->
SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id);
}
}
}
@@ -401,7 +423,7 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
// Stop further processing if there are no new notifications of any kind or
// events and document load is processed.
if (mContentInsertions.IsEmpty() && mNotifications.IsEmpty() &&
if (mContentInsertions.Count() == 0 && mNotifications.IsEmpty() &&
mEvents.IsEmpty() && mTextHash.Count() == 0 &&
mHangingChildDocuments.IsEmpty() &&
mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
@@ -409,53 +431,3 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
mObservingState = eNotObservingRefresh;
}
}
////////////////////////////////////////////////////////////////////////////////
// NotificationController: content inserted notification
NotificationController::ContentInsertion::
ContentInsertion(DocAccessible* aDocument, Accessible* aContainer) :
mDocument(aDocument), mContainer(aContainer)
{
}
bool
NotificationController::ContentInsertion::
InitChildList(nsIContent* aStartChildNode, nsIContent* aEndChildNode)
{
bool haveToUpdate = false;
nsIContent* node = aStartChildNode;
while (node != aEndChildNode) {
// Notification triggers for content insertion even if no content was
// actually inserted, check if the given content has a frame to discard
// this case early.
if (node->GetPrimaryFrame()) {
if (mInsertedContent.AppendElement(node))
haveToUpdate = true;
}
node = node->GetNextSibling();
}
return haveToUpdate;
}
NS_IMPL_CYCLE_COLLECTION(NotificationController::ContentInsertion,
mContainer)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController::ContentInsertion,
AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(NotificationController::ContentInsertion,
Release)
void
NotificationController::ContentInsertion::Process()
{
mDocument->ProcessContentInserted(mContainer, &mInsertedContent);
mDocument = nullptr;
mContainer = nullptr;
mInsertedContent.Clear();
}
+13 -40
View File
@@ -122,8 +122,15 @@ public:
*/
inline void ScheduleTextUpdate(nsIContent* aTextNode)
{
if (mTextHash.PutEntry(aTextNode))
ScheduleProcessing();
// Make sure we are not called with a node that is not in the DOM tree or
// not visible.
MOZ_ASSERT(aTextNode->GetParentNode(), "A text node is not in DOM");
MOZ_ASSERT(aTextNode->GetPrimaryFrame(), "A text node doesn't have a frame");
MOZ_ASSERT(aTextNode->GetPrimaryFrame()->StyleVisibility()->IsVisible(),
"A text node is not visible");
mTextHash.PutEntry(aTextNode);
ScheduleProcessing();
}
/**
@@ -240,44 +247,10 @@ private:
nsTArray<RefPtr<DocAccessible> > mHangingChildDocuments;
/**
* Storage for content inserted notification information.
* Pending accessible tree update notifications for content insertions.
*/
class ContentInsertion
{
public:
ContentInsertion(DocAccessible* aDocument, Accessible* aContainer);
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(ContentInsertion)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ContentInsertion)
bool InitChildList(nsIContent* aStartChildNode, nsIContent* aEndChildNode);
void Process();
protected:
virtual ~ContentInsertion() { mDocument = nullptr; }
private:
ContentInsertion();
ContentInsertion(const ContentInsertion&);
ContentInsertion& operator = (const ContentInsertion&);
// The document used to process content insertion, matched to document of
// the notification controller that this notification belongs to, therefore
// it's ok to keep it as weak ref.
DocAccessible* mDocument;
// The container accessible that content insertion occurs within.
RefPtr<Accessible> mContainer;
// Array of inserted contents.
nsTArray<nsCOMPtr<nsIContent> > mInsertedContent;
};
/**
* A pending accessible tree update notifications for content insertions.
* Don't make this an AutoTArray; we use SwapElements() on it.
*/
nsTArray<RefPtr<ContentInsertion> > mContentInsertions;
nsClassHashtable<nsRefPtrHashKey<Accessible>,
nsTArray<nsCOMPtr<nsIContent>>> mContentInsertions;
template<class T>
class nsCOMPtrHashKey : public PLDHashEntryHdr
@@ -304,7 +277,7 @@ private:
};
/**
* A pending accessible tree update notifications for rendered text changes.
* Pending accessible tree update notifications for rendered text changes.
*/
nsTHashtable<nsCOMPtrHashKey<nsIContent> > mTextHash;
+2
View File
@@ -58,6 +58,7 @@ StyleInfo::TextIndent(nsAString& aValue)
case eStyleUnit_Percent:
{
nsIFrame* frame = mElement->GetPrimaryFrame();
MOZ_ASSERT(frame, "frame must be a valid pointer.");
nsIFrame* containerFrame = frame->GetContainingBlock();
nscoord percentageBase = containerFrame->GetContentRect().width;
coordVal = NSCoordSaturatingMultiply(percentageBase,
@@ -88,6 +89,7 @@ StyleInfo::TextIndent(nsAString& aValue)
void
StyleInfo::Margin(css::Side aSide, nsAString& aValue)
{
MOZ_ASSERT(mElement->GetPrimaryFrame(), " mElement->GetPrimaryFrame() needs to be valid pointer");
aValue.Truncate();
nscoord coordVal = mElement->GetPrimaryFrame()->GetUsedMargin().Side(aSide);
+11
View File
@@ -36,6 +36,7 @@
#include "nsITreeBoxObject.h"
#include "nsITreeColumns.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLLabelElement.h"
using namespace mozilla;
@@ -43,6 +44,16 @@ using namespace mozilla;
// nsCoreUtils
////////////////////////////////////////////////////////////////////////////////
bool
nsCoreUtils::IsLabelWithControl(nsIContent* aContent)
{
dom::HTMLLabelElement* label = dom::HTMLLabelElement::FromContent(aContent);
if (label && label->GetControl())
return true;
return false;
}
bool
nsCoreUtils::HasClickListener(nsIContent *aContent)
{
+5
View File
@@ -29,6 +29,11 @@ class nsIWidget;
class nsCoreUtils
{
public:
/**
* Return true if the given node is a label of a control.
*/
static bool IsLabelWithControl(nsIContent *aContent);
/**
* Return true if the given node has registered click, mousedown or mouseup
* event listeners.
+18 -7
View File
@@ -115,13 +115,14 @@ LinkableAccessible::Value(nsString& aValue)
uint8_t
LinkableAccessible::ActionCount()
{
bool isLink, isOnclick;
ActionWalk(&isLink, &isOnclick);
return (isLink || isOnclick) ? 1 : 0;
bool isLink, isOnclick, isLabelWithControl;
ActionWalk(&isLink, &isOnclick, &isLabelWithControl);
return (isLink || isOnclick || isLabelWithControl) ? 1 : 0;
}
Accessible*
LinkableAccessible::ActionWalk(bool* aIsLink, bool* aIsOnclick)
LinkableAccessible::ActionWalk(bool* aIsLink, bool* aIsOnclick,
bool* aIsLabelWithControl)
{
if (aIsOnclick) {
*aIsOnclick = false;
@@ -129,6 +130,9 @@ LinkableAccessible::ActionWalk(bool* aIsLink, bool* aIsOnclick)
if (aIsLink) {
*aIsLink = false;
}
if (aIsLabelWithControl) {
*aIsLabelWithControl = false;
}
if (nsCoreUtils::HasClickListener(mContent)) {
if (aIsOnclick) {
@@ -155,6 +159,13 @@ LinkableAccessible::ActionWalk(bool* aIsLink, bool* aIsOnclick)
}
return walkUpAcc;
}
if (nsCoreUtils::IsLabelWithControl(walkUpAcc->GetContent())) {
if (aIsLabelWithControl) {
*aIsLabelWithControl = true;
}
return walkUpAcc;
}
}
return nullptr;
}
@@ -166,11 +177,11 @@ LinkableAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
// Action 0 (default action): Jump to link
if (aIndex == eAction_Jump) {
bool isOnclick, isLink;
ActionWalk(&isLink, &isOnclick);
bool isOnclick, isLink, isLabelWithControl;
ActionWalk(&isLink, &isOnclick, &isLabelWithControl);
if (isLink) {
aName.AssignLiteral("jump");
} else if (isOnclick) {
} else if (isOnclick || isLabelWithControl) {
aName.AssignLiteral("click");
}
}
+2 -1
View File
@@ -76,7 +76,8 @@ public:
// ActionAccessible helpers
Accessible* ActionWalk(bool* aIsLink = nullptr,
bool* aIsOnclick = nullptr);
bool* aIsOnclick = nullptr,
bool* aIsLabelWithControl = nullptr);
// HyperLinkAccessible
virtual already_AddRefed<nsIURI> AnchorURIAt(uint32_t aAnchorIndex) override;
@@ -75,6 +75,32 @@ HTMLLabelAccessible::RelationByType(RelationType aType)
return rel;
}
uint8_t
HTMLLabelAccessible::ActionCount()
{
return nsCoreUtils::IsLabelWithControl(mContent) ? 1 : 0;
}
void
HTMLLabelAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
{
if (aIndex == 0) {
if (nsCoreUtils::IsLabelWithControl(mContent))
aName.AssignLiteral("click");
}
}
bool
HTMLLabelAccessible::DoAction(uint8_t aIndex)
{
if (aIndex != 0)
return false;
DoCommand();
return true;
}
////////////////////////////////////////////////////////////////////////////////
// nsHTMLOuputAccessible
////////////////////////////////////////////////////////////////////////////////
+5
View File
@@ -62,6 +62,11 @@ public:
// Accessible
virtual Relation RelationByType(RelationType aType) override;
// ActionAccessible
virtual uint8_t ActionCount() override;
virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
virtual bool DoAction(uint8_t aIndex) override;
protected:
virtual ~HTMLLabelAccessible() {}
virtual ENameValueFlag NativeName(nsString& aName) override;
@@ -39,11 +39,19 @@
ID: "onclick_img",
actionName: "click",
events: CLICK_EVENTS
},
{
ID: "label1",
actionName: "click",
events: CLICK_EVENTS
}
];
testActions(actionsArray);
is(getAccessible("label1").firstChild.actionCount, 1, "label text should have 1 action");
getAccessible("onclick_img").takeFocus();
is(getAccessible("link1").actionCount, 1, "links should have one action");
is(getAccessible("link2").actionCount, 1, "link with onclick handler should have 1 action");
@@ -87,5 +95,13 @@
<a id="link1" href="www">linkable textleaf accessible</a>
<div id="link2" onclick="">linkable textleaf accessible</div>
<div>
<label for="TextBox_t2" id="label1">
<span>Explicit</span>
</label>
<input name="in2" id="TextBox_t2" type="text" maxlength="17">
</div>
</body>
</html>
@@ -60,6 +60,11 @@
actionName: "press",
events: CLICK_EVENTS
},
{
ID: "name_entry_label",
actionName: "click",
events: CLICK_EVENTS
},
{
ID: "labelWithPopup",
actionName: "click",
@@ -72,6 +77,8 @@
}*/
];
is(getAccessible("name_entry_label").firstChild.actionCount, 1, "label text should have 1 action");
testActions(actionsArray);
}
@@ -125,6 +132,10 @@
<label id="labelWithPopup" value="file name"
popup="fileContext"
tabindex="0"/>
<hbox>
<label id="name_entry_label" value="Name" control="name_entry"/>
<textbox id="name_entry"/>
</hbox>
</vbox>
</hbox>
</window>
+16 -16
View File
@@ -65,10 +65,10 @@ xpcAccessible::GetFirstChild(nsIAccessible** aFirstChild)
NS_ENSURE_ARG_POINTER(aFirstChild);
*aFirstChild = nullptr;
if (!Intl())
if (IntlGeneric().IsNull())
return NS_ERROR_FAILURE;
NS_IF_ADDREF(*aFirstChild = ToXPC(Intl()->FirstChild()));
NS_IF_ADDREF(*aFirstChild = ToXPC(IntlGeneric().FirstChild()));
return NS_OK;
}
@@ -78,10 +78,10 @@ xpcAccessible::GetLastChild(nsIAccessible** aLastChild)
NS_ENSURE_ARG_POINTER(aLastChild);
*aLastChild = nullptr;
if (!Intl())
if (IntlGeneric().IsNull())
return NS_ERROR_FAILURE;
NS_IF_ADDREF(*aLastChild = ToXPC(Intl()->LastChild()));
NS_IF_ADDREF(*aLastChild = ToXPC(IntlGeneric().LastChild()));
return NS_OK;
}
@@ -90,10 +90,10 @@ xpcAccessible::GetChildCount(int32_t* aChildCount)
{
NS_ENSURE_ARG_POINTER(aChildCount);
if (!Intl())
if (IntlGeneric().IsNull())
return NS_ERROR_FAILURE;
*aChildCount = Intl()->ChildCount();
*aChildCount = IntlGeneric().ChildCount();
return NS_OK;
}
@@ -103,16 +103,16 @@ xpcAccessible::GetChildAt(int32_t aChildIndex, nsIAccessible** aChild)
NS_ENSURE_ARG_POINTER(aChild);
*aChild = nullptr;
if (!Intl())
if (IntlGeneric().IsNull())
return NS_ERROR_FAILURE;
// If child index is negative, then return last child.
// XXX: do we really need this?
if (aChildIndex < 0)
aChildIndex = Intl()->ChildCount() - 1;
aChildIndex = IntlGeneric().ChildCount() - 1;
Accessible* child = Intl()->GetChildAt(aChildIndex);
if (!child)
AccessibleOrProxy child = IntlGeneric().ChildAt(aChildIndex);
if (child.IsNull())
return NS_ERROR_INVALID_ARG;
NS_ADDREF(*aChild = ToXPC(child));
@@ -125,7 +125,7 @@ xpcAccessible::GetChildren(nsIArray** aChildren)
NS_ENSURE_ARG_POINTER(aChildren);
*aChildren = nullptr;
if (!Intl())
if (IntlGeneric().IsNull())
return NS_ERROR_FAILURE;
nsresult rv = NS_OK;
@@ -133,13 +133,13 @@ xpcAccessible::GetChildren(nsIArray** aChildren)
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t childCount = Intl()->ChildCount();
uint32_t childCount = IntlGeneric().ChildCount();
for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
Accessible* child = Intl()->GetChildAt(childIdx);
AccessibleOrProxy child = IntlGeneric().ChildAt(childIdx);
children->AppendElement(static_cast<nsIAccessible*>(ToXPC(child)), false);
}
NS_ADDREF(*aChildren = children);
children.forget(aChildren);
return NS_OK;
}
@@ -204,10 +204,10 @@ xpcAccessible::GetRole(uint32_t* aRole)
NS_ENSURE_ARG_POINTER(aRole);
*aRole = nsIAccessibleRole::ROLE_NOTHING;
if (!Intl())
if (IntlGeneric().IsNull())
return NS_ERROR_FAILURE;
*aRole = Intl()->Role();
*aRole = IntlGeneric().Role();
return NS_OK;
}
+5
View File
@@ -375,6 +375,11 @@ this.PermissionsTable = { geolocation: {
privileged: ALLOW_ACTION,
certified: ALLOW_ACTION
},
"audio-capture:3gpp2": {
app: DENY_ACTION,
privileged: ALLOW_ACTION,
certified: ALLOW_ACTION
},
"nfc": {
app: DENY_ACTION,
privileged: ALLOW_ACTION,
+3 -1
View File
@@ -138,8 +138,10 @@ static const char* const gWaveTypes[5] = {
nullptr
};
static char const *const gWaveCodecs[2] = {
static char const *const gWaveCodecs[4] = {
"1", // Microsoft PCM Format
"6", // aLaw Encoding
"7", // uLaw Encoding
nullptr
};
-5
View File
@@ -29,11 +29,6 @@ public:
bool IsSeekable() const override;
void NotifyDataArrived() override;
void NotifyDataRemoved() override;
// Do not shift the calculated buffered range by the start time of the first
// decoded frame. The mac MP3 decoder will buffer some samples and the first
// frame returned has typically a start time that is non-zero, causing our
// buffered range to have a negative start time.
bool ShouldComputeStartTime() const override { return false; }
private:
// Synchronous initialization.
+6 -1
View File
@@ -869,11 +869,16 @@ MediaDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
// Make sure the element and the frame (if any) are told about
// our new size.
Invalidate();
if (aEventVisibility != MediaDecoderEventVisibility::Suppressed) {
mFiredMetadataLoaded = true;
mOwner->MetadataLoaded(mInfo, nsAutoPtr<const MetadataTags>(aTags.forget()));
}
// Invalidate() will end up calling mOwner->UpdateMediaSize with the last
// dimensions retrieved from the video frame container. The video frame
// container contains more up to date dimensions than aInfo.
// So we call Invalidate() after calling mOwner->MetadataLoaded to ensure
// the media element has the latest dimensions.
Invalidate();
EnsureTelemetryReported();
}
+3 -5
View File
@@ -2081,11 +2081,9 @@ MediaDecoderStateMachine::SeekCompleted()
// seekTime is bounded in suitable duration. See Bug 1112438.
int64_t audioStart = audio ? audio->mTime : seekTime;
// We only pin the seek time to the video start time if the video frame
// contains the seek time. We also perform this operation if there's no
// video in order to get around bug 1244639.
if (!video || (video->mTime <= seekTime && video->GetEndTime() > seekTime)) {
int64_t videoStart = video ? video->mTime : seekTime;
newCurrentTime = std::min(audioStart, videoStart);
// contains the seek time.
if (video && video->mTime <= seekTime && video->GetEndTime() > seekTime) {
newCurrentTime = std::min(audioStart, video->mTime);
} else {
newCurrentTime = audioStart;
}
+21 -7
View File
@@ -795,7 +795,7 @@ MediaFormatReader::UpdateReceivedNewData(TrackType aTrack)
bool hasLastEnd;
media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd);
if (hasLastEnd) {
if (decoder.mLastTimeRangesEnd && decoder.mLastTimeRangesEnd.ref() > lastEnd) {
if (decoder.mLastTimeRangesEnd && decoder.mLastTimeRangesEnd.ref() < lastEnd) {
// New data was added after our previous end, we can clear the EOS flag.
decoder.mDemuxEOS = false;
}
@@ -1115,8 +1115,10 @@ MediaFormatReader::Update(TrackType aTrack)
decoder.mLastSampleTime.ref().ToMicroseconds());
InternalSeek(aTrack, InternalSeekTarget(decoder.mLastSampleTime.ref(), true));
}
LOG("Rejecting %s promise: WAITING_FOR_DATA", TrackTypeToStr(aTrack));
decoder.RejectPromise(WAITING_FOR_DATA, __func__);
if (!decoder.mReceivedNewData) {
LOG("Rejecting %s promise: WAITING_FOR_DATA", TrackTypeToStr(aTrack));
decoder.RejectPromise(WAITING_FOR_DATA, __func__);
}
}
// Now that draining has completed, we check if we have received
// new data again as the result may now be different from the earlier
@@ -1186,6 +1188,16 @@ MediaFormatReader::ReturnOutput(MediaData* aData, TrackType aTrack)
}
mAudio.mPromise.Resolve(aData, __func__);
} else if (aTrack == TrackInfo::kVideoTrack) {
if (aData->mType != MediaData::RAW_DATA) {
VideoData* videoData = static_cast<VideoData*>(aData);
if (videoData->mDisplay != mInfo.mVideo.mDisplay) {
LOG("change of video display size (%dx%d->%dx%d)",
mInfo.mVideo.mDisplay.width, mInfo.mVideo.mDisplay.height,
videoData->mDisplay.width, videoData->mDisplay.height);
mInfo.mVideo.mDisplay = videoData->mDisplay;
}
}
mVideo.mPromise.Resolve(aData, __func__);
}
LOG("Resolved data promise for %s", TrackTypeToStr(aTrack));
@@ -1562,15 +1574,12 @@ MediaFormatReader::GetBuffered()
if (!mInitDone) {
return intervals;
}
int64_t startTime;
int64_t startTime = 0;
if (!ForceZeroStartTime()) {
if (!HaveStartTime()) {
return intervals;
}
startTime = StartTime();
} else {
// MSE, start time is assumed to be 0 we can proceeed with what we have.
startTime = 0;
}
// Ensure we have up to date buffered time range.
if (HasVideo()) {
@@ -1593,6 +1602,11 @@ MediaFormatReader::GetBuffered()
intervals = Move(videoti);
}
if (!intervals.Length() ||
intervals.GetStart() == media::TimeUnit::FromMicroseconds(0)) {
// IntervalSet already starts at 0 or is empty, nothing to shift.
return intervals;
}
return intervals.Shift(media::TimeUnit::FromMicroseconds(-startTime));
}
+9 -3
View File
@@ -553,7 +553,7 @@ private:
}
}
bool Check3gppPermission()
bool CheckPermission(const char* aType)
{
nsCOMPtr<nsIDocument> doc = mRecorder->GetOwner()->GetExtantDoc();
if (!doc) {
@@ -576,7 +576,7 @@ private:
}
uint32_t perm = nsIPermissionManager::DENY_ACTION;
pm->TestExactPermissionFromPrincipal(doc->NodePrincipal(), "audio-capture:3gpp", &perm);
pm->TestExactPermissionFromPrincipal(doc->NodePrincipal(), aType, &perm);
return perm == nsIPermissionManager::ALLOW_ACTION;
}
@@ -589,12 +589,18 @@ private:
// At this stage, the API doesn't allow UA to choose the output mimeType format.
// Make sure the application has permission to assign AUDIO_3GPP
if (mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP) && Check3gppPermission()) {
if (mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP) && CheckPermission("audio-capture:3gpp")) {
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP),
mRecorder->GetAudioBitrate(),
mRecorder->GetVideoBitrate(),
mRecorder->GetBitrate(),
aTrackTypes);
} else if (mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP2) && CheckPermission("audio-capture:3gpp2")) {
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP2),
mRecorder->GetAudioBitrate(),
mRecorder->GetVideoBitrate(),
mRecorder->GetBitrate(),
aTrackTypes);
} else {
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""),
mRecorder->GetAudioBitrate(),
+1 -1
View File
@@ -159,7 +159,7 @@ private:
void RegisterActivityObserver();
void UnRegisterActivityObserver();
bool Check3gppPermission();
bool CheckPermission(const nsString &aType);
};
} // namespace dom
@@ -60,6 +60,8 @@ public:
AAC_CSD, // AAC codec specific data
AMR_AUDIO_CSD,
AMR_AUDIO_FRAME,
EVRC_AUDIO_CSD,
EVRC_AUDIO_FRAME,
UNKNOWN // FrameType not set
};
void SwapInFrameData(nsTArray<uint8_t>& aData)
+8
View File
@@ -127,6 +127,14 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint32_t aAudioBitrate,
writer = new ISOMediaWriter(aTrackTypes, ISOMediaWriter::TYPE_FRAG_3GP);
NS_ENSURE_TRUE(writer, nullptr);
mimeType = NS_LITERAL_STRING(AUDIO_3GPP);
} else if (MediaEncoder::IsOMXEncoderEnabled() &&
(aMIMEType.EqualsLiteral(AUDIO_3GPP2))) {
audioEncoder = new OmxEVRCAudioTrackEncoder();
NS_ENSURE_TRUE(audioEncoder, nullptr);
writer = new ISOMediaWriter(aTrackTypes, ISOMediaWriter::TYPE_FRAG_3G2);
NS_ENSURE_TRUE(writer, nullptr);
mimeType = NS_LITERAL_STRING(AUDIO_3GPP2) ;
}
#endif // MOZ_OMX_ENCODER
else if (MediaDecoder::IsOggEnabled() && MediaDecoder::IsOpusEnabled() &&
+44 -1
View File
@@ -184,7 +184,7 @@ OmxAudioTrackEncoder::AppendEncodedFrames(EncodedFrameContainer& aContainer)
3000); // wait up to 3ms
NS_ENSURE_SUCCESS(rv, rv);
if (!frameData.IsEmpty()) {
if (!frameData.IsEmpty() || outFlags & OMXCodecWrapper::BUFFER_EOS) { // Some hw codec may send out EOS with an empty frame
bool isCSD = false;
if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) { // codec specific data
isCSD = true;
@@ -199,6 +199,9 @@ OmxAudioTrackEncoder::AppendEncodedFrames(EncodedFrameContainer& aContainer)
} else if (mEncoder->GetCodecType() == OMXCodecWrapper::AMR_NB_ENC){
audiodata->SetFrameType(isCSD ?
EncodedFrame::AMR_AUDIO_CSD : EncodedFrame::AMR_AUDIO_FRAME);
} else if (mEncoder->GetCodecType() == OMXCodecWrapper::EVRC_ENC){
audiodata->SetFrameType(isCSD ?
EncodedFrame::EVRC_AUDIO_CSD : EncodedFrame::EVRC_AUDIO_FRAME);
} else {
MOZ_ASSERT(false, "audio codec not supported");
}
@@ -343,4 +346,44 @@ OmxAMRAudioTrackEncoder::GetMetadata()
return meta.forget();
}
nsresult
OmxEVRCAudioTrackEncoder::Init(int aChannels, int aSamplingRate)
{
mChannels = aChannels;
mSamplingRate = aSamplingRate;
mEncoder = OMXCodecWrapper::CreateEVRCEncoder();
NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, EVRC_SAMPLERATE);
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mInitialized = (rv == NS_OK);
mReentrantMonitor.NotifyAll();
return NS_OK;
}
already_AddRefed<TrackMetadataBase>
OmxEVRCAudioTrackEncoder::GetMetadata()
{
PROFILER_LABEL("OmxEVRCAudioTrackEncoder", "GetMetadata",
js::ProfileEntry::Category::OTHER);
{
// Wait if mEncoder is not initialized nor is being canceled.
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
while (!mCanceled && !mInitialized) {
mReentrantMonitor.Wait();
}
}
if (mCanceled || mEncodingComplete) {
return nullptr;
}
RefPtr<EVRCTrackMetadata> meta = new EVRCTrackMetadata();
meta->mChannels = mChannels;
return meta.forget();
}
}
+16
View File
@@ -90,5 +90,21 @@ protected:
nsresult Init(int aChannels, int aSamplingRate) override;
};
class OmxEVRCAudioTrackEncoder final : public OmxAudioTrackEncoder
{
public:
OmxEVRCAudioTrackEncoder()
: OmxAudioTrackEncoder()
{}
enum {
EVRC_SAMPLERATE = 8000,
};
already_AddRefed<TrackMetadataBase> GetMetadata() override;
protected:
nsresult Init(int aChannels, int aSamplingRate) override;
};
}
#endif
+1
View File
@@ -187,6 +187,7 @@ OpusTrackEncoder::Init(int aChannels, int aSamplingRate)
mEncoder = opus_encoder_create(GetOutputSampleRate(), mChannels,
OPUS_APPLICATION_AUDIO, &error);
mInitialized = (error == OPUS_OK);
if (mAudioBitrate) {
+26 -6
View File
@@ -26,6 +26,8 @@ static const int DEFAULT_SAMPLING_RATE = 16000;
static const int DEFAULT_FRAME_WIDTH = 640;
static const int DEFAULT_FRAME_HEIGHT = 480;
static const int DEFAULT_TRACK_RATE = USECS_PER_S;
// 30 seconds threshold if the encoder still can't not be initialized.
static const int INIT_FAILED_DURATION = 30;
TrackEncoder::TrackEncoder()
: mReentrantMonitor("media.TrackEncoder")
@@ -34,8 +36,8 @@ TrackEncoder::TrackEncoder()
, mInitialized(false)
, mEndOfStream(false)
, mCanceled(false)
, mAudioInitCounter(0)
, mVideoInitCounter(0)
, mInitCounter(0)
, mNotInitDuration(0)
{
}
@@ -54,8 +56,8 @@ AudioTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
// Check and initialize parameters for codec encoder.
if (!mInitialized) {
mAudioInitCounter++;
TRACK_LOG(LogLevel::Debug, ("Init the audio encoder %d times", mAudioInitCounter));
mInitCounter++;
TRACK_LOG(LogLevel::Debug, ("Init the audio encoder %d times", mInitCounter));
AudioSegment::ChunkIterator iter(const_cast<AudioSegment&>(audio));
while (!iter.IsEnded()) {
AudioChunk chunk = *iter;
@@ -73,6 +75,15 @@ AudioTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
iter.Next();
}
mNotInitDuration += aQueuedMedia.GetDuration();
if (!mInitialized &&
(mNotInitDuration / aGraph->GraphRate() > INIT_FAILED_DURATION) &&
mInitCounter > 1) {
LOG("[AudioTrackEncoder]: Initialize failed for 30s.");
NotifyEndOfStream();
return;
}
}
// Append and consume this raw segment.
@@ -187,8 +198,8 @@ VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
// Check and initialize parameters for codec encoder.
if (!mInitialized) {
mVideoInitCounter++;
TRACK_LOG(LogLevel::Debug, ("Init the video encoder %d times", mVideoInitCounter));
mInitCounter++;
TRACK_LOG(LogLevel::Debug, ("Init the video encoder %d times", mInitCounter));
VideoSegment::ChunkIterator iter(const_cast<VideoSegment&>(video));
while (!iter.IsEnded()) {
VideoChunk chunk = *iter;
@@ -207,6 +218,15 @@ VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
iter.Next();
}
mNotInitDuration += aQueuedMedia.GetDuration();
if (!mInitialized &&
(mNotInitDuration / aGraph->GraphRate() > INIT_FAILED_DURATION) &&
mInitCounter > 1) {
LOG("[VideoTrackEncoder]: Initialize failed for 30s.");
NotifyEndOfStream();
return;
}
}
AppendVideoSegment(video);
+2 -2
View File
@@ -132,8 +132,8 @@ protected:
bool mCanceled;
// How many times we have tried to initialize the encoder.
uint32_t mAudioInitCounter;
uint32_t mVideoInitCounter;
uint32_t mInitCounter;
StreamTime mNotInitDuration;
};
class AudioTrackEncoder : public TrackEncoder
+1
View File
@@ -22,6 +22,7 @@ public:
METADATA_AVC,
METADATA_AAC,
METADATA_AMR,
METADATA_EVRC,
METADATA_UNKNOWN // Metadata Kind not set
};
// Return the specific metadata kind
+4 -2
View File
@@ -269,7 +269,7 @@ nsresult VP8TrackEncoder::PrepareRawFrame(VideoChunk &aChunk)
if (format == ImageFormat::PLANAR_YCBCR) {
PlanarYCbCrImage* yuv = static_cast<PlanarYCbCrImage *>(img.get());
MOZ_ASSERT(yuv);
MOZ_RELEASE_ASSERT(yuv);
if (!yuv->IsValid()) {
NS_WARNING("PlanarYCbCrImage is not valid");
return NS_ERROR_FAILURE;
@@ -306,7 +306,7 @@ nsresult VP8TrackEncoder::PrepareRawFrame(VideoChunk &aChunk)
if (format == ImageFormat::PLANAR_YCBCR) {
PlanarYCbCrImage* yuv = static_cast<PlanarYCbCrImage *>(img.get());
MOZ_ASSERT(yuv);
MOZ_RELEASE_ASSERT(yuv);
if (!yuv->IsValid()) {
NS_WARNING("PlanarYCbCrImage is not valid");
return NS_ERROR_FAILURE;
@@ -354,6 +354,7 @@ nsresult VP8TrackEncoder::PrepareRawFrame(VideoChunk &aChunk)
yuvFormat = "I422";
} else {
VP8LOG("Unsupported planar format\n");
NS_ASSERTION(false, "Unsupported planar format");
return NS_ERROR_NOT_IMPLEMENTED;
}
@@ -410,6 +411,7 @@ nsresult VP8TrackEncoder::PrepareRawFrame(VideoChunk &aChunk)
default:
VP8LOG("Unsupported SourceSurface format %s\n",
Stringify(surf->GetFormat()).c_str());
NS_ASSERTION(false, "Unsupported SourceSurface format");
return NS_ERROR_NOT_IMPLEMENTED;
}
+2
View File
@@ -142,6 +142,8 @@ VorbisTrackEncoder::GetEncodedFrames(EncodedFrameContainer& aData)
VORBISLOG("vorbis_analysis_blockout block size %d", oggPacket.bytes);
EncodedFrame* audiodata = new EncodedFrame();
audiodata->SetFrameType(EncodedFrame::VORBIS_AUDIO_FRAME);
audiodata->SetTimeStamp(oggPacket.granulepos * PR_USEC_PER_SEC
/ mSamplingRate);
nsTArray<uint8_t> frameData;
frameData.AppendElements(oggPacket.packet, oggPacket.bytes);
audiodata->SwapInFrameData(frameData);
+84
View File
@@ -0,0 +1,84 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ISOControl.h"
#include "ISOMediaBoxes.h"
#include "EVRCBox.h"
#include "ISOTrackMetadata.h"
namespace mozilla {
nsresult
EVRCSampleEntry::Generate(uint32_t* aBoxSize)
{
uint32_t box_size;
nsresult rv = evrc_special_box->Generate(&box_size);
NS_ENSURE_SUCCESS(rv, rv);
size += box_size;
*aBoxSize = size;
return NS_OK;
}
nsresult
EVRCSampleEntry::Write()
{
BoxSizeChecker checker(mControl, size);
nsresult rv;
rv = AudioSampleEntry::Write();
NS_ENSURE_SUCCESS(rv, rv);
rv = evrc_special_box->Write();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
EVRCSampleEntry::EVRCSampleEntry(ISOControl* aControl)
: AudioSampleEntry(NS_LITERAL_CSTRING("sevc"), aControl)
{
evrc_special_box = new EVRCSpecificBox(aControl);
MOZ_COUNT_CTOR(EVRCSampleEntry);
}
EVRCSampleEntry::~EVRCSampleEntry()
{
MOZ_COUNT_DTOR(EVRCSampleEntry);
}
nsresult
EVRCSpecificBox::Generate(uint32_t* aBoxSize)
{
nsresult rv;
FragmentBuffer* frag = mControl->GetFragment(Audio_Track);
rv = frag->GetCSD(evrcDecSpecInfo);
NS_ENSURE_SUCCESS(rv, rv);
size += evrcDecSpecInfo.Length();
*aBoxSize = size;
return NS_OK;
}
nsresult
EVRCSpecificBox::Write()
{
BoxSizeChecker checker(mControl, size);
Box::Write();
mControl->Write(evrcDecSpecInfo.Elements(), evrcDecSpecInfo.Length());
return NS_OK;
}
EVRCSpecificBox::EVRCSpecificBox(ISOControl* aControl)
: Box(NS_LITERAL_CSTRING("devc"), aControl)
{
MOZ_COUNT_CTOR(EVRCSpecificBox);
}
EVRCSpecificBox::~EVRCSpecificBox()
{
MOZ_COUNT_DTOR(EVRCSpecificBox);
}
}
+50
View File
@@ -0,0 +1,50 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef EVRCBOX_h_
#define EVRCBOX_h_
#include "nsTArray.h"
#include "MuxerOperation.h"
namespace mozilla {
class ISOControl;
// 3GPP TS 26.244 6.7 'EVRCSpecificBox field for EVRCSampleEntry box'
// Box type: 'devc'
class EVRCSpecificBox : public Box {
public:
// 3GPP members
nsTArray<uint8_t> evrcDecSpecInfo;
// MuxerOperation methods
nsresult Generate(uint32_t* aBoxSize) override;
nsresult Write() override;
// EVRCSpecificBox methods
EVRCSpecificBox(ISOControl* aControl);
~EVRCSpecificBox();
};
// 3GPP TS 26.244 6.5 'EVRCSampleEntry box'
// Box type: 'sevc'
class EVRCSampleEntry : public AudioSampleEntry {
public:
// 3GPP members
RefPtr<EVRCSpecificBox> evrc_special_box;
// MuxerOperation methods
nsresult Generate(uint32_t* aBoxSize) override;
nsresult Write() override;
// EVRCSampleEntry methods
EVRCSampleEntry(ISOControl* aControl);
~EVRCSampleEntry();
};
}
#endif // EVRCBOX_h_
+5 -3
View File
@@ -62,7 +62,7 @@ FragmentBuffer::AddFrame(EncodedFrame* aFrame)
EncodedFrame::FrameType type = aFrame->GetFrameType();
if (type == EncodedFrame::AAC_CSD || type == EncodedFrame::AVC_CSD ||
type == EncodedFrame::AMR_AUDIO_CSD) {
type == EncodedFrame::AMR_AUDIO_CSD || type == EncodedFrame::EVRC_AUDIO_CSD) {
mCSDFrame = aFrame;
// Use CSD's timestamp as the start time. Encoder should send CSD frame first
// and then data frames.
@@ -168,7 +168,8 @@ ISOControl::SetMetadata(TrackMetadataBase* aTrackMeta)
{
if (aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AAC ||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AMR ||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AVC) {
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AVC ||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_EVRC) {
mMetaArray.AppendElement(aTrackMeta);
return NS_OK;
}
@@ -180,7 +181,8 @@ ISOControl::GetAudioMetadata(RefPtr<AudioTrackMetadata>& aAudMeta)
{
for (uint32_t i = 0; i < mMetaArray.Length() ; i++) {
if (mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AAC ||
mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AMR) {
mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AMR ||
mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_EVRC) {
aAudMeta = static_cast<AudioTrackMetadata*>(mMetaArray[i].get());
return NS_OK;
}
@@ -13,6 +13,7 @@
#include "MP4ESDS.h"
#include "AMRBox.h"
#include "AVCBox.h"
#include "EVRCBox.h"
#include "VideoUtils.h"
namespace mozilla {
@@ -666,6 +667,8 @@ SampleDescriptionBox::CreateAudioSampleEntry(RefPtr<SampleEntryBox>& aSampleEntr
aSampleEntry = new AMRSampleEntry(mControl);
} else if (mAudioMeta->GetKind() == TrackMetadataBase::METADATA_AAC) {
aSampleEntry = new MP4AudioSampleEntry(mControl);
} else if (mAudioMeta->GetKind() == TrackMetadataBase::METADATA_EVRC) {
aSampleEntry = new EVRCSampleEntry(mControl);
} else {
MOZ_ASSERT(0);
}
@@ -1250,6 +1253,17 @@ FileTypeBox::Generate(uint32_t* aBoxSize)
compatible_brands.AppendElement("3gp7");
compatible_brands.AppendElement("3gp6");
compatible_brands.AppendElement("isom");
} else if (mControl->GetMuxingType() == ISOMediaWriter::TYPE_FRAG_3G2) {
major_brand = "3g2a";
// 3GPP2 Release 0 and A and 3GPP Release 6 allow movie fragmentation
compatible_brands.AppendElement("3gp9");
compatible_brands.AppendElement("3gp8");
compatible_brands.AppendElement("3gp7");
compatible_brands.AppendElement("3gp6");
compatible_brands.AppendElement("isom");
compatible_brands.AppendElement("3g2c");
compatible_brands.AppendElement("3g2b");
compatible_brands.AppendElement("3g2a");
} else {
MOZ_ASSERT(0);
}
@@ -104,7 +104,9 @@ ISOMediaWriter::WriteEncodedTrack(const EncodedFrameContainer& aData,
if (type == EncodedFrame::AAC_AUDIO_FRAME ||
type == EncodedFrame::AAC_CSD ||
type == EncodedFrame::AMR_AUDIO_FRAME ||
type == EncodedFrame::AMR_AUDIO_CSD) {
type == EncodedFrame::AMR_AUDIO_CSD ||
type == EncodedFrame::EVRC_AUDIO_FRAME ||
type == EncodedFrame::EVRC_AUDIO_CSD) {
frag = mAudioFragmentBuffer;
} else if (type == EncodedFrame::AVC_I_FRAME ||
type == EncodedFrame::AVC_P_FRAME ||
@@ -212,7 +214,8 @@ ISOMediaWriter::SetMetadata(TrackMetadataBase* aMetadata)
PROFILER_LABEL("ISOMediaWriter", "SetMetadata",
js::ProfileEntry::Category::OTHER);
if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AAC ||
aMetadata->GetKind() == TrackMetadataBase::METADATA_AMR) {
aMetadata->GetKind() == TrackMetadataBase::METADATA_AMR ||
aMetadata->GetKind() == TrackMetadataBase::METADATA_EVRC) {
mControl->SetMetadata(aMetadata);
mAudioFragmentBuffer = new FragmentBuffer(Audio_Track, FRAG_DURATION);
mControl->SetFragment(mAudioFragmentBuffer);
@@ -26,6 +26,10 @@ public:
// Brand names in 'ftyp' box are '3gp9' and 'isom'.
const static uint32_t TYPE_FRAG_3GP = 1 << 1;
// Generate an fragmented 3G2 stream, 3GPP2 C.S0050-B
// Brand names in 'ftyp' box are '3g2c' and 'isom'
const static uint32_t TYPE_FRAG_3G2 = 1 << 2;
// aType is the combination of CREATE_AUDIO_TRACK and CREATE_VIDEO_TRACK.
// It is a hint to muxer that the output streaming contains audio, video
// or both.
@@ -99,6 +99,33 @@ public:
~AMRTrackMetadata() { MOZ_COUNT_DTOR(AMRTrackMetadata); }
};
// EVRC sample rate is 8000 samples/s.
#define EVRC_SAMPLE_RATE 8000
class EVRCTrackMetadata : public AudioTrackMetadata {
public:
// AudioTrackMetadata members
//
// The number of sample sets generates by encoder is variant. So the
// frame duration and frame size are both 0.
uint32_t GetAudioFrameDuration() override { return 0; }
uint32_t GetAudioFrameSize() override { return 0; }
uint32_t GetAudioSampleRate() override { return EVRC_SAMPLE_RATE; }
uint32_t GetAudioChannels() override { return mChannels; }
// TrackMetadataBase member
MetadataKind GetKind() const override { return METADATA_EVRC; }
// EVRCTrackMetadata members
EVRCTrackMetadata()
: mChannels(0) {
MOZ_COUNT_CTOR(EVRCTrackMetadata);
}
~EVRCTrackMetadata() { MOZ_COUNT_DTOR(EVRCTrackMetadata); }
uint32_t mChannels; // Channel number, it should be 1 or 2.
};
}
#endif // ISOTrackMetadata_h_
@@ -49,6 +49,7 @@ public:
virtual nsresult Find(const nsACString& aType,
nsTArray<RefPtr<MuxerOperation>>& aOperations) = 0;
protected:
virtual ~MuxerOperation() {}
};
+1
View File
@@ -12,6 +12,7 @@ EXPORTS += [
UNIFIED_SOURCES += [
'AMRBox.cpp',
'AVCBox.cpp',
'EVRCBox.cpp',
'ISOControl.cpp',
'ISOMediaBoxes.cpp',
'ISOMediaWriter.cpp',
+9 -12
View File
@@ -9,10 +9,10 @@
#include "MP4Demuxer.h"
#include "mozilla/Preferences.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentTypeParser.h"
#include "VideoUtils.h"
#include "mozilla/Logging.h"
#include "nsMimeTypes.h"
#include "nsContentTypeParser.h"
#include "VideoUtils.h"
#ifdef XP_WIN
#include "mozilla/WindowsVersion.h"
@@ -103,21 +103,18 @@ MP4Decoder::CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs,
// etc output).
const bool isMP4Audio = aMIMETypeExcludingCodecs.EqualsASCII("audio/mp4") ||
aMIMETypeExcludingCodecs.EqualsASCII("audio/x-m4a");
const bool isMP4Video = aMIMETypeExcludingCodecs.EqualsASCII("video/mp4") ||
#ifdef XP_LINUX
aMIMETypeExcludingCodecs.EqualsASCII("video/quicktime") ||
const bool isMP4Video =
// On B2G, treat 3GPP as MP4 when Gonk PDM is available.
#ifdef MOZ_GONK_MEDIACODEC
aMIMETypeExcludingCodecs.EqualsASCII(VIDEO_3GPP) ||
#endif
aMIMETypeExcludingCodecs.EqualsASCII("video/x-m4v");
aMIMETypeExcludingCodecs.EqualsASCII("video/mp4") ||
aMIMETypeExcludingCodecs.EqualsASCII("video/quicktime") ||
aMIMETypeExcludingCodecs.EqualsASCII("video/x-m4v");
if (!isMP4Audio && !isMP4Video) {
return false;
}
#ifdef MOZ_GONK_MEDIACODEC
if (aMIMETypeExcludingCodecs.EqualsASCII(VIDEO_3GPP)) {
return Preferences::GetBool("media.gonk.enabled", false);
}
#endif
nsTArray<nsCString> codecMimes;
if (aCodecs.IsEmpty()) {
// No codecs specified. Assume AAC/H.264
+1 -1
View File
@@ -248,7 +248,7 @@ GMPDecryptorParent::RecvRejectPromise(const uint32_t& aPromiseId,
const nsCString& aMessage)
{
LOGD(("GMPDecryptorParent[%p]::RecvRejectPromise(promiseId=%u, exception=%d, msg='%s')",
this, aException, aMessage.get()));
this, aPromiseId, aException, aMessage.get()));
if (!mIsOpen) {
NS_WARNING("Trying to use a dead GMP decrypter!");
+9 -3
View File
@@ -94,10 +94,12 @@ enum GMPMediaKeyStatus {
kGMPUsable = 0,
kGMPExpired = 1,
kGMPOutputDownscaled = 2,
kGMPOutputNotAllowed = 3,
kGMPOutputRestricted = 3,
kGMPInternalError = 4,
kGMPUnknown = 5,
kGMPMediaKeyStatusInvalid = 6 // Must always be last.
kGMPUnknown = 5, // Removes key from MediaKeyStatusMap
kGMPReleased = 6,
kGMPStatusPending = 7,
kGMPMediaKeyStatusInvalid = 8 // Must always be last.
};
// Time in milliseconds, as offset from epoch, 1 Jan 1970.
@@ -125,6 +127,10 @@ typedef int64_t GMPTimestamp;
#define GMP_EME_CAP_DECRYPT_AND_DECODE_AUDIO (uint64_t(1) << 2)
#define GMP_EME_CAP_DECRYPT_AND_DECODE_VIDEO (uint64_t(1) << 3)
// Capability; CDM can decrypt and then decode and render encrypted buffers
#define GMP_EME_CAP_RENDER_AUDIO (uint64_t(1) << 4)
#define GMP_EME_CAP_RENDER_VIDEO (uint64_t(1) << 5)
// Callbacks to be called from the CDM. Threadsafe.
class GMPDecryptorCallback {
public:
+32
View File
@@ -27,6 +27,7 @@ using namespace mozilla::layers;
#define INPUT_BUFFER_TIMEOUT_US (5 * 1000ll)
// AMR NB kbps
#define AMRNB_BITRATE 12200
#define EVRC_BITRATE 8755
#define CODEC_ERROR(args...) \
do { \
@@ -35,6 +36,8 @@ using namespace mozilla::layers;
namespace android {
const char *MEDIA_MIMETYPE_AUDIO_EVRC = "audio/evrc";
enum BufferState
{
BUFFER_OK,
@@ -93,6 +96,16 @@ OMXCodecWrapper::CreateAMRNBEncoder()
return amr.forget();
}
OMXAudioEncoder*
OMXCodecWrapper::CreateEVRCEncoder()
{
nsAutoPtr<OMXAudioEncoder> evrc(new OMXAudioEncoder(CodecType::EVRC_ENC));
// Return the object only when media codec is valid.
NS_ENSURE_TRUE(evrc->IsValid(), nullptr);
return evrc.forget();
}
OMXVideoEncoder*
OMXCodecWrapper::CreateAVCEncoder()
{
@@ -107,6 +120,7 @@ OMXCodecWrapper::OMXCodecWrapper(CodecType aCodecType)
: mCodecType(aCodecType)
, mStarted(false)
, mAMRCSDProvided(false)
, mEVRCCSDProvided(false)
{
ProcessState::self()->startThreadPool();
@@ -119,6 +133,8 @@ OMXCodecWrapper::OMXCodecWrapper(CodecType aCodecType)
mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_AMR_NB, true);
} else if (aCodecType == CodecType::AAC_ENC) {
mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_AAC, true);
} else if (aCodecType == CodecType::EVRC_ENC) {
mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_EVRC, true);
} else {
NS_ERROR("Unknown codec type.");
}
@@ -572,6 +588,10 @@ OMXAudioEncoder::Configure(int aChannels, int aInputSampleRate,
format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB);
format->setInt32("bitrate", AMRNB_BITRATE);
format->setInt32("sample-rate", aEncodedSampleRate);
} else if (mCodecType == EVRC_ENC) {
format->setString("mime", MEDIA_MIMETYPE_AUDIO_EVRC);
format->setInt32("bitrate", EVRC_BITRATE);
format->setInt32("sample-rate", aEncodedSampleRate);
} else {
MOZ_ASSERT(false, "Can't support this codec type!!");
}
@@ -1009,6 +1029,18 @@ OMXCodecWrapper::GetNextEncodedFrame(nsTArray<uint8_t>* aOutputBuf,
aOutputBuf->AppendElements(decConfig, sizeof(decConfig));
outFlags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
mAMRCSDProvided = true;
} else if ((mCodecType == EVRC_ENC) && !mEVRCCSDProvided){
// OMX EVRC codec won't provide csd data, need to generate a fake one.
RefPtr<EncodedFrame> audiodata = new EncodedFrame();
// Decoder config descriptor
const uint8_t decConfig[] = {
0x0, 0x0, 0x0, 0x0, // vendor: 4 bytes
0x0, // decoder version
0x01, // frames per sample
};
aOutputBuf->AppendElements(decConfig, sizeof(decConfig));
outFlags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
mEVRCCSDProvided = true;
} else {
AppendFrame(aOutputBuf, omxBuf->data(), omxBuf->size());
}
+5
View File
@@ -89,6 +89,7 @@ public:
AAC_ENC, // AAC encoder.
AMR_NB_ENC, // AMR_NB encoder.
AVC_ENC, // AVC/H.264 encoder.
EVRC_ENC, // EVRC encoder
TYPE_COUNT
};
@@ -121,6 +122,9 @@ public:
/** Create a AMR audio encoder. Returns nullptr when failed. */
static OMXAudioEncoder* CreateAMRNBEncoder();
/** Create a EVRC audio encoder. Returns nullptr when failed. */
static OMXAudioEncoder* CreateEVRCEncoder();
/** Create a AVC/H.264 video encoder. Returns nullptr when failed. */
static OMXVideoEncoder* CreateAVCEncoder();
@@ -205,6 +209,7 @@ private:
int mCodecType;
bool mStarted; // Has MediaCodec been started?
bool mAMRCSDProvided;
bool mEVRCCSDProvided;
};
/**
+56 -9
View File
@@ -12,6 +12,39 @@ using mp4_demuxer::ByteReader;
namespace mozilla {
int16_t
DecodeALawSample(uint8_t aValue)
{
aValue = aValue ^ 0x55;
int8_t sign = (aValue & 0x80) ? -1 : 1;
uint8_t exponent = (aValue & 0x70) >> 4;
uint8_t mantissa = aValue & 0x0F;
int16_t sample = mantissa << 4;
switch (exponent) {
case 0:
sample += 8;
break;
case 1:
sample += 0x108;
break;
default:
sample += 0x108;
sample <<= exponent - 1;
}
return sign * sample;
}
int16_t
DecodeULawSample(uint8_t aValue)
{
aValue = aValue ^ 0xFF;
int8_t sign = (aValue & 0x80) ? -1 : 1;
uint8_t exponent = (aValue & 0x70) >> 4;
uint8_t mantissa = aValue & 0x0F;
int16_t sample = (33 + 2 * mantissa) * (2 << (exponent + 1)) - 33;
return sign * sample;
}
WaveDataDecoder::WaveDataDecoder(const AudioInfo& aConfig,
FlushableTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback)
@@ -69,18 +102,30 @@ WaveDataDecoder::DoDecode(MediaRawData* aSample)
auto buffer = MakeUnique<AudioDataValue[]>(frames * mInfo.mChannels);
for (int i = 0; i < frames; ++i) {
for (unsigned int j = 0; j < mInfo.mChannels; ++j) {
if (mInfo.mBitDepth == 8) {
if (mInfo.mProfile == 6) { //ALAW Data
uint8_t v = aReader.ReadU8();
int16_t decoded = DecodeALawSample(v);
buffer[i * mInfo.mChannels + j] =
UInt8bitToAudioSample<AudioDataValue>(v);
} else if (mInfo.mBitDepth == 16) {
int16_t v = aReader.ReadLE16();
IntegerToAudioSample<AudioDataValue>(decoded);
} else if (mInfo.mProfile == 7) { //ULAW Data
uint8_t v = aReader.ReadU8();
int16_t decoded = DecodeULawSample(v);
buffer[i * mInfo.mChannels + j] =
IntegerToAudioSample<AudioDataValue>(v);
} else if (mInfo.mBitDepth == 24) {
int32_t v = aReader.ReadLE24();
buffer[i * mInfo.mChannels + j] =
Int24bitToAudioSample<AudioDataValue>(v);
IntegerToAudioSample<AudioDataValue>(decoded);
} else { //PCM Data
if (mInfo.mBitDepth == 8) {
uint8_t v = aReader.ReadU8();
buffer[i * mInfo.mChannels + j] =
UInt8bitToAudioSample<AudioDataValue>(v);
} else if (mInfo.mBitDepth == 16) {
int16_t v = aReader.ReadLE16();
buffer[i * mInfo.mChannels + j] =
IntegerToAudioSample<AudioDataValue>(v);
} else if (mInfo.mBitDepth == 24) {
int32_t v = aReader.ReadLE24();
buffer[i * mInfo.mChannels + j] =
Int24bitToAudioSample<AudioDataValue>(v);
}
}
}
}
@@ -132,6 +177,8 @@ WaveDataDecoder::IsWave(const nsACString& aMimeType)
// WAVdemuxer uses "audio/wave; codecs=aNum".
return aMimeType.EqualsLiteral("audio/x-wav") ||
aMimeType.EqualsLiteral("audio/wave; codecs=1") ||
aMimeType.EqualsLiteral("audio/wave; codecs=6") ||
aMimeType.EqualsLiteral("audio/wave; codecs=7") ||
aMimeType.EqualsLiteral("audio/wave; codecs=65534");
}
@@ -325,6 +325,8 @@ AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType) const
// To avoid this we check for wav types here.
if (aMimeType.EqualsLiteral("audio/x-wav") ||
aMimeType.EqualsLiteral("audio/wave; codecs=1") ||
aMimeType.EqualsLiteral("audio/wave; codecs=6") ||
aMimeType.EqualsLiteral("audio/wave; codecs=7") ||
aMimeType.EqualsLiteral("audio/wave; codecs=65534")) {
return false;
}
@@ -115,10 +115,8 @@ FFmpegH264Decoder<LIBAV_VER>::FFmpegH264Decoder(
ImageContainer* aImageContainer)
: FFmpegDataDecoder(aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
, mImageContainer(aImageContainer)
, mPictureWidth(aConfig.mImage.width)
, mPictureHeight(aConfig.mImage.height)
, mDisplayWidth(aConfig.mDisplay.width)
, mDisplayHeight(aConfig.mDisplay.height)
, mDisplay(aConfig.mDisplay)
, mImage(aConfig.mImage)
, mCodecParser(nullptr)
{
MOZ_COUNT_CTOR(FFmpegH264Decoder);
@@ -143,18 +141,18 @@ FFmpegH264Decoder<LIBAV_VER>::Init()
void
FFmpegH264Decoder<LIBAV_VER>::InitCodecContext()
{
mCodecContext->width = mPictureWidth;
mCodecContext->height = mPictureHeight;
mCodecContext->width = mImage.width;
mCodecContext->height = mImage.height;
// We use the same logic as libvpx in determining the number of threads to use
// so that we end up behaving in the same fashion when using ffmpeg as
// we would otherwise cause various crashes (see bug 1236167)
int decode_threads = 1;
if (mDisplayWidth >= 2048) {
if (mDisplay.width >= 2048) {
decode_threads = 8;
} else if (mDisplayWidth >= 1024) {
} else if (mDisplay.width >= 1024) {
decode_threads = 4;
} else if (mDisplayWidth >= 320) {
} else if (mDisplay.width >= 320) {
decode_threads = 2;
}
@@ -311,7 +309,7 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
}
VideoInfo info;
info.mDisplay = nsIntSize(mDisplayWidth, mDisplayHeight);
info.mDisplay = mDisplay;
VideoData::YCbCrBuffer b;
b.mPlanes[0].mData = mFrame->data[0];
@@ -344,7 +342,7 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
b,
!!mFrame->key_frame,
-1,
gfx::IntRect(0, 0, mCodecContext->width, mCodecContext->height));
mImage);
if (!v) {
NS_WARNING("image allocation error.");
mCallback->Error();
@@ -69,10 +69,8 @@ private:
AVFrame* aFrame);
RefPtr<ImageContainer> mImageContainer;
uint32_t mPictureWidth;
uint32_t mPictureHeight;
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
nsIntSize mDisplay;
nsIntRect mImage;
// Parser used for VP8 and VP9 decoding.
AVCodecParserContext* mCodecParser;
@@ -139,6 +139,11 @@ H264Converter::CreateDecoder()
return NS_ERROR_NOT_INITIALIZED;
}
UpdateConfigFromExtraData(mCurrentConfig.mExtraData);
if (!mNeedAVCC) {
// When using a decoder handling AnnexB, we get here only once from the
// constructor. We do want to get the dimensions extracted from the SPS.
mOriginalConfig = mCurrentConfig;
}
mDecoder = mPDM->CreateVideoDecoder(mNeedAVCC ? mCurrentConfig : mOriginalConfig,
mLayersBackend,
+1 -1
View File
@@ -60,7 +60,7 @@ private:
void OnDecoderInitFailed(MediaDataDecoder::DecoderFailureReason aReason);
RefPtr<PlatformDecoderModule> mPDM;
const VideoInfo& mOriginalConfig;
VideoInfo mOriginalConfig;
VideoInfo mCurrentConfig;
layers::LayersBackend mLayersBackend;
RefPtr<layers::ImageContainer> mImageContainer;
Binary file not shown.
@@ -0,0 +1 @@
Cache-Control: no-store
+2
View File
@@ -11,6 +11,8 @@ function check_wave(v, enabled) {
// Supported Wave codecs
check("audio/wave; codecs=1", "probably");
check("audio/wave; codecs=6", "probably");
check("audio/wave; codecs=7", "probably");
// "no codecs" should be supported, I guess
check("audio/wave; codecs=", "probably");
check("audio/wave; codecs=\"\"", "probably");
+8
View File
@@ -128,6 +128,14 @@ var gPlayTests = [
{ name:"r11025_s16_c1_trailing.wav", type:"audio/x-wav", duration:1.0 },
// file with list chunk
{ name:"r16000_u8_c1_list.wav", type:"audio/x-wav", duration:4.2 },
// file with 2 extra bytes of metadata
{ name:"16bit_wave_extrametadata.wav", type:"audio/x-wav", duration:1.108 },
// 24-bit samples
{ name:"wavedata_s24.wav", type:"audio/x-wav", duration:1.0 },
// aLaw compressed wave file
{ name:"wavedata_alaw.wav", type:"audio/x-wav", duration:1.0 },
// uLaw compressed wave file
{ name:"wavedata_ulaw.wav", type:"audio/x-wav", duration:1.0 },
// Ogg stream without eof marker
{ name:"bug461281.ogg", type:"application/ogg", duration:2.208 },
+8
View File
@@ -24,6 +24,8 @@
[DEFAULT]
skip-if = buildapp == 'mulet' || (os == 'win' && strictContentSandbox) || android_version == '18' # strictContentSandbox (Bug 1042735)
support-files =
16bit_wave_extrametadata.wav
16bit_wave_extrametadata.wav^headers^
320x240.ogv
320x240.ogv^headers^
448636.ogv
@@ -292,10 +294,16 @@ support-files =
wave_metadata_unknown_tag.wav^headers^
wave_metadata_utf8.wav
wave_metadata_utf8.wav^headers^
wavedata_alaw.wav
wavedata_alaw.wav^headers^
wavedata_s24.wav
wavedata_s24.wav^headers^
wavedata_s16.wav
wavedata_s16.wav^headers^
wavedata_u8.wav
wavedata_u8.wav^headers^
wavedata_ulaw.wav
wavedata_ulaw.wav^headers^
[test_access_control.html]
skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
Binary file not shown.
@@ -0,0 +1 @@
Cache-Control: no-store
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
Cache-Control: no-store
Binary file not shown.
@@ -0,0 +1 @@
Cache-Control: no-store
+4 -1
View File
@@ -52,7 +52,10 @@ WaveDecoder::CanHandleMediaType(const nsACString& aType,
{
if (aType.EqualsASCII("audio/wave") || aType.EqualsASCII("audio/x-wav") ||
aType.EqualsASCII("audio/wav") || aType.EqualsASCII("audio/x-pn-wav")) {
return IsEnabled() && (aCodecs.IsEmpty() || aCodecs.EqualsASCII("1"));
return IsEnabled() && (aCodecs.IsEmpty() ||
aCodecs.EqualsASCII("1") ||
aCodecs.EqualsASCII("6") ||
aCodecs.EqualsASCII("7"));
}
return false;
+44 -26
View File
@@ -80,6 +80,20 @@ namespace {
return result;
}
int32_t
ReadInt24LE(const char** aBuffer)
{
int32_t result = int32_t((uint8_t((*aBuffer)[2]) << 16) |
(uint8_t((*aBuffer)[1]) << 8 ) |
(uint8_t((*aBuffer)[0])));
if (((*aBuffer)[2] & 0x80) == 0x80) {
result = (result | 0xff000000);
}
*aBuffer += 3 * sizeof(char);
return result;
}
uint16_t
ReadUint16LE(const char** aBuffer)
{
@@ -142,11 +156,13 @@ nsresult WaveReader::ReadMetadata(MediaInfo* aInfo,
*aTags = tags.forget();
return NS_OK;
}
template <typename T> T UnsignedByteToAudioSample(uint8_t aValue);
template <typename T> T SignedShortToAudioSample(int16_t aValue);
template <typename T> T Signed24bIntToAudioSample(int32_t aValue);
template <> inline float
UnsignedByteToAudioSample<float>(uint8_t aValue)
@@ -170,6 +186,18 @@ SignedShortToAudioSample<int16_t>(int16_t aValue)
return aValue;
}
template <> inline float
Signed24bIntToAudioSample<float>(int32_t aValue)
{
return aValue / 8388608.0f;
}
template <> inline int16_t
Signed24bIntToAudioSample<int16_t>(int32_t aValue)
{
return aValue / 256;
}
bool WaveReader::DecodeAudioData()
{
MOZ_ASSERT(OnTaskQueue());
@@ -179,10 +207,13 @@ bool WaveReader::DecodeAudioData()
int64_t remaining = len - pos;
NS_ASSERTION(remaining >= 0, "Current wave position is greater than wave file length");
static const int64_t BLOCK_SIZE = 4096;
static const int64_t BLOCK_SIZE = 6144;
int64_t readSize = std::min(BLOCK_SIZE, remaining);
int64_t frames = readSize / mFrameSize;
MOZ_ASSERT(BLOCK_SIZE % 3 == 0);
MOZ_ASSERT(BLOCK_SIZE % 2 == 0);
static_assert(uint64_t(BLOCK_SIZE) < UINT_MAX /
sizeof(AudioDataValue) / MAX_CHANNELS,
"bufferSize calculation could overflow.");
@@ -208,6 +239,9 @@ bool WaveReader::DecodeAudioData()
} else if (mSampleFormat == FORMAT_S16) {
int16_t v = ReadInt16LE(&d);
*s++ = SignedShortToAudioSample<AudioDataValue>(v);
} else if (mSampleFormat == FORMAT_S24) {
int32_t v = ReadInt24LE(&d);
*s++ = Signed24bIntToAudioSample<AudioDataValue>(v);
}
}
}
@@ -387,29 +421,10 @@ WaveReader::LoadFormatChunk(uint32_t aChunkSize)
// considering the file invalid. This code skips any extension of the
// "format" chunk.
if (aChunkSize > WAVE_FORMAT_CHUNK_SIZE) {
char extLength[2];
const char* p = extLength;
if (!ReadAll(extLength, sizeof(extLength))) {
return false;
}
static_assert(sizeof(uint16_t) <= sizeof(extLength),
"Reads would overflow extLength buffer.");
uint16_t extra = ReadUint16LE(&p);
if (aChunkSize - (WAVE_FORMAT_CHUNK_SIZE + 2) != extra) {
NS_WARNING("Invalid extended format chunk size");
return false;
}
uint16_t extra = aChunkSize - WAVE_FORMAT_CHUNK_SIZE;
extra += extra % 2;
if (extra > 0) {
static_assert(UINT16_MAX + (UINT16_MAX % 2) < UINT_MAX / sizeof(char),
"chunkExtension array too large for iterator.");
auto chunkExtension = MakeUnique<char[]>(extra);
if (!ReadAll(chunkExtension.get(), extra)) {
return false;
}
if (NS_FAILED(mResource.Seek(nsISeekableStream::NS_SEEK_CUR, extra))) {
return false;
}
}
@@ -421,12 +436,13 @@ WaveReader::LoadFormatChunk(uint32_t aChunkSize)
// but the channels check is intentionally limited to mono or stereo
// when the media is intended for direct playback because that's what the
// audio backend currently supports.
unsigned int actualFrameSize = (sampleFormat == 8 ? 1 : 2) * channels;
unsigned int actualFrameSize = sampleFormat * channels / 8;
if (rate < 100 || rate > 96000 ||
(((channels < 1 || channels > MAX_CHANNELS) ||
(frameSize != 1 && frameSize != 2 && frameSize != 4)) &&
(frameSize != 1 && frameSize != 2 && frameSize != 3 &&
frameSize != 4 && frameSize != 6)) &&
!mIgnoreAudioOutputFormat) ||
(sampleFormat != 8 && sampleFormat != 16) ||
(sampleFormat != 8 && sampleFormat != 16 && sampleFormat != 24) ||
frameSize != actualFrameSize) {
NS_WARNING("Invalid WAVE metadata");
return false;
@@ -437,6 +453,8 @@ WaveReader::LoadFormatChunk(uint32_t aChunkSize)
mFrameSize = frameSize;
if (sampleFormat == 8) {
mSampleFormat = FORMAT_U8;
} else if (sampleFormat == 24) {
mSampleFormat = FORMAT_S24;
} else {
mSampleFormat = FORMAT_S16;
}
+2 -1
View File
@@ -74,7 +74,8 @@ private:
// support U8.
enum {
FORMAT_U8,
FORMAT_S16
FORMAT_S16,
FORMAT_S24
} mSampleFormat;
// Size of PCM data stored in the WAVE as reported by the data chunk in
+9 -14
View File
@@ -113,7 +113,9 @@ EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame)
EbmlGlobal ebml;
ebml.offset = 0;
if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
auto frameType = aFrame->GetFrameType();
bool isVP8IFrame = (frameType == EncodedFrame::FrameType::VP8_I_FRAME);
if (isVP8IFrame) {
FinishCluster();
}
@@ -121,7 +123,7 @@ EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame)
block->SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE);
ebml.buf = block->Elements();
if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
if (isVP8IFrame) {
EbmlLoc ebmlLoc;
Ebml_StartSubElement(&ebml, &ebmlLoc, Cluster);
MOZ_ASSERT(mClusterBuffs.Length() > 0);
@@ -133,18 +135,11 @@ EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame)
mFlushState |= FLUSH_CLUSTER;
}
if (aFrame->GetFrameType() != EncodedFrame::FrameType::VORBIS_AUDIO_FRAME) {
short timeCode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC
- mClusterTimecode;
writeSimpleBlock(&ebml, 0x1, timeCode, aFrame->GetFrameType() ==
EncodedFrame::FrameType::VP8_I_FRAME,
0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
aFrame->GetFrameData().Length());
} else {
writeSimpleBlock(&ebml, 0x2, 0, false,
0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
aFrame->GetFrameData().Length());
}
bool isVorbis = (frameType == EncodedFrame::FrameType::VORBIS_AUDIO_FRAME);
short timeCode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC - mClusterTimecode;
writeSimpleBlock(&ebml, isVorbis ? 0x2 : 0x1, timeCode, isVP8IFrame,
0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
aFrame->GetFrameData().Length());
MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE +
aFrame->GetFrameData().Length(),
"write more data > EBML_BUFFER_SIZE");
+16 -7
View File
@@ -508,11 +508,19 @@ js::atomics_isLockFree(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
HandleValue v = args.get(0);
if (!v.isInt32()) {
args.rval().setBoolean(false);
return true;
int32_t size;
if (v.isInt32()) {
size = v.toInt32();
} else {
double dsize;
if (!ToInteger(cx, v, &dsize))
return false;
if (!mozilla::NumberIsInt32(dsize, &size)) {
args.rval().setBoolean(false);
return true;
}
}
args.rval().setBoolean(jit::AtomicOperations::isLockfree(v.toInt32()));
args.rval().setBoolean(jit::AtomicOperations::isLockfree(size));
return true;
}
@@ -877,8 +885,8 @@ js::atomics_futexWakeOrRequeue(JSContext* cx, unsigned argc, Value* vp)
HandleValue objv = args.get(0);
HandleValue idx1v = args.get(1);
HandleValue countv = args.get(2);
HandleValue valv = args.get(3);
HandleValue idx2v = args.get(4);
HandleValue idx2v = args.get(3);
HandleValue valv = args.get(4);
MutableHandleValue r = args.rval();
Rooted<TypedArrayObject*> view(cx, nullptr);
@@ -1029,7 +1037,8 @@ js::FutexRuntime::unlock()
js::FutexRuntime::FutexRuntime()
: cond_(nullptr),
state_(Idle)
state_(Idle),
canWait_(false)
{
}
+11 -11
View File
@@ -2,7 +2,7 @@
* 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/. */
// ES6 spec draft 29, 19.2.3.2
// ES7 draft (January 21, 2016) 19.2.3.2 Function.prototype.bind
function FunctionBind(thisArg, ...boundArgs) {
// Step 1.
var target = this;
@@ -11,7 +11,7 @@ function FunctionBind(thisArg, ...boundArgs) {
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, 'Function', 'bind', target);
// Step 3 (implicit).
// Steps 4-5.
// Step 4.
var F;
var argCount = boundArgs.length;
switch (argCount) {
@@ -28,30 +28,30 @@ function FunctionBind(thisArg, ...boundArgs) {
F = bind_bindFunctionN(target, thisArg, boundArgs);
}
// Steps 6-7.
// Step 5.
var targetHasLength = callFunction(std_Object_hasOwnProperty, target, "length");
// Step 8.
// Step 6.
var L;
if (targetHasLength) {
// Steps 8.a-b.
// Step 6.a.
var targetLen = target.length;
// Step 8.c.
// Step 6.b.
if (typeof targetLen !== 'number') {
L = 0;
} else {
// Steps d.i-ii.
// Steps 6.b.i-ii.
L = std_Math_max(0, ToInteger(targetLen) - argCount);
}
} else {
// Step 9.
// Step 7.
L = 0;
}
// Steps 12-13.
// Step 9.
var targetName = target.name;
// Step 14.
// Step 10.
if (typeof targetName !== "string")
targetName = "";
@@ -62,7 +62,7 @@ function FunctionBind(thisArg, ...boundArgs) {
// JIT code.
var funApply = std_Function_apply;
// Step 17.
// Step 12.
return F;
}
/**
+15 -12
View File
@@ -295,10 +295,10 @@ ModuleNamespaceObject::module()
return GetProxyPrivate(this).toObject().as<ModuleObject>();
}
ArrayObject&
JSObject&
ModuleNamespaceObject::exports()
{
ArrayObject* exports = module().namespaceExports();
JSObject* exports = module().namespaceExports();
MOZ_ASSERT(exports);
return *exports;
}
@@ -493,15 +493,17 @@ ModuleNamespaceObject::ProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject
AutoIdVector& props) const
{
Rooted<ModuleNamespaceObject*> ns(cx, &proxy->as<ModuleNamespaceObject>());
RootedArrayObject exports(cx, &ns->exports());
uint32_t count = exports->length();
if (!props.reserve(props.length() + count))
RootedObject exports(cx, &ns->exports());
uint32_t count;
if (!GetLengthProperty(cx, exports, &count) || !props.reserve(props.length() + count))
return false;
for (uint32_t i = 0; i < count; i++) {
Value value = exports->getDenseElement(i);
props.infallibleAppend(AtomToId(&value.toString()->asAtom()));
}
Rooted<ValueVector> names(cx, ValueVector(cx));
if (!names.resize(count) || !GetElements(cx, exports, count, names.begin()))
return false;
for (uint32_t i = 0; i < count; i++)
props.infallibleAppend(AtomToId(&names[i].toString()->asAtom()));
return true;
}
@@ -625,14 +627,14 @@ ModuleObject::importBindings()
return *static_cast<IndirectBindingMap*>(getReservedSlot(ImportBindingsSlot).toPrivate());
}
ArrayObject*
JSObject*
ModuleObject::namespaceExports()
{
Value value = getReservedSlot(NamespaceExportsSlot);
if (value.isUndefined())
return nullptr;
return &value.toObject().as<ArrayObject>();
return &value.toObject();
}
IndirectBindingMap*
@@ -869,9 +871,10 @@ ModuleObject::evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValu
}
/* static */ ModuleNamespaceObject*
ModuleObject::createNamespace(JSContext* cx, HandleModuleObject self, HandleArrayObject exports)
ModuleObject::createNamespace(JSContext* cx, HandleModuleObject self, HandleObject exports)
{
MOZ_ASSERT(!self->namespace_());
MOZ_ASSERT(exports->is<ArrayObject>() || exports->is<UnboxedArrayObject>());
RootedModuleNamespaceObject ns(cx, ModuleNamespaceObject::create(cx, self));
if (!ns)
+3 -3
View File
@@ -133,7 +133,7 @@ class ModuleNamespaceObject : public ProxyObject
static ModuleNamespaceObject* create(JSContext* cx, HandleModuleObject module);
ModuleObject& module();
ArrayObject& exports();
JSObject& exports();
IndirectBindingMap& bindings();
bool addBinding(JSContext* cx, HandleAtom exportedName, HandleModuleObject targetModule,
@@ -248,7 +248,7 @@ class ModuleObject : public NativeObject
ArrayObject& indirectExportEntries() const;
ArrayObject& starExportEntries() const;
IndirectBindingMap& importBindings();
ArrayObject* namespaceExports();
JSObject* namespaceExports();
IndirectBindingMap* namespaceBindings();
void createEnvironment();
@@ -260,7 +260,7 @@ class ModuleObject : public NativeObject
static bool evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValue rval);
static ModuleNamespaceObject* createNamespace(JSContext* cx, HandleModuleObject self,
HandleArrayObject exports);
HandleObject exports);
private:
static void trace(JSTracer* trc, JSObject* obj);
+6 -2
View File
@@ -24,12 +24,16 @@
#include "SelfHostingDefines.h"
// Assertions, defined here instead of in the header above to make `assert`
// invisible to C++.
// Assertions and debug printing, defined here instead of in the header above
// to make `assert` invisible to C++.
#ifdef DEBUG
#define assert(b, info) if (!(b)) AssertionFailed(__FILE__ + ":" + __LINE__ + ": " + info)
#define dbg(msg) DumpMessage(callFunction(std_Array_pop, \
callFunction(std_String_split, __FILE__, '/')) \
+ '#' + __LINE__ + ': ' + msg)
#else
#define assert(b, info) // Elided assertion.
#define dbg(msg) // Elided debugging output.
#endif
// All C++-implemented standard builtins library functions used in self-hosted
@@ -409,6 +409,38 @@ function testIsLockFree() {
assertEq(Atomics.isLockFree(12), false);
}
function testIsLockFree2() {
assertEq(Atomics.isLockFree(0), false);
assertEq(Atomics.isLockFree(0/-1), false);
assertEq(Atomics.isLockFree(3.5), false);
assertEq(Atomics.isLockFree(Number.NaN), false); // NaN => +0
assertEq(Atomics.isLockFree(Number.POSITIVE_INFINITY), false);
assertEq(Atomics.isLockFree(Number.NEGATIVE_INFINITY), false);
assertEq(Atomics.isLockFree(-4), false);
assertEq(Atomics.isLockFree('4'), true);
assertEq(Atomics.isLockFree('-4'), false);
assertEq(Atomics.isLockFree('4.5'), true);
assertEq(Atomics.isLockFree('5.5'), false);
assertEq(Atomics.isLockFree(new Number(4)), true);
assertEq(Atomics.isLockFree(new String('4')), true);
assertEq(Atomics.isLockFree(new Boolean(true)), true);
var thrown = false;
try {
Atomics.isLockFree(Symbol('1'));
} catch (e) {
thrown = e;
}
assertEq(thrown instanceof TypeError, true);
assertEq(Atomics.isLockFree(true), true);
assertEq(Atomics.isLockFree(false), false);
assertEq(Atomics.isLockFree(undefined), false);
assertEq(Atomics.isLockFree(null), false);
assertEq(Atomics.isLockFree({toString: () => '4'}), true);
assertEq(Atomics.isLockFree({valueOf: () => 4}), true);
assertEq(Atomics.isLockFree({valueOf: () => 5}), false);
assertEq(Atomics.isLockFree({password: "qumquat"}), false);
}
function testUint8Clamped(sab) {
var ta = new Uint8ClampedArray(sab);
var thrown = false;
@@ -516,6 +548,7 @@ function runTests() {
// Misc
testIsLockFree();
testIsLockFree2();
}
if (this.Atomics && this.SharedArrayBuffer)
+15
View File
@@ -0,0 +1,15 @@
// |jit-test| exitstatus: 3
if (!('oomAfterAllocations' in this))
quit(3);
var g = newGlobal();
var dbg = new Debugger(g);
dbg.onDebuggerStatement = function(frame) {
oomAfterAllocations(5);
// OOMs here, and possibly again in the error reporter when trying to
// report the OOM, so the shell just exits with code 3.
frame.older.eval("escaped = function() { return y }");
}
g.eval("function h() { debugger }");
g.eval("(function () { var y = {p:42}; h(); yield })().next();");
@@ -0,0 +1,5 @@
g = newGlobal();
g.log = "";
Debugger(g).onDebuggerStatement = frame => frame.eval("log += this.Math.toString();");
g.eval("(function() { with ({}) debugger })()");
assertEq(g.log, "[object Math]");
@@ -0,0 +1,31 @@
// Test that eval-in-frame throws on accessing optimized out values.
load(libdir + "jitopts.js");
if (!jitTogglesMatch(Opts_IonEagerNoOffthreadCompilation))
quit(0);
withJitOptions(Opts_IonEagerNoOffthreadCompilation, function() {
var dbgGlobal = newGlobal();
var dbg = new dbgGlobal.Debugger();
dbg.addDebuggee(this);
function f() {
assertEq(dbg.getNewestFrame().older.eval("print(a)").throw.unsafeDereference().toString(),
"Error: variable `a' has been optimized out");
}
// Test optimized out binding in function scope.
(function () {
function a() {}
for (var i = 0; i < 1; i++) f();
})();
// Test optimized out binding in block scope.
(function () {
{
function a() {}
for (var i = 0; i < 1; i++) f();
}
})();
});
+14
View File
@@ -0,0 +1,14 @@
if (typeof offThreadCompileScript !== 'function' ||
typeof runOffThreadScript !== 'function' ||
typeof oomTest !== 'function' ||
typeof fullcompartmentchecks !== 'function' ||
helperThreadCount() === 0)
{
quit(0);
}
offThreadCompileScript(`
oomTest(() => "".search(/d/));
fullcompartmentchecks(3);
`);
runOffThreadScript();
+16
View File
@@ -0,0 +1,16 @@
if (typeof oomTest !== 'function' || typeof Intl !== 'object')
quit();
oomTest(() => {
try {
new Intl.DateTimeFormat;
x1 = 0;
} catch (e) {
switch (1) {
case 0:
let s;
case 1:
x;
}
}
})
@@ -0,0 +1,11 @@
// |jit-test| --unboxed-arrays
let moduleRepo = {};
setModuleResolveHook(function(module, specifier) {
return moduleRepo[specifier];
});
setJitCompilerOption("ion.warmup.trigger", 50);
s = "";
for (i = 0; i < 1024; i++) s += "export let e" + i + "\n";
moduleRepo['a'] = parseModule(s);
parseModule("import * as ns from 'a'").declarationInstantiation();
+2 -1
View File
@@ -398,6 +398,7 @@ MSG_DEF(JSMSG_PROXY_EXTENSIBILITY, 0, JSEXN_TYPEERR, "proxy must report same
MSG_DEF(JSMSG_PROXY_GETOWN_OBJORUNDEF, 0, JSEXN_TYPEERR, "proxy [[GetOwnProperty]] must return an object or undefined")
MSG_DEF(JSMSG_PROXY_REVOKED, 0, JSEXN_TYPEERR, "illegal operation attempted on a revoked proxy")
MSG_DEF(JSMSG_PROXY_ARG_REVOKED, 1, JSEXN_TYPEERR, "argument {0} cannot be a revoked proxy")
MSG_DEF(JSMSG_DEPRECATED_PROXY_CREATE, 0, JSEXN_NONE, "Proxy.create and Proxy.createFunction are deprecated, use new Proxy instead")
// Structured cloning
MSG_DEF(JSMSG_SC_BAD_CLONE_VERSION, 0, JSEXN_ERR, "unsupported structured clone version")
@@ -424,7 +425,7 @@ MSG_DEF(JSMSG_DEBUG_NOT_LIVE, 1, JSEXN_ERR, "{0} is not live")
MSG_DEF(JSMSG_DEBUG_NO_SCOPE_OBJECT, 0, JSEXN_TYPEERR, "declarative Environments don't have binding objects")
MSG_DEF(JSMSG_DEBUG_OBJECT_PROTO, 0, JSEXN_TYPEERR, "Debugger.Object.prototype is not a valid Debugger.Object")
MSG_DEF(JSMSG_DEBUG_OBJECT_WRONG_OWNER,0, JSEXN_TYPEERR, "Debugger.Object belongs to a different Debugger")
MSG_DEF(JSMSG_DEBUG_OPTIMIZED_OUT, 0, JSEXN_ERR, "variable has been optimized out")
MSG_DEF(JSMSG_DEBUG_OPTIMIZED_OUT, 1, JSEXN_ERR, "variable `{0}' has been optimized out")
MSG_DEF(JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED, 0, JSEXN_TYPEERR, "resumption values are disallowed in this hook")
MSG_DEF(JSMSG_DEBUG_VARIABLE_NOT_FOUND,0, JSEXN_TYPEERR, "variable not found in environment")
MSG_DEF(JSMSG_DEBUG_WRAPPER_IN_WAY, 3, JSEXN_TYPEERR, "{0} is {1}{2}a global object, but a direct reference is required")
@@ -494,6 +494,11 @@ bool
js::proxy_create(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject create(cx, &args.callee());
if (!GlobalObject::warnOnceAboutProxyCreate(cx, create))
return false;
if (args.length() < 1) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"create", "0", "s");
@@ -523,6 +528,11 @@ bool
js::proxy_createFunction(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedObject createFunction(cx, &args.callee());
if (!GlobalObject::warnOnceAboutProxyCreate(cx, createFunction))
return false;
if (args.length() < 2) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"createFunction", "1", "");
@@ -0,0 +1,25 @@
// |reftest| skip-if(!xulRuntime.shell) -- requires serialize()
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
function test()
{
// On my system, with an unfixed build where transfer-list processing is
// quadratic, 5e5 elements makes this test take ~70s in a shell opt build.
// Debug build is well into timeout-land at 300+s. As long as at least *one*
// platform times out for a quadratic algorithm, a regression should be
// obvious. (Time to run the test in even a debug shell is ~17s, well short
// of timing out.)
var transfers = [];
for (var i = 0; i < 5e5; i++)
transfers.push(new ArrayBuffer());
// If serialization is quadratic in the length of |transfers|, the test will
// time out. If the test doesn't time out, it passed.
serialize({}, transfers);
}
test();
if (typeof reportCompare === "function")
reportCompare(0, 0, 'ok');
+118
View File
@@ -0,0 +1,118 @@
// |reftest| skip-if(!xulRuntime.shell)
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
if (!(this.SharedArrayBuffer && this.Atomics)) {
reportCompare(true,true);
quit(0);
}
// Checks for parameter validation of futex API. ALl of these test
// cases should throw exceptions during parameter validation, before
// we check whether any waiting should be done.
let ab = new ArrayBuffer(16);
let sab = new SharedArrayBuffer(16);
//////////////////////////////////////////////////////////////////////
//
// The view must be an Int32Array on a SharedArrayBuffer.
// Check against non-TypedArray cases.
{
let values = [null,
undefined,
true,
false,
new Boolean(true),
10,
3.14,
new Number(4),
"Hi there",
new Date,
/a*utomaton/g,
{ password: "qumquat" },
new DataView(new ArrayBuffer(10)),
new ArrayBuffer(128),
new SharedArrayBuffer(128),
new Error("Ouch"),
[1,1,2,3,5,8],
((x) => -x),
new Map(),
new Set(),
new WeakMap(),
new WeakSet(),
this.Promise ? new Promise(() => "done") : null,
Symbol("halleluja"),
// TODO: Proxy?
Object,
Int32Array,
Date,
Math,
Atomics ];
for ( let i=0 ; i < values.length ; i++ ) {
let view = values[i];
assertThrowsInstanceOf(() => Atomics.futexWait(view, 0, 0), TypeError);
assertThrowsInstanceOf(() => Atomics.futexWake(view, 0), TypeError);
assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 0, 1, 0), TypeError);
}
}
// Check against TypedArray on non-shared memory cases.
{
let views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array];
for ( let View of views ) {
let view = new View(ab);
assertThrowsInstanceOf(() => Atomics.futexWait(view, 0, 0), TypeError);
assertThrowsInstanceOf(() => Atomics.futexWake(view, 0), TypeError);
assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 0, 1, 0), TypeError);
}
}
// Check against TypedArray on shared memory, but wrong view type
{
let views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Uint32Array,
Uint8ClampedArray, Float32Array, Float64Array];
for ( let View of views ) {
let view = new View(sab);
assertThrowsInstanceOf(() => Atomics.futexWait(view, 0, 0), TypeError);
assertThrowsInstanceOf(() => Atomics.futexWake(view, 0), TypeError);
assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 0, 1, 0), TypeError);
}
}
//////////////////////////////////////////////////////////////////////
//
// The indices must be in the range of the array
{
let view = new Int32Array(sab);
let indices = [ (view) => -1,
(view) => view.length,
(view) => view.length*2,
(view) => undefined,
(view) => '3.5',
(view) => { password: "qumquat" } ];
for ( let iidx=0 ; iidx < indices.length ; iidx++ ) {
let Idx = indices[iidx](view);
assertThrowsInstanceOf(() => Atomics.futexWait(view, Idx, 10), RangeError);
assertThrowsInstanceOf(() => Atomics.futexWake(view, Idx), RangeError);
assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, Idx, 5, 0, 0), RangeError);
assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 5, Idx, 0), RangeError);
}
}
reportCompare(true,true);
+12 -6
View File
@@ -134,9 +134,10 @@ class GlobalObject : public NativeObject
"global object slot counts are inconsistent");
enum WarnOnceFlag : int32_t {
WARN_WATCH_DEPRECATED = 0x00000001,
WARN_PROTO_SETTING_SLOW = 0x00000002,
WARN_STRING_CONTAINS_DEPRECATED = 0x00000004
WARN_WATCH_DEPRECATED = 1 << 0,
WARN_PROTO_SETTING_SLOW = 1 << 1,
WARN_STRING_CONTAINS_DEPRECATED = 1 << 2,
WARN_PROXY_CREATE_DEPRECATED = 1 << 3,
};
// Emit the specified warning if the given slot in |obj|'s global isn't
@@ -531,8 +532,7 @@ class GlobalObject : public NativeObject
}
public:
static NativeObject* getOrCreateIteratorPrototype(JSContext* cx,
Handle<GlobalObject*> global)
static NativeObject* getOrCreateIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global)
{
return MaybeNativeObject(global->getOrCreateObject(cx, ITERATOR_PROTO, initIteratorProto));
}
@@ -707,11 +707,17 @@ class GlobalObject : public NativeObject
}
// Warn about use of the deprecated String.prototype.contains method
static bool warnOnceAboutStringContains(JSContext *cx, HandleObject strContains) {
static bool warnOnceAboutStringContains(JSContext* cx, HandleObject strContains) {
return warnOnceAbout(cx, strContains, WARN_STRING_CONTAINS_DEPRECATED,
JSMSG_DEPRECATED_STRING_CONTAINS);
}
// Warn about uses of Proxy.create and Proxy.createFunction
static bool warnOnceAboutProxyCreate(JSContext* cx, HandleObject create) {
return warnOnceAbout(cx, create, WARN_PROXY_CREATE_DEPRECATED,
JSMSG_DEPRECATED_PROXY_CREATE);
}
static bool getOrCreateEval(JSContext* cx, Handle<GlobalObject*> global,
MutableHandleObject eval);
+4 -2
View File
@@ -535,8 +535,9 @@ js::EnqueuePendingParseTasksAfterGC(JSRuntime* rt)
for (size_t i = 0; i < waiting.length(); i++) {
ParseTask* task = waiting[i];
if (task->runtimeMatches(rt)) {
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!newTasks.append(task))
MOZ_CRASH("EnqueuePendingParseTasksAfterGC");
oomUnsafe.crash("EnqueuePendingParseTasksAfterGC");
HelperThreadState().remove(waiting, &i);
}
}
@@ -554,8 +555,9 @@ js::EnqueuePendingParseTasksAfterGC(JSRuntime* rt)
AutoLockHelperThreadState lock;
{
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!HelperThreadState().parseWorklist().appendAll(newTasks))
MOZ_CRASH("EnqueuePendingParseTasksAfterGC");
oomUnsafe.crash("EnqueuePendingParseTasksAfterGC");
}
HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER);
+5 -9
View File
@@ -532,7 +532,7 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp,
}
ObjectGroupFlags initialFlags = 0;
if (!proto.isObject() || proto.toObject()->isNewGroupUnknown())
if (proto.isLazy() || (proto.isObject() && proto.toObject()->isNewGroupUnknown()))
initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
Rooted<TaggedProto> protoRoot(cx, proto);
@@ -569,14 +569,8 @@ ObjectGroup::defaultNewGroup(ExclusiveContext* cx, const Class* clasp,
const JSAtomState& names = cx->names();
if (obj->is<RegExpObject>()) {
AddTypePropertyId(cx, group, nullptr, NameToId(names.source), TypeSet::StringType());
AddTypePropertyId(cx, group, nullptr, NameToId(names.global), TypeSet::BooleanType());
AddTypePropertyId(cx, group, nullptr, NameToId(names.ignoreCase), TypeSet::BooleanType());
AddTypePropertyId(cx, group, nullptr, NameToId(names.multiline), TypeSet::BooleanType());
AddTypePropertyId(cx, group, nullptr, NameToId(names.sticky), TypeSet::BooleanType());
if (obj->is<RegExpObject>())
AddTypePropertyId(cx, group, nullptr, NameToId(names.lastIndex), TypeSet::Int32Type());
}
if (obj->is<StringObject>())
AddTypePropertyId(cx, group, nullptr, NameToId(names.length), TypeSet::Int32Type());
@@ -1309,8 +1303,10 @@ ObjectGroup::newPlainObject(ExclusiveContext* cx, IdValuePair* properties, size_
entry.types = types;
ObjectGroupCompartment::PlainObjectTable::AddPtr np = table->lookupForAdd(lookup);
if (!table->add(np, key, entry))
if (!table->add(np, key, entry)) {
ReportOutOfMemory(cx);
return nullptr;
}
ids.forget();
types.forget();
+2 -3
View File
@@ -30,9 +30,8 @@ static void
resc_trace(JSTracer* trc, JSObject* obj)
{
void* pdata = obj->as<RegExpStaticsObject>().getPrivate();
MOZ_ASSERT(pdata);
RegExpStatics* res = static_cast<RegExpStatics*>(pdata);
res->mark(trc);
if (pdata)
static_cast<RegExpStatics*>(pdata)->mark(trc);
}
const Class RegExpStaticsObject::class_ = {
+1 -1
View File
@@ -566,7 +566,7 @@ InvokeInterruptCallback(JSContext* cx)
// invoke the onStep handler.
if (cx->compartment()->isDebuggee()) {
ScriptFrameIter iter(cx);
if (iter.script()->stepModeEnabled()) {
if (!iter.done() && iter.script()->stepModeEnabled()) {
RootedValue rval(cx);
switch (Debugger::onSingleStep(cx, &rval)) {
case JSTRAP_ERROR:
+16 -13
View File
@@ -27,6 +27,7 @@
#include "js/Vector.h"
#include "vm/Debugger.h"
#include "vm/SavedFrame.h"
#include "vm/SPSProfiler.h"
#include "vm/StringBuffer.h"
#include "vm/Time.h"
#include "vm/WrapperObject.h"
@@ -155,6 +156,10 @@ struct SavedFrame::Lookup {
MOZ_ASSERT(source);
MOZ_ASSERT_IF(framePtr.isSome(), pc);
MOZ_ASSERT_IF(framePtr.isSome(), activation);
#ifdef JS_MORE_DETERMINISTIC
column = 0;
#endif
}
explicit Lookup(SavedFrame& savedFrame)
@@ -421,6 +426,9 @@ SavedFrame::initLine(uint32_t line)
void
SavedFrame::initColumn(uint32_t column)
{
#ifdef JS_MORE_DETERMINISTIC
column = 0;
#endif
initReservedSlot(JSSLOT_COLUMN, PrivateUint32Value(column));
}
@@ -485,7 +493,8 @@ SavedFrame::create(JSContext* cx)
return nullptr;
assertSameCompartment(cx, proto);
RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto));
RootedObject frameObj(cx, NewObjectWithGivenProto(cx, &SavedFrame::class_, proto,
TenuredObject));
if (!frameObj)
return nullptr;
@@ -988,10 +997,8 @@ SavedFrame::toStringMethod(JSContext* cx, unsigned argc, Value* vp)
bool
SavedStacks::init()
{
if (!pcLocationMap.init())
return false;
return frames.init();
return frames.init() &&
pcLocationMap.init();
}
bool
@@ -1008,6 +1015,7 @@ SavedStacks::saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame, unsi
return true;
}
AutoSPSEntry psuedoFrame(cx->runtime(), "js::SavedStacks::saveCurrentStack");
FrameIter iter(cx, FrameIter::ALL_CONTEXTS, FrameIter::GO_THROUGH_SAVED);
return insertFrames(cx, iter, frame, maxFrameCount);
}
@@ -1037,13 +1045,7 @@ SavedStacks::sweep()
void
SavedStacks::trace(JSTracer* trc)
{
if (pcLocationMap.initialized()) {
// Mark each of the source strings in our pc to location cache.
for (PCLocationMap::Enum e(pcLocationMap); !e.empty(); e.popFront()) {
LocationValue& loc = e.front().value();
TraceEdge(trc, &loc.source, "SavedStacks::PCLocationMap's memoized script source name");
}
}
pcLocationMap.trace(trc);
}
uint32_t
@@ -1062,7 +1064,8 @@ SavedStacks::clear()
size_t
SavedStacks::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
{
return frames.sizeOfExcludingThis(mallocSizeOf);
return frames.sizeOfExcludingThis(mallocSizeOf) +
pcLocationMap.sizeOfExcludingThis(mallocSizeOf);
}
bool
+13 -1
View File
@@ -223,6 +223,7 @@ class SavedStacks {
RelocatablePtrScript script;
jsbytecode* pc;
void trace(JSTracer* trc) { /* PCKey is weak. */ }
bool needsSweep() { return IsAboutToBeFinalized(&script); }
};
@@ -239,8 +240,12 @@ class SavedStacks {
}
bool needsSweep() {
// LocationValue is always held strongly, but in a weak map.
// Assert that it has been marked already, but allow it to be
// ejected from the map when the key dies.
MOZ_ASSERT(source);
return !IsAboutToBeFinalized(&source);
MOZ_ASSERT(!IsAboutToBeFinalized(&source));
return true;
}
RelocatablePtrAtom source;
@@ -282,6 +287,13 @@ class SavedStacks {
}
};
// We eagerly Atomize the script source stored in LocationValue because
// asm.js does not always have a JSScript and the source might not be
// available when we need it later. However, since the JSScript does not
// actually hold this atom, we have to trace it strongly to keep it alive.
// Thus, it takes two GC passes to fully clean up this table: the first GC
// removes the dead script; the second will clear out the source atom since
// it is no longer held by the table.
using PCLocationMap = GCHashMap<PCKey, LocationValue, PCLocationHasher, SystemAllocPolicy>;
PCLocationMap pcLocationMap;
+40 -15
View File
@@ -1663,6 +1663,16 @@ LiveScopeVal::staticAsserts()
namespace {
static void
ReportOptimizedOut(JSContext* cx, HandleId id)
{
JSAutoByteString printable;
if (ValueToPrintable(cx, IdToValue(id), &printable)) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT,
printable.ptr());
}
}
/*
* DebugScopeProxy is the handler for DebugScopeObject proxy objects. Having a
* custom handler (rather than trying to reuse js::Wrapper) gives us several
@@ -1802,7 +1812,15 @@ class DebugScopeProxy : public BaseProxyHandler
TypeScript::SetArgument(cx, script, i, vp);
}
*accessResult = ACCESS_UNALIASED;
// It is possible that an optimized out value flows to this
// location due to Debugger.Frame.prototype.eval operating on a
// live bailed-out Baseline frame. In that case, treat the access
// as lost.
if (vp.isMagic() && vp.whyMagic() == JS_OPTIMIZED_OUT)
*accessResult = ACCESS_LOST;
else
*accessResult = ACCESS_UNALIASED;
return true;
}
@@ -1847,7 +1865,12 @@ class DebugScopeProxy : public BaseProxyHandler
}
}
*accessResult = ACCESS_UNALIASED;
// See comment above in analogous CallObject case.
if (vp.isMagic() && vp.whyMagic() == JS_OPTIMIZED_OUT)
*accessResult = ACCESS_LOST;
else
*accessResult = ACCESS_UNALIASED;
return true;
}
@@ -2078,7 +2101,7 @@ class DebugScopeProxy : public BaseProxyHandler
case ACCESS_GENERIC:
return JS_GetOwnPropertyDescriptorById(cx, scope, id, desc);
case ACCESS_LOST:
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT);
ReportOptimizedOut(cx, id);
return false;
default:
MOZ_CRASH("bad AccessResult");
@@ -2142,7 +2165,7 @@ class DebugScopeProxy : public BaseProxyHandler
case ACCESS_GENERIC:
return GetProperty(cx, scope, scope, id, vp);
case ACCESS_LOST:
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_OPTIMIZED_OUT);
ReportOptimizedOut(cx, id);
return false;
default:
MOZ_CRASH("bad AccessResult");
@@ -2306,8 +2329,11 @@ class DebugScopeProxy : public BaseProxyHandler
*bp = true;
return true;
}
if (isThis(cx, id) && isFunctionScopeWithThis(scopeObj)) {
*bp = true;
// Be careful not to look up '.this' as a normal binding below, it will
// assert in with_HasProperty.
if (isThis(cx, id)) {
*bp = isFunctionScopeWithThis(scopeObj);
return true;
}
@@ -2449,7 +2475,7 @@ DebugScopes::DebugScopes(JSContext* cx)
DebugScopes::~DebugScopes()
{
MOZ_ASSERT(missingScopes.empty());
MOZ_ASSERT_IF(missingScopes.initialized(), missingScopes.empty());
}
bool
@@ -2549,15 +2575,14 @@ DebugScopes::ensureCompartmentData(JSContext* cx)
if (c->debugScopes)
return c->debugScopes;
c->debugScopes = cx->runtime()->new_<DebugScopes>(cx);
if (c->debugScopes && c->debugScopes->init())
return c->debugScopes;
AutoInitGCManagedObject<DebugScopes> debugScopes(cx->make_unique<DebugScopes>(cx));
if (!debugScopes || !debugScopes->init()) {
ReportOutOfMemory(cx);
return nullptr;
}
if (c->debugScopes)
js_delete<DebugScopes>(c->debugScopes);
c->debugScopes = nullptr;
ReportOutOfMemory(cx);
return nullptr;
c->debugScopes = debugScopes.release();
return c->debugScopes;
}
DebugScopeObject*
+67 -40
View File
@@ -137,6 +137,18 @@ intrinsic_IsConstructor(JSContext* cx, unsigned argc, Value* vp)
return true;
}
template<typename T>
static bool
intrinsic_IsInstanceOfBuiltin(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
args.rval().setBoolean(args[0].toObject().is<T>());
return true;
}
/**
* Self-hosting intrinsic returning the original constructor for a builtin
* the name of which is the first and only argument.
@@ -172,18 +184,6 @@ intrinsic_GetBuiltinConstructor(JSContext* cx, unsigned argc, Value* vp)
return true;
}
template<typename T>
static bool
intrinsic_IsInstanceOfBuiltin(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 1);
MOZ_ASSERT(args[0].isObject());
args.rval().setBoolean(args[0].toObject().is<T>());
return true;
}
static bool
intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp)
{
@@ -301,6 +301,29 @@ intrinsic_AssertionFailed(JSContext* cx, unsigned argc, Value* vp)
return false;
}
/**
* Dumps a message to stderr, after stringifying it. Doesn't append a newline.
*/
static bool
intrinsic_DumpMessage(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
#ifdef DEBUG
if (args.length() > 0) {
// try to dump the informative string
JSString* str = ToString<CanGC>(cx, args[0]);
if (str) {
str->dumpCharsNoNewline();
fputc('\n', stderr);
} else {
cx->recoverFromOutOfMemory();
}
}
#endif
args.rval().setUndefined();
return true;
}
static bool
intrinsic_MakeConstructible(JSContext* cx, unsigned argc, Value* vp)
{
@@ -363,9 +386,11 @@ intrinsic_FinishBoundFunctionInit(JSContext* cx, unsigned argc, Value* vp)
if (targetObj->isConstructor())
bound->setIsConstructor();
// 9.4.1.3 BoundFunctionCreate, steps 2-3,8.
// 9.4.1.3 BoundFunctionCreate, Steps 2-3., 8.
RootedObject proto(cx);
GetPrototype(cx, targetObj, &proto);
if (!GetPrototype(cx, targetObj, &proto))
return false;
if (bound->getProto() != proto) {
if (!SetPrototype(cx, bound, proto))
return false;
@@ -373,6 +398,8 @@ intrinsic_FinishBoundFunctionInit(JSContext* cx, unsigned argc, Value* vp)
bound->setExtendedSlot(BOUND_FUN_LENGTH_SLOT, args[2]);
MOZ_ASSERT(!bound->hasGuessedAtom());
// 9.2.11 SetFunctionName, Step 6.
RootedAtom name(cx, AtomizeString(cx, args[3].toString()));
if (!name)
return false;
@@ -608,26 +635,6 @@ intrinsic_NewStringIterator(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static bool
intrinsic_SetCanonicalName(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
MOZ_ASSERT(fun->isSelfHostedBuiltin());
RootedAtom atom(cx, AtomizeString(cx, args[1].toString()));
if (!atom)
return false;
fun->setAtom(atom);
#ifdef DEBUG
fun->setExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT, BooleanValue(true));
#endif
args.rval().setUndefined();
return true;
}
static bool
intrinsic_NewListIterator(JSContext* cx, unsigned argc, Value* vp)
{
@@ -659,6 +666,26 @@ intrinsic_ActiveFunction(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static bool
intrinsic_SetCanonicalName(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
MOZ_ASSERT(fun->isSelfHostedBuiltin());
RootedAtom atom(cx, AtomizeString(cx, args[1].toString()));
if (!atom)
return false;
fun->setAtom(atom);
#ifdef DEBUG
fun->setExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT, BooleanValue(true));
#endif
args.rval().setUndefined();
return true;
}
static bool
intrinsic_StarGeneratorObjectIsClosed(JSContext* cx, unsigned argc, Value* vp)
{
@@ -1637,7 +1664,7 @@ intrinsic_NewModuleNamespace(JSContext* cx, unsigned argc, Value* vp)
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
RootedModuleObject module(cx, &args[0].toObject().as<ModuleObject>());
RootedArrayObject exports(cx, &args[1].toObject().as<ArrayObject>());
RootedObject exports(cx, &args[1].toObject());
RootedObject namespace_(cx, ModuleObject::createNamespace(cx, module, exports));
if (!namespace_)
return false;
@@ -1769,6 +1796,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("ThrowTypeError", intrinsic_ThrowTypeError, 4,0),
JS_FN("ThrowSyntaxError", intrinsic_ThrowSyntaxError, 4,0),
JS_FN("AssertionFailed", intrinsic_AssertionFailed, 1,0),
JS_FN("DumpMessage", intrinsic_DumpMessage, 1,0),
JS_FN("OwnPropertyKeys", intrinsic_OwnPropertyKeys, 1,0),
JS_FN("MakeDefaultConstructor", intrinsic_MakeDefaultConstructor, 2,0),
JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0),
@@ -1801,7 +1829,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_FN("GetIteratorPrototype", intrinsic_GetIteratorPrototype, 0,0),
JS_FN("NewArrayIterator", intrinsic_NewArrayIterator, 0,0),
JS_FN("_SetCanonicalName", intrinsic_SetCanonicalName, 2,0),
JS_FN("CallArrayIteratorMethodIfWrapped",
CallNonGenericSelfhostedMethod<Is<ArrayIteratorObject>>, 2,0),
@@ -1810,6 +1837,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
CallNonGenericSelfhostedMethod<Is<ListIteratorObject>>, 2,0),
JS_FN("ActiveFunction", intrinsic_ActiveFunction, 0,0),
JS_FN("_SetCanonicalName", intrinsic_SetCanonicalName, 2,0),
JS_INLINABLE_FN("IsArrayIterator",
intrinsic_IsInstanceOfBuiltin<ArrayIteratorObject>, 1,0,
IntrinsicIsArrayIterator),
@@ -2096,9 +2125,7 @@ JSRuntime::initSelfHosting(JSContext* cx)
char* filename = getenv("MOZ_SELFHOSTEDJS");
if (filename) {
RootedScript script(cx);
if (Compile(cx, options, filename, &script))
ok = Execute(cx, script, *shg.get(), rv.address());
ok = ok && Evaluate(cx, options, filename, &rv);
} else {
uint32_t srcLen = GetRawScriptsSize();
@@ -2382,7 +2409,7 @@ JSRuntime::createLazySelfHostedFunctionClone(JSContext* cx, HandlePropertyName s
if (!selfHostedFun)
return false;
if (selfHostedFun->atom() != selfHostedName) {
if (!selfHostedFun->hasGuessedAtom() && selfHostedFun->atom() != selfHostedName) {
MOZ_ASSERT(selfHostedFun->getExtendedSlot(HAS_SELFHOSTED_CANONICAL_NAME_SLOT).toBoolean());
funName = selfHostedFun->atom();
}
+22 -29
View File
@@ -62,8 +62,8 @@ InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script, AbstractF
prevpc_ = nullptr;
prevsp_ = nullptr;
MOZ_ASSERT_IF(evalInFramePrev, isDebuggerEvalFrame());
evalInFramePrev_ = evalInFramePrev;
MOZ_ASSERT_IF(evalInFramePrev, isDebuggerEvalFrame());
if (script->isDebuggee())
setIsDebuggee();
@@ -357,33 +357,17 @@ InterpreterFrame::popWith(JSContext* cx)
}
void
InterpreterFrame::mark(JSTracer* trc)
InterpreterFrame::trace(JSTracer* trc, Value* sp, jsbytecode* pc)
{
/*
* Normally we would use MarkRoot here, except that generators also take
* this path. However, generators use a special write barrier when the stack
* frame is copied to the floating frame. Therefore, no barrier is needed.
*/
TraceManuallyBarrieredEdge(trc, &scopeChain_, "scope chain");
TraceRoot(trc, &scopeChain_, "scope chain");
TraceRoot(trc, &script_, "script");
if (flags_ & HAS_ARGS_OBJ)
TraceManuallyBarrieredEdge(trc, &argsObj_, "arguments");
TraceManuallyBarrieredEdge(trc, &script_, "script");
if (trc->isMarkingTracer())
script()->compartment()->zone()->active = true;
TraceRoot(trc, &argsObj_, "arguments");
if (hasReturnValue())
TraceManuallyBarrieredEdge(trc, &rval_, "rval");
}
TraceRoot(trc, &rval_, "rval");
void
InterpreterFrame::markValues(JSTracer* trc, unsigned start, unsigned end)
{
if (start < end)
TraceRootRange(trc, end - start, slots() + start, "vm_stack");
}
void
InterpreterFrame::markValues(JSTracer* trc, Value* sp, jsbytecode* pc)
{
MOZ_ASSERT(sp >= slots());
if (hasArgs()) {
@@ -406,18 +390,28 @@ InterpreterFrame::markValues(JSTracer* trc, Value* sp, jsbytecode* pc)
if (nfixed == nlivefixed) {
// All locals are live.
markValues(trc, 0, sp - slots());
traceValues(trc, 0, sp - slots());
} else {
// Mark operand stack.
markValues(trc, nfixed, sp - slots());
traceValues(trc, nfixed, sp - slots());
// Clear dead block-scoped locals.
while (nfixed > nlivefixed)
unaliasedLocal(--nfixed).setMagic(JS_UNINITIALIZED_LEXICAL);
// Mark live locals.
markValues(trc, 0, nlivefixed);
traceValues(trc, 0, nlivefixed);
}
if (trc->isMarkingTracer())
script->compartment()->zone()->active = true;
}
void
InterpreterFrame::traceValues(JSTracer* trc, unsigned start, unsigned end)
{
if (start < end)
TraceRootRange(trc, end - start, slots() + start, "vm_stack");
}
static void
@@ -425,8 +419,7 @@ MarkInterpreterActivation(JSTracer* trc, InterpreterActivation* act)
{
for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) {
InterpreterFrame* fp = frames.frame();
fp->markValues(trc, frames.sp(), frames.pc());
fp->mark(trc);
fp->trace(trc, frames.sp(), frames.pc());
}
}
+2 -3
View File
@@ -787,9 +787,8 @@ class InterpreterFrame
}
public:
void mark(JSTracer* trc);
void markValues(JSTracer* trc, unsigned start, unsigned end);
void markValues(JSTracer* trc, Value* sp, jsbytecode* pc);
void trace(JSTracer* trc, Value* sp, jsbytecode* pc);
void traceValues(JSTracer* trc, unsigned start, unsigned end);
// Entered Baseline/Ion from the interpreter.
bool runningInJit() const {
+26 -9
View File
@@ -40,6 +40,14 @@ PerformanceMonitoring::reset()
// be overwritten progressively during the execution.
++iteration_;
recentGroups_.clear();
// Every so often, we will be rescheduled to another CPU. If this
// happens, we may end up with an entirely unsynchronized
// timestamp counter. If we do not reset
// `highestTimestampCounter_`, we could end up ignoring entirely
// valid sets of measures just because we are on a CPU that has a
// lower RDTSC.
highestTimestampCounter_ = 0;
}
void
@@ -158,6 +166,19 @@ PerformanceMonitoring::commit()
return success;
}
uint64_t
PerformanceMonitoring::monotonicReadTimestampCounter()
{
#if defined(MOZ_HAVE_RDTSC)
const uint64_t hardware = ReadTimestampCounter();
if (highestTimestampCounter_ < hardware)
highestTimestampCounter_ = hardware;
return highestTimestampCounter_;
#else
return 0;
#endif // defined(MOZ_HAVE_RDTSC)
}
void
PerformanceMonitoring::dispose(JSRuntime* rt)
{
@@ -272,7 +293,7 @@ AutoStopwatch::enter()
}
if (runtime->performanceMonitoring.isMonitoringJank()) {
cyclesStart_ = this->getCycles();
cyclesStart_ = this->getCycles(runtime);
cpuStart_ = this->getCPU();
isMonitoringJank_ = true;
}
@@ -295,8 +316,8 @@ AutoStopwatch::exit()
// limited.
const cpuid_t cpuEnd = this->getCPU();
if (isSameCPU(cpuStart_, cpuEnd)) {
const uint64_t cyclesEnd = getCycles();
cyclesDelta = getDelta(cyclesEnd, cyclesStart_);
const uint64_t cyclesEnd = getCycles(runtime);
cyclesDelta = cyclesEnd - cyclesStart_; // Always >= 0 by definition of `getCycles`.
}
#if WINVER >= 0x600
updateTelemetry(cpuStart_, cpuEnd);
@@ -382,13 +403,9 @@ AutoStopwatch::getDelta(const uint64_t end, const uint64_t start) const
}
uint64_t
AutoStopwatch::getCycles() const
AutoStopwatch::getCycles(JSRuntime* runtime) const
{
#if defined(MOZ_HAVE_RDTSC)
return ReadTimestampCounter();
#else
return 0;
#endif // defined(MOZ_HAVE_RDTSC)
return runtime->performanceMonitoring.monotonicReadTimestampCounter();
}
cpuid_t inline
+15 -1
View File
@@ -85,6 +85,7 @@ struct PerformanceMonitoring {
, isMonitoringCPOW_(false)
, iteration_(0)
, startedAtIteration_(0)
, highestTimestampCounter_(0)
{ }
/**
@@ -211,6 +212,13 @@ struct PerformanceMonitoring {
*/
uint64_t totalCPOWTime;
/**
* A variant of RDTSC artificially made monotonic.
*
* Always return 0 on platforms that do not support RDTSC.
*/
uint64_t monotonicReadTimestampCounter();
/**
* Data extracted by the AutoStopwatch to determine how often
* we reschedule the process to a different CPU during the
@@ -285,6 +293,12 @@ struct PerformanceMonitoring {
* Groups used in the current iteration.
*/
GroupVector recentGroups_;
/**
* The highest value of the timestamp counter encountered
* during this iteration.
*/
uint64_t highestTimestampCounter_;
};
#if WINVER >= 0x0600
@@ -377,7 +391,7 @@ class AutoStopwatch final {
// Return the value of the Timestamp Counter, as provided by the CPU.
// 0 on platforms for which we do not have access to a Timestamp Counter.
uint64_t inline getCycles() const;
uint64_t inline getCycles(JSRuntime*) const;
// Return the identifier of the current CPU, on platforms for which we have
+34 -17
View File
@@ -279,7 +279,8 @@ struct JSStructuredCloneWriter {
: out(cx), objs(out.context()),
counts(out.context()), entries(out.context()),
memory(out.context(), CloneMemory(out.context())), callbacks(cb),
closure(cbClosure), transferable(out.context(), tVal), transferableObjects(out.context())
closure(cbClosure), transferable(out.context(), tVal),
transferableObjects(out.context(), GCHashSet<JSObject*>(cx))
{}
~JSStructuredCloneWriter();
@@ -350,9 +351,9 @@ struct JSStructuredCloneWriter {
// Any value passed to JS_WriteStructuredClone.
void* closure;
// List of transferable objects
// Set of transferable objects
RootedValue transferable;
AutoObjectVector transferableObjects;
Rooted<GCHashSet<JSObject*>> transferableObjects;
friend bool JS_WriteString(JSStructuredCloneWriter* w, HandleString str);
friend bool JS_WriteTypedArray(JSStructuredCloneWriter* w, HandleValue v);
@@ -766,10 +767,14 @@ JSStructuredCloneWriter::~JSStructuredCloneWriter()
bool
JSStructuredCloneWriter::parseTransferable()
{
MOZ_ASSERT(transferableObjects.empty(), "parseTransferable called with stale data");
// NOTE: The transferables set is tested for non-emptiness at various
// junctures in structured cloning, so this set must be initialized
// by this method in all non-error cases.
MOZ_ASSERT(!transferableObjects.initialized(),
"parseTransferable called with stale data");
if (transferable.isNull() || transferable.isUndefined())
return true;
return transferableObjects.init(0);
if (!transferable.isObject())
return reportErrorTransferable(JS_SCERR_TRANSFERABLE);
@@ -783,26 +788,36 @@ JSStructuredCloneWriter::parseTransferable()
return reportErrorTransferable(JS_SCERR_TRANSFERABLE);
uint32_t length;
if (!JS_GetArrayLength(cx, array, &length)) {
if (!JS_GetArrayLength(cx, array, &length))
return false;
}
// Initialize the set for the provided array's length.
if (!transferableObjects.init(length))
return false;
if (length == 0)
return true;
RootedValue v(context());
RootedObject tObj(context());
for (uint32_t i = 0; i < length; ++i) {
if (!CheckForInterrupt(cx))
return false;
if (!JS_GetElement(cx, array, i, &v))
return false;
if (!v.isObject())
return reportErrorTransferable(JS_SCERR_TRANSFERABLE);
RootedObject tObj(context(), &v.toObject());
tObj = &v.toObject();
// No duplicates allowed
if (std::find(transferableObjects.begin(), transferableObjects.end(), tObj) != transferableObjects.end()) {
auto p = transferableObjects.lookupForAdd(tObj);
if (p)
return reportErrorTransferable(JS_SCERR_DUP_TRANSFERABLE);
}
if (!transferableObjects.append(tObj))
if (!transferableObjects.add(p, tObj))
return false;
}
@@ -1228,11 +1243,12 @@ JSStructuredCloneWriter::writeTransferMap()
if (!out.writePair(SCTAG_TRANSFER_MAP_HEADER, (uint32_t)SCTAG_TM_UNREAD))
return false;
if (!out.write(transferableObjects.length()))
if (!out.write(transferableObjects.count()))
return false;
for (JS::AutoObjectVector::Range tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
JSObject* obj = tr.front();
RootedObject obj(context());
for (auto tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
obj = tr.front();
if (!memory.put(obj, memory.count()))
return false;
@@ -1262,11 +1278,12 @@ JSStructuredCloneWriter::transferOwnership()
uint64_t* point = out.rawBuffer();
MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point) >> 32) == SCTAG_TRANSFER_MAP_HEADER);
point++;
MOZ_ASSERT(LittleEndian::readUint64(point) == transferableObjects.length());
MOZ_ASSERT(LittleEndian::readUint64(point) == transferableObjects.count());
point++;
for (JS::AutoObjectVector::Range tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
RootedObject obj(context(), tr.front());
RootedObject obj(context());
for (auto tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
obj = tr.front();
uint32_t tag;
JS::TransferableOwnership ownership;
+7 -2
View File
@@ -3143,7 +3143,11 @@ js::AddClearDefiniteGetterSetterForPrototypeChain(JSContext* cx, ObjectGroup* gr
RootedObject proto(cx, group->proto().toObjectOrNull());
while (proto) {
ObjectGroup* protoGroup = proto->getGroup(cx);
if (!protoGroup || protoGroup->unknownProperties())
if (!protoGroup) {
cx->recoverFromOutOfMemory();
return false;
}
if (protoGroup->unknownProperties())
return false;
HeapTypeSet* protoTypes = protoGroup->getProperty(cx, proto, id);
if (!protoTypes || protoTypes->nonDataProperty() || protoTypes->nonWritableProperty())
@@ -3925,8 +3929,9 @@ TypeNewScript::rollbackPartiallyInitializedObjects(JSContext* cx, ObjectGroup* g
Vector<uint32_t, 32> pcOffsets(cx);
for (ScriptFrameIter iter(cx); !iter.done(); ++iter) {
{
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!pcOffsets.append(iter.script()->pcToOffset(iter.pc())))
MOZ_CRASH("rollbackPartiallyInitializedObjects");
oomUnsafe.crash("rollbackPartiallyInitializedObjects");
}
if (!iter.isConstructing()) {
+2 -2
View File
@@ -31,11 +31,11 @@ namespace js {
*
* (If you're wondering, 0xb973c0de is used because it looks like "bytecode".)
*/
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 334;
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 335;
static const uint32_t XDR_BYTECODE_VERSION =
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
static_assert(JSErr_Limit == 444,
static_assert(JSErr_Limit == 445,
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
"removed MSG_DEFs from js.msg, you should increment "
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
+3 -1
View File
@@ -8548,7 +8548,9 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
#ifdef ACCESSIBILITY
// Schedule the update of accessible tree since rendered text might be changed.
ReflowTextA11yNotifier(presContext, mContent);
if (StyleVisibility()->IsVisible()) {
ReflowTextA11yNotifier(presContext, mContent);
}
#endif
/////////////////////////////////////////////////////////////////////
+1
View File
@@ -84,6 +84,7 @@
#define AUDIO_AMR "audio/amr"
#define AUDIO_FLAC "audio/flac"
#define AUDIO_3GPP "audio/3gpp"
#define AUDIO_3GPP2 "audio/3gpp2"
#define AUDIO_MIDI "audio/x-midi"
#define AUDIO_MATROSKA "audio/x-matroska"
+69
View File
@@ -0,0 +1,69 @@
// Test that we get `js::SavedStacks::saveCurrentStack` frames.
function run_test() {
let p = Cc["@mozilla.org/tools/profiler;1"];
// Just skip the test if the profiler component isn't present.
if (!p)
return;
p = p.getService(Ci.nsIProfiler);
if (!p)
return;
const { saveStack } = Cu.getJSTestingFunctions();
const ms = 5;
p.StartProfiler(100, ms, ["js"], 1);
let then = Date.now();
while (Date.now() - then < 30000) {
function a() {
saveStack();
saveStack();
saveStack();
saveStack();
saveStack();
saveStack();
saveStack();
saveStack();
saveStack();
saveStack();
saveStack();
saveStack();
saveStack();
}
a();
a();
a();
a();
a();
function b() {
a();
}
b();
b();
b();
b();
b();
}
var profile = p.getProfileData().threads[0];
do_check_neq(profile.samples.data.length, 0);
let found = false;
for (let sample of profile.samples.data) {
const stack = getInflatedStackLocations(profile, sample);
for (let frame of stack) {
if (frame.indexOf("js::SavedStacks::saveCurrentStack") >= 0) {
found = true;
break;
}
}
}
do_check_true(found);
p.StopProfiler();
}

Some files were not shown because too many files have changed in this diff Show More