mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 13:23:07 +00:00
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:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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() &&
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 +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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,6 +22,7 @@ public:
|
||||
METADATA_AVC,
|
||||
METADATA_AAC,
|
||||
METADATA_AMR,
|
||||
METADATA_EVRC,
|
||||
METADATA_UNKNOWN // Metadata Kind not set
|
||||
};
|
||||
// Return the specific metadata kind
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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() {}
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ EXPORTS += [
|
||||
UNIFIED_SOURCES += [
|
||||
'AMRBox.cpp',
|
||||
'AVCBox.cpp',
|
||||
'EVRCBox.cpp',
|
||||
'ISOControl.cpp',
|
||||
'ISOMediaBoxes.cpp',
|
||||
'ISOMediaWriter.cpp',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!");
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -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");
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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.
@@ -0,0 +1 @@
|
||||
Cache-Control: no-store
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
Cache-Control: no-store
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
})();
|
||||
});
|
||||
@@ -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();
|
||||
@@ -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
@@ -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');
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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_ = {
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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 "
|
||||
|
||||
@@ -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
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user