mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to fx-team.
This commit is contained in:
commit
84cac0677c
@ -10,6 +10,7 @@
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsIAccessibleTypes.h"
|
||||
#include "DocAccessible.h"
|
||||
#include "HTMLListAccessible.h"
|
||||
#include "Role.h"
|
||||
#include "States.h"
|
||||
#include "TextAttrs.h"
|
||||
@ -238,7 +239,7 @@ HyperTextAccessible::DOMPointToOffset(nsINode* aNode, int32_t aNodeOffset,
|
||||
if (!aNode)
|
||||
return 0;
|
||||
|
||||
uint32_t addTextOffset = 0;
|
||||
uint32_t offset = 0;
|
||||
nsINode* findNode = nullptr;
|
||||
|
||||
if (aNodeOffset == -1) {
|
||||
@ -251,7 +252,7 @@ HyperTextAccessible::DOMPointToOffset(nsINode* aNode, int32_t aNodeOffset,
|
||||
nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame();
|
||||
NS_ENSURE_TRUE(frame, 0);
|
||||
|
||||
nsresult rv = ContentToRenderedOffset(frame, aNodeOffset, &addTextOffset);
|
||||
nsresult rv = ContentToRenderedOffset(frame, aNodeOffset, &offset);
|
||||
NS_ENSURE_SUCCESS(rv, 0);
|
||||
// Get the child node and
|
||||
findNode = aNode;
|
||||
@ -304,14 +305,20 @@ HyperTextAccessible::DOMPointToOffset(nsINode* aNode, int32_t aNodeOffset,
|
||||
descendant = GetFirstAvailableAccessible(findNode);
|
||||
}
|
||||
|
||||
// From the descendant, go up and get the immediate child of this hypertext
|
||||
Accessible* childAtOffset = nullptr;
|
||||
return TransformOffset(descendant, offset, aIsEndOffset);
|
||||
}
|
||||
|
||||
int32_t
|
||||
HyperTextAccessible::TransformOffset(Accessible* aDescendant,
|
||||
int32_t aOffset, bool aIsEndOffset) const
|
||||
{
|
||||
// From the descendant, go up and get the immediate child of this hypertext.
|
||||
int32_t offset = aOffset;
|
||||
Accessible* descendant = aDescendant;
|
||||
while (descendant) {
|
||||
Accessible* parent = descendant->Parent();
|
||||
if (parent == this) {
|
||||
childAtOffset = descendant;
|
||||
break;
|
||||
}
|
||||
if (parent == this)
|
||||
return GetChildOffset(descendant) + offset;
|
||||
|
||||
// This offset no longer applies because the passed-in text object is not
|
||||
// a child of the hypertext. This happens when there are nested hypertexts,
|
||||
@ -321,17 +328,16 @@ HyperTextAccessible::DOMPointToOffset(nsINode* aNode, int32_t aNodeOffset,
|
||||
// is not at 0 offset then the returned offset should be after an embedded
|
||||
// character the original point belongs to.
|
||||
if (aIsEndOffset)
|
||||
addTextOffset = (addTextOffset > 0 || descendant->IndexInParent() > 0) ? 1 : 0;
|
||||
offset = (offset > 0 || descendant->IndexInParent() > 0) ? 1 : 0;
|
||||
else
|
||||
addTextOffset = 0;
|
||||
offset = 0;
|
||||
|
||||
descendant = parent;
|
||||
}
|
||||
|
||||
// If the given DOM point cannot be mapped into offset relative this hypertext
|
||||
// If the given a11y point cannot be mapped into offset relative this hypertext
|
||||
// offset then return length as fallback value.
|
||||
return childAtOffset ?
|
||||
GetChildOffset(childAtOffset) + addTextOffset : CharacterCount();
|
||||
return CharacterCount();
|
||||
}
|
||||
|
||||
bool
|
||||
@ -420,6 +426,31 @@ HyperTextAccessible::FindOffset(int32_t aOffset, nsDirection aDirection,
|
||||
return -1;
|
||||
|
||||
child = text->GetChildAt(childIdx);
|
||||
|
||||
// HTML list items may need special processing because PeekOffset doesn't
|
||||
// work with list bullets.
|
||||
if (text->IsHTMLListItem()) {
|
||||
HTMLLIAccessible* li = text->AsHTMLListItem();
|
||||
if (child == li->Bullet()) {
|
||||
// It works only when the bullet is one single char.
|
||||
if (aDirection == eDirPrevious)
|
||||
return text != this ? TransformOffset(text, 0, false) : 0;
|
||||
|
||||
if (aAmount == eSelectEndLine || aAmount == eSelectLine) {
|
||||
if (text != this)
|
||||
return TransformOffset(text, 1, true);
|
||||
|
||||
// Ask a text leaf next (if not empty) to the bullet for an offset
|
||||
// since list item may be multiline.
|
||||
return aOffset + 1 < CharacterCount() ?
|
||||
FindOffset(aOffset + 1, aDirection, aAmount, aWordMovementType) : 1;
|
||||
}
|
||||
|
||||
// Case of word and char boundaries.
|
||||
return text != this ? TransformOffset(text, 1, true) : 1;
|
||||
}
|
||||
}
|
||||
|
||||
innerOffset -= text->GetChildOffset(childIdx);
|
||||
|
||||
text = child->AsHyperText();
|
||||
@ -463,10 +494,16 @@ HyperTextAccessible::FindOffset(int32_t aOffset, nsDirection aDirection,
|
||||
pos.mContentOffset,
|
||||
aDirection == eDirNext);
|
||||
|
||||
// If we reached the end during search, this means we didn't find the DOM point
|
||||
// and we're actually at the start of the paragraph
|
||||
if (hyperTextOffset == CharacterCount() && aDirection == eDirPrevious)
|
||||
return 0;
|
||||
if (aDirection == eDirPrevious) {
|
||||
// If we reached the end during search, this means we didn't find the DOM point
|
||||
// and we're actually at the start of the paragraph
|
||||
if (hyperTextOffset == CharacterCount())
|
||||
return 0;
|
||||
|
||||
// PeekOffset stops right before bullet so return 0 to workaround it.
|
||||
if (IsHTMLListItem() && aAmount == eSelectBeginLine && hyperTextOffset == 1)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return hyperTextOffset;
|
||||
}
|
||||
|
@ -120,6 +120,12 @@ public:
|
||||
int32_t DOMPointToOffset(nsINode* aNode, int32_t aNodeOffset,
|
||||
bool aIsEndOffset = false) const;
|
||||
|
||||
/**
|
||||
* Transform the given a11y point into the offset relative this hypertext.
|
||||
*/
|
||||
int32_t TransformOffset(Accessible* aDescendant, int32_t aOffset,
|
||||
bool aIsEndOffset) const;
|
||||
|
||||
/**
|
||||
* Convert start and end hypertext offsets into DOM range.
|
||||
*
|
||||
@ -435,9 +441,9 @@ protected:
|
||||
* Return an offset corresponding to the given direction and selection amount
|
||||
* relative the given offset. A helper used to find word or line boundaries.
|
||||
*/
|
||||
virtual int32_t FindOffset(int32_t aOffset, nsDirection aDirection,
|
||||
nsSelectionAmount aAmount,
|
||||
EWordMovementType aWordMovementType = eDefaultBehavior);
|
||||
int32_t FindOffset(int32_t aOffset, nsDirection aDirection,
|
||||
nsSelectionAmount aAmount,
|
||||
EWordMovementType aWordMovementType = eDefaultBehavior);
|
||||
|
||||
/**
|
||||
* Return the boundaries of the substring in case of textual frame or
|
||||
|
@ -98,33 +98,6 @@ HTMLLIAccessible::GetBounds(int32_t* aX, int32_t* aY,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
HTMLLIAccessible::FindOffset(int32_t aOffset, nsDirection aDirection,
|
||||
nsSelectionAmount aAmount,
|
||||
EWordMovementType aWordMovementType)
|
||||
{
|
||||
Accessible* child = GetChildAtOffset(aOffset);
|
||||
if (!child)
|
||||
return -1;
|
||||
|
||||
if (child != mBullet) {
|
||||
if (aDirection == eDirPrevious &&
|
||||
(aAmount == eSelectBeginLine || aAmount == eSelectLine))
|
||||
return 0;
|
||||
|
||||
return HyperTextAccessible::FindOffset(aOffset, aDirection,
|
||||
aAmount, aWordMovementType);
|
||||
}
|
||||
|
||||
if (aDirection == eDirPrevious)
|
||||
return 0;
|
||||
|
||||
if (aAmount == eSelectEndLine || aAmount == eSelectLine)
|
||||
return CharacterCount();
|
||||
|
||||
return nsAccUtils::TextLength(child);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// HTMLLIAccessible: public
|
||||
|
||||
|
@ -55,12 +55,8 @@ public:
|
||||
virtual a11y::role NativeRole();
|
||||
virtual uint64_t NativeState();
|
||||
|
||||
// HyperTextAccessible
|
||||
virtual int32_t FindOffset(int32_t aOffset, nsDirection aDirection,
|
||||
nsSelectionAmount aAmount,
|
||||
EWordMovementType aWordMovementType) MOZ_OVERRIDE;
|
||||
|
||||
// HTMLLIAccessible
|
||||
HTMLListBulletAccessible* Bullet() const { return mBullet; }
|
||||
void UpdateBullet(bool aHasBullet);
|
||||
|
||||
protected:
|
||||
|
@ -119,6 +119,18 @@
|
||||
|
||||
testTextAtOffset([ "li1" ], BOUNDARY_LINE_START,
|
||||
[ [ 0, 5, kDiscBulletChar + "Item", 0, 5 ] ]);
|
||||
testTextAtOffset([ "li2" ], BOUNDARY_LINE_START,
|
||||
[ [ 0, 1, kDiscBulletChar, 0, 1 ] ]);
|
||||
testTextAtOffset([ "li3" ], BOUNDARY_LINE_START,
|
||||
[ [ 0, 7, kDiscBulletChar + "a long ", 0, 8 ],
|
||||
[ 8, 11, "and ", 8, 12 ] ]);
|
||||
testTextAtOffset([ "li4" ], BOUNDARY_LINE_START,
|
||||
[ [ 0, 6, kDiscBulletChar + "a " + kEmbedChar + " c", 0, 6 ] ]);
|
||||
testTextAtOffset([ "ul1" ], BOUNDARY_LINE_START,
|
||||
[ [ 0, 0, kEmbedChar, 0, 1 ],
|
||||
[ 1, 1, kEmbedChar, 1, 2 ],
|
||||
[ 2, 2, kEmbedChar, 2, 3 ],
|
||||
[ 3, 4, kEmbedChar, 3, 4 ] ]);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Nested hypertexts
|
||||
@ -193,8 +205,11 @@ two words
|
||||
<p id="ht_4">Hello world
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<ul id="ul1">
|
||||
<li id="li1">Item</li>
|
||||
<li id="li2"></li>
|
||||
<li id="li3" style="width:10ex; font-family:monospace; font-size:10pt;">a long and winding road that lead me to your door</li>
|
||||
<li id="li4">a <a href=''>b</a> c</li>
|
||||
</ul>
|
||||
|
||||
<div id="ht_5">
|
||||
@ -203,5 +218,6 @@ two words
|
||||
<p>seciofarus</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -12,8 +12,8 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ce212ba54f36284db84068f82af0c790ceb2c3ff"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ac8a273809b5b160554e616bc5ef2d6fa026ce0e"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="87aa8679560ce09f6445621d6f370d9de722cdba"/>
|
||||
|
@ -11,8 +11,8 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ce212ba54f36284db84068f82af0c790ceb2c3ff"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac8a273809b5b160554e616bc5ef2d6fa026ce0e"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="788d9ce293a9b44f64536130cf4ad577e8101dbe"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
|
||||
|
@ -12,8 +12,8 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ce212ba54f36284db84068f82af0c790ceb2c3ff"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ac8a273809b5b160554e616bc5ef2d6fa026ce0e"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="87aa8679560ce09f6445621d6f370d9de722cdba"/>
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
"revision": "cea79abbb7a97c0bd67051087bcdf40d25611930",
|
||||
"revision": "0fd4065b6621e3b7409cf90ac30d4e174e7317a6",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -11,8 +11,8 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ce212ba54f36284db84068f82af0c790ceb2c3ff"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ac8a273809b5b160554e616bc5ef2d6fa026ce0e"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
|
@ -10,8 +10,8 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ce212ba54f36284db84068f82af0c790ceb2c3ff"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ac8a273809b5b160554e616bc5ef2d6fa026ce0e"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
|
@ -12,8 +12,8 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ce212ba54f36284db84068f82af0c790ceb2c3ff"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ac8a273809b5b160554e616bc5ef2d6fa026ce0e"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
|
@ -11,8 +11,8 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ce212ba54f36284db84068f82af0c790ceb2c3ff"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ac8a273809b5b160554e616bc5ef2d6fa026ce0e"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
|
@ -11,8 +11,8 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ce212ba54f36284db84068f82af0c790ceb2c3ff"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ac8a273809b5b160554e616bc5ef2d6fa026ce0e"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="788d9ce293a9b44f64536130cf4ad577e8101dbe"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/>
|
||||
|
@ -11,8 +11,8 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ce212ba54f36284db84068f82af0c790ceb2c3ff"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ac8a273809b5b160554e616bc5ef2d6fa026ce0e"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="49c722fa1a5e1873fa0010829fd97d0b74009ca5"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
|
@ -564,7 +564,6 @@
|
||||
|
||||
@BINPATH@/components/Payment.js
|
||||
@BINPATH@/components/PaymentFlowInfo.js
|
||||
@BINPATH@/components/PaymentRequestInfo.js
|
||||
@BINPATH@/components/Payment.manifest
|
||||
|
||||
@BINPATH@/components/DownloadsAPI.js
|
||||
|
@ -288,6 +288,7 @@ run-if = crashreporter
|
||||
[browser_popupNotification.js]
|
||||
skip-if = toolkit == "windows" # Disabled on Windows due to frequent failures (bugs 825739, 841341)
|
||||
[browser_popupUI.js]
|
||||
[browser_printpreview.js]
|
||||
[browser_private_browsing_window.js]
|
||||
[browser_private_no_prompt.js]
|
||||
[browser_relatedTabs.js]
|
||||
|
65
browser/base/content/test/general/browser_printpreview.js
Normal file
65
browser/base/content/test/general/browser_printpreview.js
Normal file
@ -0,0 +1,65 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
ok(!gInPrintPreviewMode,
|
||||
"Should NOT be in print preview mode at starting this tests");
|
||||
// Skip access key test on platforms which don't support access key.
|
||||
if (!/Win|Linux/.test(navigator.platform)) {
|
||||
openPrintPreview(testClosePrintPreviewWithEscKey);
|
||||
} else {
|
||||
openPrintPreview(testClosePrintPreviewWithAccessKey);
|
||||
}
|
||||
}
|
||||
|
||||
function testClosePrintPreviewWithAccessKey() {
|
||||
EventUtils.synthesizeKey("c", { altKey: true });
|
||||
checkPrintPreviewClosed(function (aSucceeded) {
|
||||
ok(aSucceeded,
|
||||
"print preview mode should be finished by access key");
|
||||
openPrintPreview(testClosePrintPreviewWithEscKey);
|
||||
});
|
||||
}
|
||||
|
||||
function testClosePrintPreviewWithEscKey() {
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
checkPrintPreviewClosed(function (aSucceeded) {
|
||||
ok(aSucceeded,
|
||||
"print preview mode should be finished by Esc key press");
|
||||
openPrintPreview(testClosePrintPreviewWithClosingWindowShortcutKey);
|
||||
});
|
||||
}
|
||||
|
||||
function testClosePrintPreviewWithClosingWindowShortcutKey() {
|
||||
EventUtils.synthesizeKey("w", { accelKey: true });
|
||||
checkPrintPreviewClosed(function (aSucceeded) {
|
||||
ok(aSucceeded,
|
||||
"print preview mode should be finished by closing window shortcut key");
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
function openPrintPreview(aCallback) {
|
||||
document.getElementById("cmd_printPreview").doCommand();
|
||||
executeSoon(function () {
|
||||
if (gInPrintPreviewMode) {
|
||||
executeSoon(aCallback);
|
||||
return;
|
||||
}
|
||||
executeSoon(arguments.callee);
|
||||
});
|
||||
}
|
||||
|
||||
function checkPrintPreviewClosed(aCallback) {
|
||||
let count = 0;
|
||||
executeSoon(function () {
|
||||
if (!gInPrintPreviewMode) {
|
||||
executeSoon(function () { aCallback(count < 1000); });
|
||||
return;
|
||||
}
|
||||
if (++count == 1000) {
|
||||
// The test might fail.
|
||||
PrintUtils.exitPrintPreview();
|
||||
}
|
||||
executeSoon(arguments.callee);
|
||||
});
|
||||
}
|
@ -569,7 +569,6 @@
|
||||
|
||||
@BINPATH@/components/Payment.js
|
||||
@BINPATH@/components/PaymentFlowInfo.js
|
||||
@BINPATH@/components/PaymentRequestInfo.js
|
||||
@BINPATH@/components/Payment.manifest
|
||||
|
||||
#ifdef MOZ_WEBRTC
|
||||
|
@ -512,11 +512,9 @@ def dumpScreen(utilityPath):
|
||||
|
||||
# Run the capture
|
||||
try:
|
||||
with mozfile.NamedTemporaryFile(suffix='.png',
|
||||
prefix='mozilla-test-fail-screenshot_',
|
||||
dir=parent_dir,
|
||||
delete=False) as f:
|
||||
returncode = subprocess.call(utility + [f.name])
|
||||
tmpfd, imgfilename = tempfile.mkstemp(prefix='mozilla-test-fail-screenshot_', suffix='.png', dir=parent_dir)
|
||||
os.close(tmpfd)
|
||||
returncode = subprocess.call(utility + [imgfilename])
|
||||
except OSError, err:
|
||||
log.info("Failed to start %s for screenshot: %s",
|
||||
utility[0], err.strerror)
|
||||
|
@ -1460,7 +1460,7 @@ ifeq (,$(wildcard $(DIST)/bin/nsinstall$(HOST_BIN_SUFFIX)))
|
||||
nsinstall_is_usable = $(if $(wildcard $(DIST)/bin/nsinstall$(HOST_BIN_SUFFIX)),yes)
|
||||
|
||||
define install_cmd_override
|
||||
$(1): install_cmd = $$(if $$(nsinstall_is_usable),$$(INSTALL),$$(NSINSTALL_PY)) $$(1)
|
||||
$(1): install_cmd = $$(if $$(nsinstall_is_usable),$$(INSTALL),$$(NSINSTALL_PY) -t) $$(1)
|
||||
endef
|
||||
endif
|
||||
endif
|
||||
|
@ -1249,6 +1249,7 @@ GK_ATOM(feConvolveMatrix, "feConvolveMatrix")
|
||||
GK_ATOM(feDiffuseLighting, "feDiffuseLighting")
|
||||
GK_ATOM(feDisplacementMap, "feDisplacementMap")
|
||||
GK_ATOM(feDistantLight,"feDistantLight")
|
||||
GK_ATOM(feDropShadow, "feDropShadow")
|
||||
GK_ATOM(feFlood, "feFlood")
|
||||
GK_ATOM(feFuncA, "feFuncA")
|
||||
GK_ATOM(feFuncB, "feFuncB")
|
||||
|
@ -306,6 +306,7 @@ nsIAtom** const kElementsSVG[] = {
|
||||
&nsGkAtoms::feDiffuseLighting, // feDiffuseLighting
|
||||
&nsGkAtoms::feDisplacementMap, // feDisplacementMap
|
||||
&nsGkAtoms::feDistantLight, // feDistantLight
|
||||
&nsGkAtoms::feDropShadow, // feDropShadow
|
||||
&nsGkAtoms::feFlood, // feFlood
|
||||
&nsGkAtoms::feFuncA, // feFuncA
|
||||
&nsGkAtoms::feFuncB, // feFuncB
|
||||
|
@ -1,72 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "AudioCompactor.h"
|
||||
#if defined(MOZ_MEMORY)
|
||||
# include "mozmemory.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
static size_t
|
||||
MallocGoodSize(size_t aSize)
|
||||
{
|
||||
# if defined(MOZ_MEMORY)
|
||||
return malloc_good_size(aSize);
|
||||
# else
|
||||
return aSize;
|
||||
# endif
|
||||
}
|
||||
|
||||
static size_t
|
||||
TooMuchSlop(size_t aSize, size_t aAllocSize, size_t aMaxSlop)
|
||||
{
|
||||
// If the allocated size is less then our target size, then we
|
||||
// are chunking. This means it will be completely filled with
|
||||
// zero slop.
|
||||
size_t slop = (aAllocSize > aSize) ? (aAllocSize - aSize) : 0;
|
||||
return slop > aMaxSlop;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
AudioCompactor::GetChunkSamples(uint32_t aFrames, uint32_t aChannels,
|
||||
size_t aMaxSlop)
|
||||
{
|
||||
size_t size = AudioDataSize(aFrames, aChannels);
|
||||
size_t chunkSize = MallocGoodSize(size);
|
||||
|
||||
// Reduce the chunk size until we meet our slop goal or the chunk
|
||||
// approaches an unreasonably small size.
|
||||
while (chunkSize > 64 && TooMuchSlop(size, chunkSize, aMaxSlop)) {
|
||||
chunkSize = MallocGoodSize(chunkSize / 2);
|
||||
}
|
||||
|
||||
// Calculate the number of samples based on expected malloc size
|
||||
// in order to allow as many frames as possible to be packed.
|
||||
return chunkSize / sizeof(AudioDataValue);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
AudioCompactor::NativeCopy::operator()(AudioDataValue *aBuffer, size_t aSamples)
|
||||
{
|
||||
NS_ASSERTION(aBuffer, "cannot copy to null buffer pointer");
|
||||
NS_ASSERTION(aSamples, "cannot copy zero values");
|
||||
|
||||
size_t bufferBytes = aSamples * sizeof(AudioDataValue);
|
||||
size_t maxBytes = std::min(bufferBytes, mSourceBytes - mNextByte);
|
||||
uint32_t frames = maxBytes / BytesPerFrame(mChannels);
|
||||
size_t bytes = frames * BytesPerFrame(mChannels);
|
||||
|
||||
NS_ASSERTION((mNextByte + bytes) <= mSourceBytes,
|
||||
"tried to copy beyond source buffer");
|
||||
NS_ASSERTION(bytes <= bufferBytes, "tried to copy beyond destination buffer");
|
||||
|
||||
memcpy(aBuffer, mSource + mNextByte, bytes);
|
||||
|
||||
mNextByte += bytes;
|
||||
return frames;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
@ -1,121 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#if !defined(AudioCompactor_h)
|
||||
#define AudioCompactor_h
|
||||
|
||||
#include "MediaQueue.h"
|
||||
#include "MediaData.h"
|
||||
#include "VideoUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class AudioCompactor
|
||||
{
|
||||
public:
|
||||
AudioCompactor(MediaQueue<AudioData>& aQueue)
|
||||
: mQueue(aQueue)
|
||||
{ }
|
||||
|
||||
// Push audio data into the underlying queue with minimal heap allocation
|
||||
// slop. This method is responsible for allocating AudioDataValue[] buffers.
|
||||
// The caller must provide a functor to copy the data into the buffers. The
|
||||
// functor must provide the following signature:
|
||||
//
|
||||
// uint32_t operator()(AudioDataValue *aBuffer, size_t aSamples);
|
||||
//
|
||||
// The functor must copy as many complete frames as possible to the provided
|
||||
// buffer given its length (in AudioDataValue elements). The number of frames
|
||||
// copied must be returned. This copy functor must support being called
|
||||
// multiple times in order to copy the audio data fully. The copy functor
|
||||
// must copy full frames as partial frames will be ignored.
|
||||
template<typename CopyFunc>
|
||||
bool Push(int64_t aOffset, int64_t aTime, int32_t aSampleRate,
|
||||
uint32_t aFrames, uint32_t aChannels, CopyFunc aCopyFunc)
|
||||
{
|
||||
// If we are losing more than a reasonable amount to padding, try to chunk
|
||||
// the data.
|
||||
size_t maxSlop = AudioDataSize(aFrames, aChannels) / MAX_SLOP_DIVISOR;
|
||||
|
||||
while (aFrames > 0) {
|
||||
uint32_t samples = GetChunkSamples(aFrames, aChannels, maxSlop);
|
||||
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[samples]);
|
||||
|
||||
// Copy audio data to buffer using caller-provided functor.
|
||||
uint32_t framesCopied = aCopyFunc(buffer, samples);
|
||||
|
||||
NS_ASSERTION(framesCopied <= aFrames, "functor copied too many frames");
|
||||
|
||||
CheckedInt64 duration = FramesToUsecs(framesCopied, aSampleRate);
|
||||
if (!duration.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mQueue.Push(new AudioData(aOffset,
|
||||
aTime,
|
||||
duration.value(),
|
||||
framesCopied,
|
||||
buffer.forget(),
|
||||
aChannels));
|
||||
|
||||
// Remove the frames we just pushed into the queue and loop if there is
|
||||
// more to be done.
|
||||
aTime += duration.value();
|
||||
aFrames -= framesCopied;
|
||||
|
||||
// NOTE: No need to update aOffset as its only an approximation anyway.
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copy functor suitable for copying audio samples already in the
|
||||
// AudioDataValue format/layout expected by AudioStream on this platform.
|
||||
class NativeCopy
|
||||
{
|
||||
public:
|
||||
NativeCopy(const uint8_t* aSource, size_t aSourceBytes,
|
||||
uint32_t aChannels)
|
||||
: mSource(aSource)
|
||||
, mSourceBytes(aSourceBytes)
|
||||
, mChannels(aChannels)
|
||||
, mNextByte(0)
|
||||
{ }
|
||||
|
||||
uint32_t operator()(AudioDataValue *aBuffer, size_t aSamples);
|
||||
|
||||
private:
|
||||
const uint8_t* const mSource;
|
||||
const size_t mSourceBytes;
|
||||
const uint32_t mChannels;
|
||||
size_t mNextByte;
|
||||
};
|
||||
|
||||
// Allow 12.5% slop before chunking kicks in. Public so that the gtest can
|
||||
// access it.
|
||||
static const size_t MAX_SLOP_DIVISOR = 8;
|
||||
|
||||
private:
|
||||
// Compute the number of AudioDataValue samples that will be fit the most
|
||||
// frames while keeping heap allocation slop less than the given threshold.
|
||||
static uint32_t
|
||||
GetChunkSamples(uint32_t aFrames, uint32_t aChannels, size_t aMaxSlop);
|
||||
|
||||
static size_t BytesPerFrame(uint32_t aChannels)
|
||||
{
|
||||
return sizeof(AudioDataValue) * aChannels;
|
||||
}
|
||||
|
||||
static size_t AudioDataSize(uint32_t aFrames, uint32_t aChannels)
|
||||
{
|
||||
return aFrames * BytesPerFrame(aChannels);
|
||||
}
|
||||
|
||||
MediaQueue<AudioData> &mQueue;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // AudioCompactor_h
|
@ -45,8 +45,7 @@ void* MediaDecoderReader::VideoQueueMemoryFunctor::operator()(void* anObject) {
|
||||
}
|
||||
|
||||
MediaDecoderReader::MediaDecoderReader(AbstractMediaDecoder* aDecoder)
|
||||
: mAudioCompactor(mAudioQueue),
|
||||
mDecoder(aDecoder),
|
||||
: mDecoder(aDecoder),
|
||||
mIgnoreAudioOutputFormat(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(MediaDecoderReader);
|
||||
@ -281,3 +280,4 @@ MediaDecoderReader::GetBuffered(mozilla::dom::TimeRanges* aBuffered,
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "MediaInfo.h"
|
||||
#include "MediaData.h"
|
||||
#include "MediaQueue.h"
|
||||
#include "AudioCompactor.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -106,12 +105,6 @@ protected:
|
||||
// the decoder, state machine, and main threads.
|
||||
MediaQueue<VideoData> mVideoQueue;
|
||||
|
||||
// An adapter to the audio queue which first copies data to buffers with
|
||||
// minimal allocation slop and then pushes them to the queue. This is
|
||||
// useful for decoders working with formats that give awkward numbers of
|
||||
// frames such as mp3.
|
||||
AudioCompactor mAudioCompactor;
|
||||
|
||||
public:
|
||||
// Populates aBuffered with the time ranges which are buffered. aStartTime
|
||||
// must be the presentation time of the first frame in the media, e.g.
|
||||
|
@ -45,13 +45,6 @@ extern PRLogModuleInfo* gMediaDecoderLog;
|
||||
#define DECODER_LOG(type, msg)
|
||||
#endif
|
||||
|
||||
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
|
||||
// GetTickCount() and conflicts with MediaDecoderStateMachine::GetCurrentTime
|
||||
// implementation. With unified builds, putting this in headers is not enough.
|
||||
#ifdef GetCurrentTime
|
||||
#undef GetCurrentTime
|
||||
#endif
|
||||
|
||||
// Wait this number of seconds when buffering, then leave and play
|
||||
// as best as we can if the required amount of data hasn't been
|
||||
// retrieved.
|
||||
|
@ -92,13 +92,6 @@ namespace mozilla {
|
||||
class AudioSegment;
|
||||
class VideoSegment;
|
||||
|
||||
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
|
||||
// GetTickCount() and conflicts with MediaDecoderStateMachine::GetCurrentTime
|
||||
// implementation.
|
||||
#ifdef GetCurrentTime
|
||||
#undef GetCurrentTime
|
||||
#endif
|
||||
|
||||
/*
|
||||
The state machine class. This manages the decoding and seeking in the
|
||||
MediaDecoderReader on the decode thread, and A/V sync on the shared
|
||||
|
@ -72,6 +72,8 @@ MediaStreamGraphImpl::FinishStream(MediaStream* aStream)
|
||||
// on UpdateCurrentTime to notify our listeners once the stream end
|
||||
// has been reached.
|
||||
EnsureNextIteration();
|
||||
|
||||
SetStreamOrderDirty();
|
||||
}
|
||||
|
||||
void
|
||||
@ -80,6 +82,8 @@ MediaStreamGraphImpl::AddStream(MediaStream* aStream)
|
||||
aStream->mBufferStartTime = mCurrentTime;
|
||||
*mStreams.AppendElement() = already_AddRefed<MediaStream>(aStream);
|
||||
STREAM_LOG(PR_LOG_DEBUG, ("Adding media stream %p to the graph", aStream));
|
||||
|
||||
SetStreamOrderDirty();
|
||||
}
|
||||
|
||||
void
|
||||
@ -97,6 +101,8 @@ MediaStreamGraphImpl::RemoveStream(MediaStream* aStream)
|
||||
}
|
||||
}
|
||||
|
||||
SetStreamOrderDirty();
|
||||
|
||||
// This unrefs the stream, probably destroying it
|
||||
mStreams.RemoveElement(aStream);
|
||||
|
||||
@ -430,6 +436,7 @@ MediaStreamGraphImpl::UpdateCurrentTime()
|
||||
stream->StreamTimeToGraphTime(stream->GetStreamBuffer().GetAllTracksEnd())) {
|
||||
stream->mNotifiedFinished = true;
|
||||
stream->mLastPlayedVideoFrame.SetNull();
|
||||
SetStreamOrderDirty();
|
||||
for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
|
||||
MediaStreamListener* l = stream->mListeners[j];
|
||||
l->NotifyFinished(this);
|
||||
@ -1168,7 +1175,9 @@ MediaStreamGraphImpl::RunThread()
|
||||
}
|
||||
messageQueue.Clear();
|
||||
|
||||
UpdateStreamOrder();
|
||||
if (mStreamOrderDirty) {
|
||||
UpdateStreamOrder();
|
||||
}
|
||||
|
||||
// Find the sampling rate that we need to use for non-realtime graphs.
|
||||
TrackRate sampleRate = IdealAudioRate();
|
||||
@ -2279,6 +2288,8 @@ MediaInputPort::Disconnect()
|
||||
mSource = nullptr;
|
||||
mDest->RemoveInput(this);
|
||||
mDest = nullptr;
|
||||
|
||||
GraphImpl()->SetStreamOrderDirty();
|
||||
}
|
||||
|
||||
MediaInputPort::InputInterval
|
||||
@ -2357,6 +2368,7 @@ ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, uint32_t aFlags,
|
||||
{
|
||||
mPort->Init();
|
||||
// The graph holds its reference implicitly
|
||||
mPort->GraphImpl()->SetStreamOrderDirty();
|
||||
mPort.forget();
|
||||
}
|
||||
virtual void RunDuringShutdown()
|
||||
@ -2410,6 +2422,7 @@ ProcessedMediaStream::DestroyImpl()
|
||||
mInputs[i]->Disconnect();
|
||||
}
|
||||
MediaStream::DestroyImpl();
|
||||
GraphImpl()->SetStreamOrderDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -204,7 +204,7 @@ public:
|
||||
void Revoke() { mResource = nullptr; }
|
||||
|
||||
private:
|
||||
nsRefPtr<RtspMediaResource> mResource;
|
||||
RtspMediaResource* mResource;
|
||||
};
|
||||
friend class Listener;
|
||||
|
||||
|
@ -12,12 +12,8 @@
|
||||
#define AUDIO_READ_BYTES 4096
|
||||
|
||||
// Maximum number of audio frames we will accept from the audio decoder in one
|
||||
// go. Carefully select this to work well with both the mp3 1152 max frames
|
||||
// per block and power-of-2 allocation sizes. Since we must pre-allocate the
|
||||
// buffer we cannot use AudioCompactor without paying for an additional
|
||||
// allocation and copy. Therefore, choosing a value that divides exactly into
|
||||
// 1152 is most memory efficient.
|
||||
#define MAX_AUDIO_FRAMES 128
|
||||
// go.
|
||||
#define MAX_AUDIO_FRAMES 4096
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -205,8 +201,7 @@ AppleMP3Reader::AudioSampleCallback(UInt32 aNumBytes,
|
||||
LOGD("got %u bytes, %u packets\n", aNumBytes, aNumPackets);
|
||||
|
||||
// 1 frame per packet * num channels * 32-bit float
|
||||
uint32_t decodedSize = MAX_AUDIO_FRAMES * mAudioChannels *
|
||||
sizeof(AudioDataValue);
|
||||
uint32_t decodedSize = MAX_AUDIO_FRAMES * mAudioChannels * 4;
|
||||
|
||||
// descriptions for _decompressed_ audio packets. ignored.
|
||||
nsAutoArrayPtr<AudioStreamPacketDescription>
|
||||
@ -243,14 +238,6 @@ AppleMP3Reader::AudioSampleCallback(UInt32 aNumBytes,
|
||||
break;
|
||||
}
|
||||
|
||||
// If we decoded zero frames then AudiOConverterFillComplexBuffer is out
|
||||
// of data to provide. We drained its internal buffer completely on the
|
||||
// last pass.
|
||||
if (numFrames == 0 && rv == kNeedMoreData) {
|
||||
LOGD("FillComplexBuffer out of data exactly\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int64_t time = FramesToUsecs(mCurrentAudioFrame, mAudioSampleRate).value();
|
||||
int64_t duration = FramesToUsecs(numFrames, mAudioSampleRate).value();
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
@ -248,48 +247,6 @@ DirectShowReader::Finish(HRESULT aStatus)
|
||||
return false;
|
||||
}
|
||||
|
||||
class DirectShowCopy
|
||||
{
|
||||
public:
|
||||
DirectShowCopy(uint8_t *aSource, uint32_t aBytesPerSample,
|
||||
uint32_t aSamples, uint32_t aChannels)
|
||||
: mSource(aSource)
|
||||
, mBytesPerSample(aBytesPerSample)
|
||||
, mSamples(aSamples)
|
||||
, mChannels(aChannels)
|
||||
, mNextSample(0)
|
||||
{ }
|
||||
|
||||
uint32_t operator()(AudioDataValue *aBuffer, size_t aSamples)
|
||||
{
|
||||
size_t maxSamples = std::min(aSamples, mSamples - mNextSample);
|
||||
uint32_t frames = maxSamples / mChannels;
|
||||
size_t byteOffset = mNextSample * mBytesPerSample;
|
||||
if (mBytesPerSample == 1) {
|
||||
for (uint32_t i = 0; i < maxSamples; ++i) {
|
||||
uint8_t *sample = mSource + byteOffset;
|
||||
aBuffer[i] = UnsignedByteToAudioSample(*sample);
|
||||
byteOffset += mBytesPerSample;
|
||||
}
|
||||
} else if (mBytesPerSample == 2) {
|
||||
for (uint32_t i = 0; i < maxSamples; ++i) {
|
||||
int16_t *sample = reinterpret_cast<int16_t *>(mSource + byteOffset);
|
||||
aBuffer[i] = AudioSampleToFloat(*sample);
|
||||
byteOffset += mBytesPerSample;
|
||||
}
|
||||
}
|
||||
mNextSample = maxSamples;
|
||||
return frames;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t * const mSource;
|
||||
const uint32_t mBytesPerSample;
|
||||
const uint32_t mSamples;
|
||||
const uint32_t mChannels;
|
||||
uint32_t mNextSample;
|
||||
};
|
||||
|
||||
bool
|
||||
DirectShowReader::DecodeAudioData()
|
||||
{
|
||||
@ -324,15 +281,26 @@ DirectShowReader::DecodeAudioData()
|
||||
hr = sample->GetPointer(&data);
|
||||
NS_ENSURE_TRUE(SUCCEEDED(hr), Finish(hr));
|
||||
|
||||
mAudioCompactor.Push(mDecoder->GetResource()->Tell(),
|
||||
RefTimeToUsecs(start),
|
||||
mInfo.mAudio.mRate,
|
||||
numFrames,
|
||||
mNumChannels,
|
||||
DirectShowCopy(reinterpret_cast<uint8_t *>(data),
|
||||
mBytesPerSample,
|
||||
numSamples,
|
||||
mNumChannels));
|
||||
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[numSamples]);
|
||||
AudioDataValue* dst = buffer.get();
|
||||
if (mBytesPerSample == 1) {
|
||||
uint8_t* src = reinterpret_cast<uint8_t*>(data);
|
||||
for (int32_t i = 0; i < numSamples; ++i) {
|
||||
dst[i] = UnsignedByteToAudioSample(src[i]);
|
||||
}
|
||||
} else if (mBytesPerSample == 2) {
|
||||
int16_t* src = reinterpret_cast<int16_t*>(data);
|
||||
for (int32_t i = 0; i < numSamples; ++i) {
|
||||
dst[i] = AudioSampleToFloat(src[i]);
|
||||
}
|
||||
}
|
||||
|
||||
mAudioQueue.Push(new AudioData(mDecoder->GetResource()->Tell(),
|
||||
RefTimeToUsecs(start),
|
||||
RefTimeToUsecs(end - start),
|
||||
numFrames,
|
||||
buffer.forget(),
|
||||
mNumChannels));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -532,20 +532,20 @@ bool GStreamerReader::DecodeAudioData()
|
||||
timestamp = gst_segment_to_stream_time(&mAudioSegment,
|
||||
GST_FORMAT_TIME, timestamp);
|
||||
timestamp = GST_TIME_AS_USECONDS(timestamp);
|
||||
int64_t duration = 0;
|
||||
if (GST_CLOCK_TIME_IS_VALID(GST_BUFFER_DURATION(buffer)))
|
||||
duration = GST_TIME_AS_USECONDS(GST_BUFFER_DURATION(buffer));
|
||||
|
||||
int64_t offset = GST_BUFFER_OFFSET(buffer);
|
||||
unsigned int size = GST_BUFFER_SIZE(buffer);
|
||||
int32_t frames = (size / sizeof(AudioDataValue)) / mInfo.mAudio.mChannels;
|
||||
ssize_t outSize = static_cast<size_t>(size / sizeof(AudioDataValue));
|
||||
nsAutoArrayPtr<AudioDataValue> data(new AudioDataValue[outSize]);
|
||||
memcpy(data, GST_BUFFER_DATA(buffer), GST_BUFFER_SIZE(buffer));
|
||||
AudioData* audio = new AudioData(offset, timestamp, duration,
|
||||
frames, data.forget(), mInfo.mAudio.mChannels);
|
||||
|
||||
typedef AudioCompactor::NativeCopy GstCopy;
|
||||
mAudioCompactor.Push(offset,
|
||||
timestamp,
|
||||
mInfo.mAudio.mRate,
|
||||
frames,
|
||||
mInfo.mAudio.mChannels,
|
||||
GstCopy(GST_BUFFER_DATA(buffer),
|
||||
size,
|
||||
mInfo.mAudio.mChannels));
|
||||
mAudioQueue.Push(audio);
|
||||
gst_buffer_unref(buffer);
|
||||
|
||||
return true;
|
||||
|
@ -1,131 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include "gtest/gtest.h"
|
||||
#include "AudioCompactor.h"
|
||||
#include "MediaDecoderReader.h"
|
||||
|
||||
using mozilla::AudioCompactor;
|
||||
using mozilla::AudioData;
|
||||
using mozilla::AudioDataValue;
|
||||
using mozilla::MediaDecoderReader;
|
||||
using mozilla::MediaQueue;
|
||||
|
||||
class TestCopy
|
||||
{
|
||||
public:
|
||||
TestCopy(uint32_t aFrames, uint32_t aChannels,
|
||||
uint32_t &aCallCount, uint32_t &aFrameCount)
|
||||
: mFrames(aFrames)
|
||||
, mChannels(aChannels)
|
||||
, mCallCount(aCallCount)
|
||||
, mFrameCount(aFrameCount)
|
||||
{ }
|
||||
|
||||
uint32_t operator()(AudioDataValue *aBuffer, uint32_t aSamples)
|
||||
{
|
||||
mCallCount += 1;
|
||||
uint32_t frames = std::min(mFrames - mFrameCount, aSamples / mChannels);
|
||||
mFrameCount += frames;
|
||||
return frames;
|
||||
}
|
||||
|
||||
private:
|
||||
const uint32_t mFrames;
|
||||
const uint32_t mChannels;
|
||||
uint32_t &mCallCount;
|
||||
uint32_t &mFrameCount;
|
||||
};
|
||||
|
||||
static void TestAudioCompactor(size_t aBytes)
|
||||
{
|
||||
MediaQueue<AudioData> queue;
|
||||
AudioCompactor compactor(queue);
|
||||
|
||||
uint64_t offset = 0;
|
||||
uint64_t time = 0;
|
||||
uint32_t sampleRate = 44000;
|
||||
uint32_t channels = 2;
|
||||
uint32_t frames = aBytes / (channels * sizeof(AudioDataValue));
|
||||
size_t maxSlop = aBytes / AudioCompactor::MAX_SLOP_DIVISOR;
|
||||
|
||||
uint32_t callCount = 0;
|
||||
uint32_t frameCount = 0;
|
||||
|
||||
compactor.Push(offset, time, sampleRate, frames, channels,
|
||||
TestCopy(frames, channels, callCount, frameCount));
|
||||
|
||||
EXPECT_GT(callCount, 0U) << "copy functor never called";
|
||||
EXPECT_EQ(frames, frameCount) << "incorrect number of frames copied";
|
||||
|
||||
MediaDecoderReader::AudioQueueMemoryFunctor memoryFunc;
|
||||
queue.LockedForEach(memoryFunc);
|
||||
size_t allocSize = memoryFunc.mSize - (callCount * sizeof(AudioData));
|
||||
size_t slop = allocSize - aBytes;
|
||||
EXPECT_LE(slop, maxSlop) << "allowed too much allocation slop";
|
||||
}
|
||||
|
||||
TEST(Media, AudioCompactor_4000)
|
||||
{
|
||||
TestAudioCompactor(4000);
|
||||
}
|
||||
|
||||
TEST(Media, AudioCompactor_4096)
|
||||
{
|
||||
TestAudioCompactor(4096);
|
||||
}
|
||||
|
||||
TEST(Media, AudioCompactor_5000)
|
||||
{
|
||||
TestAudioCompactor(5000);
|
||||
}
|
||||
|
||||
TEST(Media, AudioCompactor_5256)
|
||||
{
|
||||
TestAudioCompactor(5256);
|
||||
}
|
||||
|
||||
TEST(Media, AudioCompactor_NativeCopy)
|
||||
{
|
||||
const uint32_t channels = 2;
|
||||
const size_t srcBytes = 32;
|
||||
const uint32_t srcSamples = srcBytes / sizeof(AudioDataValue);
|
||||
const uint32_t srcFrames = srcSamples / channels;
|
||||
uint8_t src[srcBytes];
|
||||
|
||||
for (uint32_t i = 0; i < srcBytes; ++i) {
|
||||
src[i] = i;
|
||||
}
|
||||
|
||||
AudioCompactor::NativeCopy copy(src, srcBytes, channels);
|
||||
|
||||
const uint32_t dstSamples = srcSamples * 2;
|
||||
AudioDataValue dst[dstSamples];
|
||||
|
||||
const AudioDataValue notCopied = 0xffff;
|
||||
for (uint32_t i = 0; i < dstSamples; ++i) {
|
||||
dst[i] = notCopied;
|
||||
}
|
||||
|
||||
const uint32_t copyCount = 8;
|
||||
uint32_t copiedFrames = 0;
|
||||
uint32_t nextSample = 0;
|
||||
for (uint32_t i = 0; i < copyCount; ++i) {
|
||||
uint32_t copySamples = dstSamples / copyCount;
|
||||
copiedFrames += copy(dst + nextSample, copySamples);
|
||||
nextSample += copySamples;
|
||||
}
|
||||
|
||||
EXPECT_EQ(srcFrames, copiedFrames) << "copy exact number of source frames";
|
||||
|
||||
// Verify that the only the correct bytes were copied.
|
||||
for (uint32_t i = 0; i < dstSamples; ++i) {
|
||||
if (i < srcSamples) {
|
||||
EXPECT_NE(notCopied, dst[i]) << "should have copied over these bytes";
|
||||
} else {
|
||||
EXPECT_EQ(notCopied, dst[i]) << "should not have copied over these bytes";
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,6 @@
|
||||
LIBRARY_NAME = 'media_gtest'
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'TestAudioCompactor.cpp',
|
||||
'TestTrackEncoder.cpp',
|
||||
]
|
||||
|
||||
|
@ -58,7 +58,6 @@ EXPORTS += [
|
||||
'AbstractMediaDecoder.h',
|
||||
'AudioAvailableEventManager.h',
|
||||
'AudioChannelFormat.h',
|
||||
'AudioCompactor.h',
|
||||
'AudioEventTimeline.h',
|
||||
'AudioNodeEngine.h',
|
||||
'AudioNodeExternalInputStream.h',
|
||||
@ -116,7 +115,6 @@ EXPORTS.mozilla.dom += [
|
||||
UNIFIED_SOURCES += [
|
||||
'AudioAvailableEventManager.cpp',
|
||||
'AudioChannelFormat.cpp',
|
||||
'AudioCompactor.cpp',
|
||||
'AudioNodeEngine.cpp',
|
||||
'AudioNodeExternalInputStream.cpp',
|
||||
'AudioNodeStream.cpp',
|
||||
|
@ -312,29 +312,33 @@ bool MediaOmxReader::DecodeAudioData()
|
||||
int64_t pos = mDecoder->GetResource()->Tell();
|
||||
|
||||
// Read next frame
|
||||
MPAPI::AudioFrame source;
|
||||
if (!mOmxDecoder->ReadAudio(&source, mAudioSeekTimeUs)) {
|
||||
MPAPI::AudioFrame frame;
|
||||
if (!mOmxDecoder->ReadAudio(&frame, mAudioSeekTimeUs)) {
|
||||
return false;
|
||||
}
|
||||
mAudioSeekTimeUs = -1;
|
||||
|
||||
// Ignore empty buffer which stagefright media read will sporadically return
|
||||
if (source.mSize == 0) {
|
||||
if (frame.mSize == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t frames = source.mSize / (source.mAudioChannels *
|
||||
sizeof(AudioDataValue));
|
||||
nsAutoArrayPtr<AudioDataValue> buffer(new AudioDataValue[frame.mSize/2] );
|
||||
memcpy(buffer.get(), frame.mData, frame.mSize);
|
||||
|
||||
typedef AudioCompactor::NativeCopy OmxCopy;
|
||||
return mAudioCompactor.Push(pos,
|
||||
source.mTimeUs,
|
||||
source.mAudioSampleRate,
|
||||
frames,
|
||||
source.mAudioChannels,
|
||||
OmxCopy(static_cast<uint8_t *>(source.mData),
|
||||
source.mSize,
|
||||
source.mAudioChannels));
|
||||
uint32_t frames = frame.mSize / (2 * frame.mAudioChannels);
|
||||
CheckedInt64 duration = FramesToUsecs(frames, frame.mAudioSampleRate);
|
||||
if (!duration.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mAudioQueue.Push(new AudioData(pos,
|
||||
frame.mTimeUs,
|
||||
duration.value(),
|
||||
frames,
|
||||
buffer.forget(),
|
||||
frame.mAudioChannels));
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult MediaOmxReader::Seek(int64_t aTarget, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime)
|
||||
|
@ -1886,7 +1886,7 @@ InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
JS::Rooted<JSObject*> unwrapped(cx, js::CheckedUnwrap(instance, true));
|
||||
if (unwrapped && jsipc::JavaScriptParent::IsCPOW(unwrapped)) {
|
||||
bool boolp = false;
|
||||
if (!jsipc::JavaScriptParent::DOMInstanceOf(unwrapped, clasp->mPrototypeID,
|
||||
if (!jsipc::JavaScriptParent::DOMInstanceOf(cx, unwrapped, clasp->mPrototypeID,
|
||||
clasp->mDepth, &boolp)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1467,7 +1467,7 @@ BluetoothOppManager::OnSocketConnectError(BluetoothSocket* aSocket)
|
||||
}
|
||||
|
||||
// Listen as a server if there's no more batch to process
|
||||
if (!ProcessNextBatch()) {
|
||||
if (!ProcessNextBatch() && !mIsServer) {
|
||||
Listen();
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=944397
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=944397">Mozilla Bug 944397</a>
|
||||
<input type="text" />
|
||||
<p id="display"></p>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="application/javascript;version=1.7">
|
||||
@ -26,6 +25,14 @@ function appFrameScript() {
|
||||
input.oninput = function() {
|
||||
sendAsyncMessage('test:InputMethod:oninput', {});
|
||||
};
|
||||
|
||||
/*
|
||||
* Bug 957213. Sometimes we need to refocus the input field to avoid
|
||||
* intermittent test failure.
|
||||
*/
|
||||
content.setInterval(function() {
|
||||
input.focus();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
@ -40,8 +47,12 @@ function runTest() {
|
||||
let mm = SpecialPowers.getBrowserFrameMessageManager(app);
|
||||
mm.loadFrameScript('data:,(' + appFrameScript.toString() + ')();', false);
|
||||
mm.addMessageListener("test:InputMethod:oninput", function() {
|
||||
if (!timeoutId) {
|
||||
return;
|
||||
}
|
||||
ok(true, 'Keyboard input was received.');
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = null;
|
||||
inputmethod_cleanup();
|
||||
});
|
||||
});
|
||||
@ -80,7 +91,7 @@ function runTest() {
|
||||
timeoutId = setTimeout(function() {
|
||||
inputmethod_cleanup();
|
||||
ok(false, 'Failed to generate keyboard input.');
|
||||
}, 60000);
|
||||
}, 20000);
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
@ -89,6 +100,7 @@ function runTest() {
|
||||
};
|
||||
|
||||
// Loads the input method app to the browser frame after a delay.
|
||||
SpecialPowers.DOMWindowUtils.focus(app);
|
||||
setTimeout(function() {
|
||||
keyboard.src = imeUrl;
|
||||
}, 100);
|
||||
|
@ -92,6 +92,10 @@
|
||||
#define getpid _getpid
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_X11
|
||||
#include "mozilla/X11Util.h"
|
||||
#endif
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
#include "nsIAccessibilityService.h"
|
||||
#endif
|
||||
@ -352,6 +356,13 @@ ContentChild::Init(MessageLoop* aIOLoop,
|
||||
Open(aChannel, aParentHandle, aIOLoop);
|
||||
sSingleton = this;
|
||||
|
||||
#ifdef MOZ_X11
|
||||
// Send the parent our X socket to act as a proxy reference for our X
|
||||
// resources.
|
||||
int xSocketFd = ConnectionNumber(DefaultXDisplay());
|
||||
SendBackUpXResources(FileDescriptor(xSocketFd));
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
SendPCrashReporterConstructor(CrashReporter::CurrentThreadId(),
|
||||
XRE_GetProcessType());
|
||||
|
@ -2012,7 +2012,7 @@ ContentParent::AllocPJavaScriptParent()
|
||||
bool
|
||||
ContentParent::DeallocPJavaScriptParent(PJavaScriptParent *parent)
|
||||
{
|
||||
static_cast<mozilla::jsipc::JavaScriptParent *>(parent)->destroyFromContent();
|
||||
static_cast<mozilla::jsipc::JavaScriptParent *>(parent)->decref();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3258,6 +3258,22 @@ ContentParent::RecvRemoveIdleObserver(const uint64_t& aObserver, const uint32_t&
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvBackUpXResources(const FileDescriptor& aXSocketFd)
|
||||
{
|
||||
#ifndef MOZ_X11
|
||||
NS_RUNTIMEABORT("This message only makes sense on X11 platforms");
|
||||
#else
|
||||
NS_ABORT_IF_FALSE(0 > mChildXSocketFdDup.get(),
|
||||
"Already backed up X resources??");
|
||||
mChildXSocketFdDup.forget();
|
||||
if (aXSocketFd.IsValid()) {
|
||||
mChildXSocketFdDup.reset(aXSocketFd.PlatformHandle());
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "mozilla/ipc/GeckoChildProcessHost.h"
|
||||
#include "mozilla/dom/ipc/Blob.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/FileUtils.h"
|
||||
#include "mozilla/HalTypes.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
@ -534,6 +535,9 @@ private:
|
||||
virtual bool RecvRemoveIdleObserver(const uint64_t& observerId,
|
||||
const uint32_t& aIdleTimeInS) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
RecvBackUpXResources(const FileDescriptor& aXSocketFd) MOZ_OVERRIDE;
|
||||
|
||||
// If you add strong pointers to cycle collected objects here, be sure to
|
||||
// release these objects in ShutDownProcess. See the comment there for more
|
||||
// details.
|
||||
@ -586,6 +590,12 @@ private:
|
||||
nsConsoleService* GetConsoleService();
|
||||
|
||||
nsDataHashtable<nsUint64HashKey, nsCOMPtr<ParentIdleListener> > mIdleListeners;
|
||||
|
||||
#ifdef MOZ_X11
|
||||
// Dup of child's X socket, used to scope its resources to this
|
||||
// object instead of the child process's lifetime.
|
||||
ScopedClose mChildXSocketFdDup;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -519,6 +519,20 @@ parent:
|
||||
|
||||
AddIdleObserver(uint64_t observerId, uint32_t idleTimeInS);
|
||||
RemoveIdleObserver(uint64_t observerId, uint32_t idleTimeInS);
|
||||
|
||||
/**
|
||||
* This message is only used on X11 platforms.
|
||||
*
|
||||
* Send a dup of the plugin process's X socket to the parent
|
||||
* process. In theory, this scheme keeps the plugin's X resources
|
||||
* around until after both the plugin process shuts down *and* the
|
||||
* parent process closes the dup fd. This is used to prevent the
|
||||
* parent process from crashing on X errors if, e.g., the plugin
|
||||
* crashes *just before* a repaint and the parent process tries to
|
||||
* use the newly-invalid surface.
|
||||
*/
|
||||
BackUpXResources(FileDescriptor aXSocketFd);
|
||||
|
||||
both:
|
||||
AsyncMessage(nsString aMessage, ClonedMessageData aData,
|
||||
CpowEntry[] aCpows, Principal aPrincipal);
|
||||
|
@ -610,7 +610,7 @@ TabChild::HandlePossibleViewportChange()
|
||||
// The page must have been refreshed in some way such as a new document or
|
||||
// new CSS viewport, so we know that there's no velocity, acceleration, and
|
||||
// we have no idea how long painting will take.
|
||||
metrics, ScreenPoint(0.0f, 0.0f), gfx::Point(0.0f, 0.0f), 0.0);
|
||||
metrics, ScreenPoint(0.0f, 0.0f), 0.0);
|
||||
metrics.mCumulativeResolution = metrics.mZoom / metrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
|
||||
// This is the root layer, so the cumulative resolution is the same
|
||||
// as the resolution.
|
||||
|
@ -29,6 +29,15 @@ const TOPIC_INTERFACE_UNREGISTERED = "network-interface-unregistered";
|
||||
const NET_TYPE_WIFI = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
|
||||
const NET_TYPE_MOBILE = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE;
|
||||
|
||||
// Networks have different status that NetworkStats API needs to be aware of.
|
||||
// Network is present and ready, so NetworkManager provides the whole info.
|
||||
const NETWORK_STATUS_READY = 0;
|
||||
// Network is present but hasn't established a connection yet (e.g. SIM that has not
|
||||
// enabled 3G since boot).
|
||||
const NETWORK_STATUS_STANDBY = 1;
|
||||
// Network is not present, but stored in database by the previous connections.
|
||||
const NETWORK_STATUS_AWAY = 2;
|
||||
|
||||
// The maximum traffic amount can be saved in the |cachedStats|.
|
||||
const MAX_CACHED_TRAFFIC = 500 * 1000 * 1000; // 500 MB
|
||||
|
||||
@ -89,7 +98,8 @@ this.NetworkStatsService = {
|
||||
let netId = this.getNetworkId('0', NET_TYPE_WIFI);
|
||||
this._networks[netId] = { network: { id: '0',
|
||||
type: NET_TYPE_WIFI },
|
||||
interfaceName: null };
|
||||
interfaceName: null,
|
||||
status: NETWORK_STATUS_STANDBY };
|
||||
|
||||
this.messages = ["NetworkStats:Get",
|
||||
"NetworkStats:Clear",
|
||||
@ -278,6 +288,7 @@ this.NetworkStatsService = {
|
||||
type: aNetwork.type };
|
||||
}
|
||||
|
||||
this._networks[netId].status = NETWORK_STATUS_READY;
|
||||
this._networks[netId].interfaceName = aNetwork.name;
|
||||
return netId;
|
||||
},
|
||||
@ -286,6 +297,46 @@ this.NetworkStatsService = {
|
||||
return aIccId + '' + aNetworkType;
|
||||
},
|
||||
|
||||
/* Function to ensure that one network is valid. The network is valid if its status is
|
||||
* NETWORK_STATUS_READY, NETWORK_STATUS_STANDBY or NETWORK_STATUS_AWAY.
|
||||
*
|
||||
* The result is |netId| or null in case of a non-valid network
|
||||
* aCallback is signatured as |function(netId)|.
|
||||
*/
|
||||
validateNetwork: function validateNetwork(aNetwork, aCallback) {
|
||||
let netId = this.getNetworkId(aNetwork.id, aNetwork.type);
|
||||
|
||||
if (this._networks[netId]) {
|
||||
aCallback(netId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if network is valid (RIL entry) but has not established a connection yet.
|
||||
// If so add to networks list with empty interfaceName.
|
||||
let rilNetworks = this.getRilNetworks();
|
||||
if (rilNetworks[netId]) {
|
||||
this._networks[netId] = Object.create(null);
|
||||
this._networks[netId].network = rilNetworks[netId];
|
||||
this._networks[netId].status = NETWORK_STATUS_STANDBY;
|
||||
this._currentAlarms[netId] = Object.create(null);
|
||||
aCallback(netId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if network is available in the DB.
|
||||
this._db.isNetworkAvailable(aNetwork, function(aError, aResult) {
|
||||
if (aResult) {
|
||||
this._networks[netId] = Object.create(null);
|
||||
this._networks[netId].network = aNetwork;
|
||||
this._networks[netId].status = NETWORK_STATUS_AWAY;
|
||||
this._currentAlarms[netId] = Object.create(null);
|
||||
aCallback(netId);
|
||||
}
|
||||
|
||||
aCallback(null);
|
||||
});
|
||||
},
|
||||
|
||||
getAvailableNetworks: function getAvailableNetworks(mm, msg) {
|
||||
let self = this;
|
||||
let rilNetworks = this.getRilNetworks();
|
||||
@ -368,91 +419,55 @@ this.NetworkStatsService = {
|
||||
let start = new Date(msg.start);
|
||||
let end = new Date(msg.end);
|
||||
|
||||
// Check if the network is currently active. If yes, we need to update
|
||||
// the cached stats first before retrieving stats from the DB.
|
||||
if (this._networks[netId]) {
|
||||
this.updateStats(netId, function onStatsUpdated(aResult, aMessage) {
|
||||
debug("getstats for network " + network.id + " of type " + network.type);
|
||||
debug("appId: " + appId + " from appManifestURL: " + appManifestURL);
|
||||
|
||||
self.updateCachedStats(function onStatsUpdated(aResult, aMessage) {
|
||||
self._db.find(function onStatsFound(aError, aResult) {
|
||||
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||
{ id: msg.id, error: aError, result: aResult });
|
||||
}, appId, serviceType, network, start, end, appManifestURL);
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the network is available in the DB. If yes, we also
|
||||
// retrieve the stats for it from the DB.
|
||||
this._db.isNetworkAvailable(network, function(aError, aResult) {
|
||||
let toFind = false;
|
||||
if (aResult) {
|
||||
toFind = true;
|
||||
} else if (!aError) {
|
||||
// Network is not found in the database without any errors.
|
||||
// Check if network is valid but has not established a connection yet.
|
||||
let rilNetworks = self.getRilNetworks();
|
||||
if (rilNetworks[netId]) {
|
||||
// find will not get data for network from the database but will format the
|
||||
// result object in order to make NetworkStatsManager be able to construct a
|
||||
// nsIDOMMozNetworkStats object.
|
||||
toFind = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (toFind) {
|
||||
// If network is not active, there is no need to update stats before finding.
|
||||
self._db.find(function onStatsFound(aError, aResult) {
|
||||
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||
{ id: msg.id, error: aError, result: aResult });
|
||||
}, appId, serviceType, network, start, end, appManifestURL);
|
||||
this.validateNetwork(network, function onValidateNetwork(aNetId) {
|
||||
if (!aNetId) {
|
||||
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||
{ id: msg.id, error: "Invalid connectionType", result: null });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aError) {
|
||||
aError = "Invalid connectionType";
|
||||
// If network is currently active we need to update the cached stats first before
|
||||
// retrieving stats from the DB.
|
||||
if (self._networks[aNetId].status == NETWORK_STATUS_READY) {
|
||||
self.updateStats(aNetId, function onStatsUpdated(aResult, aMessage) {
|
||||
debug("getstats for network " + network.id + " of type " + network.type);
|
||||
debug("appId: " + appId + " from appManifestURL: " + appManifestURL);
|
||||
|
||||
self.updateCachedStats(function onStatsUpdated(aResult, aMessage) {
|
||||
self._db.find(function onStatsFound(aError, aResult) {
|
||||
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||
{ id: msg.id, error: aError, result: aResult });
|
||||
}, appId, serviceType, network, start, end, appManifestURL);
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||
{ id: msg.id, error: aError, result: null });
|
||||
// Network not active, so no need to update
|
||||
self._db.find(function onStatsFound(aError, aResult) {
|
||||
mm.sendAsyncMessage("NetworkStats:Get:Return",
|
||||
{ id: msg.id, error: aError, result: aResult });
|
||||
}, appId, serviceType, network, start, end, appManifestURL);
|
||||
});
|
||||
},
|
||||
|
||||
clearInterfaceStats: function clearInterfaceStats(mm, msg) {
|
||||
let self = this;
|
||||
let network = msg.network;
|
||||
let netId = this.getNetworkId(network.id, network.type);
|
||||
|
||||
debug("clear stats for network " + network.id + " of type " + network.type);
|
||||
|
||||
if (!this._networks[netId]) {
|
||||
// Check if network is valid but has not established a connection yet. If it is not
|
||||
// found in RIL networks, it can be a SIM network used in the past having sample
|
||||
// in the database.
|
||||
let rilNetworks = this.getRilNetworks();
|
||||
if (!rilNetworks[netId]) {
|
||||
// Check if it is available in the DB.
|
||||
this._db.isNetworkAvailable(network, function(aError, aResult) {
|
||||
if (aResult) {
|
||||
this._db.clearInterfaceStats(network, function onDBCleared(aError, aResult) {
|
||||
mm.sendAsyncMessage("NetworkStats:Clear:Return",
|
||||
{ id: msg.id, error: aError, result: aResult });
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
mm.sendAsyncMessage("NetworkStats:Clear:Return",
|
||||
{ id: msg.id, error: "Invalid networkType", result: null });
|
||||
});
|
||||
this.validateNetwork(network, function onValidateNetwork(aNetId) {
|
||||
if (!aNetId) {
|
||||
mm.sendAsyncMessage("NetworkStats:Clear:Return",
|
||||
{ id: msg.id, error: "Invalid connectionType", result: null });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this._db.clearInterfaceStats(network, function onDBCleared(aError, aResult) {
|
||||
mm.sendAsyncMessage("NetworkStats:Clear:Return",
|
||||
{ id: msg.id, error: aError, result: aResult });
|
||||
self._db.clearInterfaceStats(network, function onDBCleared(aError, aResult) {
|
||||
mm.sendAsyncMessage("NetworkStats:Clear:Return",
|
||||
{ id: msg.id, error: aError, result: aResult });
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@ -478,7 +493,7 @@ this.NetworkStatsService = {
|
||||
this.updateCachedStats();
|
||||
|
||||
let elements = [];
|
||||
let lastElement;
|
||||
let lastElement = null;
|
||||
|
||||
// For each connectionType create an object containning the type
|
||||
// and the 'queueIndex', the 'queueIndex' is an integer representing
|
||||
@ -487,14 +502,27 @@ this.NetworkStatsService = {
|
||||
// else it is pushed in 'elements' array, which later will be pushed to
|
||||
// the queue array.
|
||||
for (let netId in this._networks) {
|
||||
if (this._networks[netId].status != NETWORK_STATUS_READY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lastElement = { netId: netId,
|
||||
queueIndex: this.updateQueueIndex(netId)};
|
||||
|
||||
if (lastElement.queueIndex == -1) {
|
||||
elements.push({netId: lastElement.netId, callbacks: []});
|
||||
elements.push({ netId: lastElement.netId, callbacks: [] });
|
||||
}
|
||||
}
|
||||
|
||||
if (!lastElement) {
|
||||
// No elements need to be updated, probably because status is different than
|
||||
// NETWORK_STATUS_READY.
|
||||
if (aCallback) {
|
||||
aCallback(true, "OK");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (elements.length > 0) {
|
||||
// If length of elements is greater than 0, callback is set to
|
||||
// the last element.
|
||||
@ -800,21 +828,29 @@ this.NetworkStatsService = {
|
||||
},
|
||||
|
||||
getAlarms: function getAlarms(mm, msg) {
|
||||
let self = this;
|
||||
let network = msg.data.network;
|
||||
let manifestURL = msg.data.manifestURL;
|
||||
|
||||
let netId = null;
|
||||
if (network) {
|
||||
netId = this.getNetworkId(network.id, network.type);
|
||||
if (!this._networks[netId]) {
|
||||
mm.sendAsyncMessage("NetworkStats:GetAlarms:Return",
|
||||
{ id: msg.id, error: "InvalidInterface", result: null });
|
||||
return;
|
||||
}
|
||||
this.validateNetwork(network, function onValidateNetwork(aNetId) {
|
||||
if (!aNetId) {
|
||||
mm.sendAsyncMessage("NetworkStats:GetAlarms:Return",
|
||||
{ id: msg.id, error: "InvalidInterface", result: null });
|
||||
return;
|
||||
}
|
||||
|
||||
self._getAlarms(mm, msg, aNetId, manifestURL);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._getAlarms(mm, msg, null, manifestURL);
|
||||
},
|
||||
|
||||
_getAlarms: function _getAlarms(mm, msg, aNetId, aManifestURL) {
|
||||
let self = this;
|
||||
this._db.getAlarms(netId, manifestURL, function onCompleted(error, result) {
|
||||
this._db.getAlarms(aNetId, aManifestURL, function onCompleted(error, result) {
|
||||
if (error) {
|
||||
mm.sendAsyncMessage("NetworkStats:GetAlarms:Return",
|
||||
{ id: msg.id, error: error, result: result });
|
||||
@ -884,48 +920,49 @@ this.NetworkStatsService = {
|
||||
return;
|
||||
}
|
||||
|
||||
let netId = this.getNetworkId(network.id, network.type);
|
||||
if (!this._networks[netId]) {
|
||||
mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
|
||||
{ id: msg.id, error: "InvalidiConnectionType", result: null });
|
||||
return;
|
||||
}
|
||||
|
||||
let newAlarm = {
|
||||
id: null,
|
||||
networkId: netId,
|
||||
threshold: threshold,
|
||||
absoluteThreshold: null,
|
||||
startTime: options.startTime,
|
||||
data: options.data,
|
||||
pageURL: options.pageURL,
|
||||
manifestURL: options.manifestURL
|
||||
};
|
||||
|
||||
let self = this;
|
||||
this._updateThreshold(newAlarm, function onUpdate(error, _threshold) {
|
||||
if (error) {
|
||||
this.validateNetwork(network, function onValidateNetwork(aNetId) {
|
||||
if (!aNetId) {
|
||||
mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
|
||||
{ id: msg.id, error: error, result: null });
|
||||
{ id: msg.id, error: "InvalidiConnectionType", result: null });
|
||||
return;
|
||||
}
|
||||
|
||||
newAlarm.absoluteThreshold = _threshold.absoluteThreshold;
|
||||
self._db.addAlarm(newAlarm, function addSuccessCb(error, newId) {
|
||||
let newAlarm = {
|
||||
id: null,
|
||||
networkId: aNetId,
|
||||
threshold: threshold,
|
||||
absoluteThreshold: null,
|
||||
startTime: options.startTime,
|
||||
data: options.data,
|
||||
pageURL: options.pageURL,
|
||||
manifestURL: options.manifestURL
|
||||
};
|
||||
|
||||
self._updateThreshold(newAlarm, function onUpdate(error, _threshold) {
|
||||
if (error) {
|
||||
mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
|
||||
{ id: msg.id, error: error, result: null });
|
||||
return;
|
||||
}
|
||||
|
||||
newAlarm.id = newId;
|
||||
self._setAlarm(newAlarm, function onSet(error, success) {
|
||||
mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
|
||||
{ id: msg.id, error: error, result: newId });
|
||||
|
||||
if (error == "InvalidStateError") {
|
||||
self._fireAlarm(newAlarm);
|
||||
newAlarm.absoluteThreshold = _threshold.absoluteThreshold;
|
||||
self._db.addAlarm(newAlarm, function addSuccessCb(error, newId) {
|
||||
if (error) {
|
||||
mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
|
||||
{ id: msg.id, error: error, result: null });
|
||||
return;
|
||||
}
|
||||
|
||||
newAlarm.id = newId;
|
||||
self._setAlarm(newAlarm, function onSet(error, success) {
|
||||
mm.sendAsyncMessage("NetworkStats:SetAlarm:Return",
|
||||
{ id: msg.id, error: error, result: newId });
|
||||
|
||||
if (error == "InvalidStateError") {
|
||||
self._fireAlarm(newAlarm);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -933,8 +970,9 @@ this.NetworkStatsService = {
|
||||
|
||||
_setAlarm: function _setAlarm(aAlarm, aCallback) {
|
||||
let currentAlarm = this._currentAlarms[aAlarm.networkId];
|
||||
if (Object.getOwnPropertyNames(currentAlarm).length !== 0 &&
|
||||
aAlarm.absoluteThreshold > currentAlarm.alarm.absoluteThreshold) {
|
||||
if ((Object.getOwnPropertyNames(currentAlarm).length !== 0 &&
|
||||
aAlarm.absoluteThreshold > currentAlarm.alarm.absoluteThreshold) ||
|
||||
this._networks[aAlarm.networkId].status != NETWORK_STATUS_READY) {
|
||||
aCallback(null, true);
|
||||
return;
|
||||
}
|
||||
@ -985,6 +1023,14 @@ this.NetworkStatsService = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
// There are no stats for the network of the alarm, set them to default 0 in
|
||||
// order to be able to calculate the offset, systemThreshold and
|
||||
// absoluteThreshold.
|
||||
result = { rxTotalBytes: 0, txTotalBytes: 0,
|
||||
rxSystemBytes: 0, txSystemBytes: 0 };
|
||||
}
|
||||
|
||||
let offset = aAlarm.threshold - result.rxTotalBytes - result.txTotalBytes;
|
||||
|
||||
// Alarm set to a threshold lower than current rx/tx bytes.
|
||||
|
@ -3,6 +3,10 @@
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
const NETWORK_STATUS_READY = 0;
|
||||
const NETWORK_STATUS_STANDBY = 1;
|
||||
const NETWORK_STATUS_AWAY = 2;
|
||||
|
||||
function getNetworks(callback) {
|
||||
NetworkStatsService._db.getAvailableNetworks(function onGetNetworks(aError, aResult) {
|
||||
callback(aError, aResult);
|
||||
@ -85,9 +89,18 @@ add_test(function test_updateQueueIndex() {
|
||||
});
|
||||
|
||||
add_test(function test_updateAllStats() {
|
||||
NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_READY;
|
||||
NetworkStatsService.updateAllStats(function(success, msg) {
|
||||
do_check_eq(success, true);
|
||||
run_next_test();
|
||||
NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_STANDBY;
|
||||
NetworkStatsService.updateAllStats(function(success, msg) {
|
||||
do_check_eq(success, true);
|
||||
NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_AWAY;
|
||||
NetworkStatsService.updateAllStats(function(success, msg) {
|
||||
do_check_eq(success, true);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -189,6 +202,8 @@ add_test(function test_setAlarm_invalid_threshold() {
|
||||
pageURL: testPageURL,
|
||||
manifestURL: testManifestURL };
|
||||
|
||||
NetworkStatsService._networks[wifiId].status = NETWORK_STATUS_READY;
|
||||
|
||||
NetworkStatsService._setAlarm(alarm, function onSet(error, result) {
|
||||
do_check_eq(error, "InvalidStateError");
|
||||
run_next_test();
|
||||
|
@ -93,9 +93,6 @@ let PaymentManager = {
|
||||
if (!pr) {
|
||||
continue;
|
||||
}
|
||||
if (!(pr instanceof Ci.nsIDOMPaymentRequestInfo)) {
|
||||
return;
|
||||
}
|
||||
// We consider jwt type repetition an error.
|
||||
if (jwtTypes[pr.type]) {
|
||||
this.paymentFailed(requestId,
|
||||
@ -342,17 +339,7 @@ let PaymentManager = {
|
||||
}
|
||||
|
||||
let pldRequest = payloadObject.request;
|
||||
let request = Cc["@mozilla.org/payment/request-info;1"]
|
||||
.createInstance(Ci.nsIDOMPaymentRequestInfo);
|
||||
if (!request) {
|
||||
this.paymentFailed(aRequestId,
|
||||
"INTERNAL_ERROR_ERROR_CREATING_PAY_REQUEST");
|
||||
return true;
|
||||
}
|
||||
request.wrappedJSObject.init(aJwt,
|
||||
payloadObject.typ,
|
||||
provider.name);
|
||||
return request;
|
||||
return { jwt: aJwt, type: payloadObject.typ, providerName: provider.name };
|
||||
},
|
||||
|
||||
showPaymentFlow: function showPaymentFlow(aRequestId,
|
||||
|
@ -4,6 +4,3 @@ category JavaScript-navigator-property mozPay @mozilla.org/payment/content-helpe
|
||||
|
||||
component {b8bce4e7-fbf0-4719-a634-b1bf9018657c} PaymentFlowInfo.js
|
||||
contract @mozilla.org/payment/flow-info;1 {b8bce4e7-fbf0-4719-a634-b1bf9018657c}
|
||||
|
||||
component {0a58c67d-f003-48da-81d1-bd8f605f4b1c} PaymentRequestInfo.js
|
||||
contract @mozilla.org/payment/request-info;1 {0a58c67d-f003-48da-81d1-bd8f605f4b1c}
|
||||
|
@ -1,41 +0,0 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const PAYMENTREQUESTINFO_CID =
|
||||
Components.ID("{0a58c67d-f003-48da-81d1-bd8f605f4b1c}");
|
||||
|
||||
// nsIDOMPaymentRequestInfo
|
||||
|
||||
function PaymentRequestInfo() {
|
||||
this.wrappedJSObject = this;
|
||||
};
|
||||
|
||||
PaymentRequestInfo.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMPaymentRequestInfo]),
|
||||
classID: PAYMENTREQUESTINFO_CID,
|
||||
classInfo: XPCOMUtils.generateCI({
|
||||
classID: PAYMENTREQUESTINFO_CID,
|
||||
contractID: "@mozilla.org/payment/request-info;1",
|
||||
classDescription: "Payment request information",
|
||||
flags: Ci.nsIClassInfo.DOM_OBJECT,
|
||||
interfaces: [Ci.nsIDOMPaymentRequestInfo]
|
||||
}),
|
||||
jwt: null,
|
||||
type: null,
|
||||
providerName: null,
|
||||
|
||||
init: function init(aJwt, aType, aProviderName) {
|
||||
this.jwt = aJwt;
|
||||
this.type = aType;
|
||||
this.providerName = aProviderName;
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PaymentRequestInfo]);
|
@ -5,7 +5,6 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDOMPaymentRequestInfo.idl',
|
||||
'nsINavigatorPayment.idl',
|
||||
'nsIPaymentFlowInfo.idl',
|
||||
'nsIPaymentUIGlue.idl',
|
||||
|
@ -1,19 +0,0 @@
|
||||
/* 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 "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(93462984-0e9a-4016-bdb4-a24a88c08a29)]
|
||||
interface nsIDOMPaymentRequestInfo : nsISupports
|
||||
{
|
||||
// Base64 encoded and digitally signed payment request.
|
||||
readonly attribute DOMString jwt;
|
||||
|
||||
// JWT type that identifies the payment provider owner of the payment request
|
||||
// format.
|
||||
readonly attribute DOMString type;
|
||||
|
||||
// Payment provider name.
|
||||
readonly attribute DOMString providerName;
|
||||
};
|
@ -16,6 +16,5 @@ EXTRA_COMPONENTS += [
|
||||
'Payment.js',
|
||||
'Payment.manifest',
|
||||
'PaymentFlowInfo.js',
|
||||
'PaymentRequestInfo.js',
|
||||
]
|
||||
|
||||
|
@ -288,6 +288,11 @@ static bool gCrossSlideEnabled = false;
|
||||
*/
|
||||
static bool gUseProgressiveTilePainting = false;
|
||||
|
||||
/**
|
||||
* Pref that allows or disallows checkerboarding
|
||||
*/
|
||||
static bool gAllowCheckerboarding = true;
|
||||
|
||||
/**
|
||||
* Is aAngle within the given threshold of the horizontal axis?
|
||||
* @param aAngle an angle in radians in the range [0, pi]
|
||||
@ -412,6 +417,7 @@ AsyncPanZoomController::InitializeGlobalState()
|
||||
Preferences::AddIntVarCache(&gAsyncScrollTimeout, "apz.asyncscroll.timeout", gAsyncScrollTimeout);
|
||||
Preferences::AddBoolVarCache(&gCrossSlideEnabled, "apz.cross_slide.enabled", gCrossSlideEnabled);
|
||||
Preferences::AddIntVarCache(&gAxisLockMode, "apz.axis_lock_mode", gAxisLockMode);
|
||||
Preferences::AddBoolVarCache(&gAllowCheckerboarding, "apz.allow-checkerboarding", gAllowCheckerboarding);
|
||||
gUseProgressiveTilePainting = gfxPlatform::UseProgressiveTilePainting();
|
||||
|
||||
gComputedTimingFunction = new ComputedTimingFunction();
|
||||
@ -994,10 +1000,6 @@ const ScreenPoint AsyncPanZoomController::GetVelocityVector() {
|
||||
return ScreenPoint(mX.GetVelocity(), mY.GetVelocity());
|
||||
}
|
||||
|
||||
const gfx::Point AsyncPanZoomController::GetAccelerationVector() {
|
||||
return gfx::Point(mX.GetAccelerationFactor(), mY.GetAccelerationFactor());
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::HandlePanningWithTouchAction(double aAngle, TouchBehaviorFlags aBehavior) {
|
||||
// Handling of cross sliding will need to be added in this method after touch-action released
|
||||
// enabled by default.
|
||||
@ -1309,7 +1311,6 @@ EnlargeDisplayPortAlongAxis(float* aOutOffset, float* aOutLength,
|
||||
const CSSRect AsyncPanZoomController::CalculatePendingDisplayPort(
|
||||
const FrameMetrics& aFrameMetrics,
|
||||
const ScreenPoint& aVelocity,
|
||||
const gfx::Point& aAcceleration,
|
||||
double aEstimatedPaintDuration)
|
||||
{
|
||||
// convert to milliseconds
|
||||
@ -1343,10 +1344,9 @@ const CSSRect AsyncPanZoomController::CalculatePendingDisplayPort(
|
||||
displayPort = displayPort.ForceInside(scrollableRect) - scrollOffset;
|
||||
|
||||
APZC_LOG_FM(aFrameMetrics,
|
||||
"Calculated displayport as (%f %f %f %f) from velocity (%f %f) acceleration (%f %f) paint time %f metrics",
|
||||
"Calculated displayport as (%f %f %f %f) from velocity (%f %f) paint time %f metrics",
|
||||
displayPort.x, displayPort.y, displayPort.width, displayPort.height,
|
||||
aVelocity.x, aVelocity.y, aAcceleration.x, aAcceleration.y,
|
||||
(float)estimatedPaintDurationMillis);
|
||||
aVelocity.x, aVelocity.y, (float)estimatedPaintDurationMillis);
|
||||
|
||||
return displayPort;
|
||||
}
|
||||
@ -1365,7 +1365,6 @@ void AsyncPanZoomController::RequestContentRepaint(FrameMetrics& aFrameMetrics)
|
||||
aFrameMetrics.mDisplayPort =
|
||||
CalculatePendingDisplayPort(aFrameMetrics,
|
||||
GetVelocityVector(),
|
||||
GetAccelerationVector(),
|
||||
mPaintThrottler.AverageDuration().ToSeconds());
|
||||
|
||||
// If we're trying to paint what we already think is painted, discard this
|
||||
@ -1537,7 +1536,28 @@ ViewTransform AsyncPanZoomController::GetCurrentAsyncTransform() {
|
||||
if (mLastContentPaintMetrics.IsScrollable()) {
|
||||
lastPaintScrollOffset = mLastContentPaintMetrics.mScrollOffset;
|
||||
}
|
||||
LayerPoint translation = (mFrameMetrics.mScrollOffset - lastPaintScrollOffset)
|
||||
|
||||
CSSPoint currentScrollOffset = mFrameMetrics.mScrollOffset;
|
||||
|
||||
// If checkerboarding has been disallowed, clamp the scroll position to stay
|
||||
// within rendered content.
|
||||
if (!gAllowCheckerboarding &&
|
||||
!mLastContentPaintMetrics.mDisplayPort.IsEmpty()) {
|
||||
CSSRect compositedRect = mLastContentPaintMetrics.CalculateCompositedRectInCssPixels();
|
||||
CSSPoint maxScrollOffset = lastPaintScrollOffset +
|
||||
CSSPoint(mLastContentPaintMetrics.mDisplayPort.XMost() - compositedRect.width,
|
||||
mLastContentPaintMetrics.mDisplayPort.YMost() - compositedRect.height);
|
||||
CSSPoint minScrollOffset = lastPaintScrollOffset + mLastContentPaintMetrics.mDisplayPort.TopLeft();
|
||||
|
||||
if (minScrollOffset.x < maxScrollOffset.x) {
|
||||
currentScrollOffset.x = clamped(currentScrollOffset.x, minScrollOffset.x, maxScrollOffset.x);
|
||||
}
|
||||
if (minScrollOffset.y < maxScrollOffset.y) {
|
||||
currentScrollOffset.y = clamped(currentScrollOffset.y, minScrollOffset.y, maxScrollOffset.y);
|
||||
}
|
||||
}
|
||||
|
||||
LayerPoint translation = (currentScrollOffset - lastPaintScrollOffset)
|
||||
* mLastContentPaintMetrics.LayersPixelsPerCSSPixel();
|
||||
|
||||
return ViewTransform(-translation,
|
||||
@ -1720,7 +1740,6 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
|
||||
endZoomToMetrics.mDisplayPort =
|
||||
CalculatePendingDisplayPort(endZoomToMetrics,
|
||||
ScreenPoint(0,0),
|
||||
gfx::Point(0,0),
|
||||
0);
|
||||
|
||||
StartAnimation(new ZoomAnimation(
|
||||
|
@ -240,7 +240,6 @@ public:
|
||||
static const CSSRect CalculatePendingDisplayPort(
|
||||
const FrameMetrics& aFrameMetrics,
|
||||
const ScreenPoint& aVelocity,
|
||||
const gfx::Point& aAcceleration,
|
||||
double aEstimatedPaintDuration);
|
||||
|
||||
/**
|
||||
@ -438,11 +437,6 @@ protected:
|
||||
*/
|
||||
const ScreenPoint GetVelocityVector();
|
||||
|
||||
/**
|
||||
* Gets a vector of the acceleration factors of each axis.
|
||||
*/
|
||||
const gfx::Point GetAccelerationVector();
|
||||
|
||||
/**
|
||||
* Gets a reference to the first touch point from a MultiTouchInput. This
|
||||
* gets only the first one and assumes the rest are either missing or not
|
||||
|
@ -33,22 +33,6 @@ static float gMaxEventAcceleration = 999.0f;
|
||||
*/
|
||||
static float gFlingFriction = 0.002f;
|
||||
|
||||
/**
|
||||
* Threshold for velocity beneath which we turn off any acceleration we had
|
||||
* during repeated flings.
|
||||
*/
|
||||
static float gVelocityThreshold = 0.14f;
|
||||
|
||||
/**
|
||||
* Amount of acceleration we multiply in each time the user flings in one
|
||||
* direction. Every time they let go of the screen, we increase the acceleration
|
||||
* by this amount raised to the power of the amount of times they have let go,
|
||||
* times two (to make the curve steeper). This stops if the user lets go and we
|
||||
* slow down enough, or if they put their finger down without moving it for a
|
||||
* moment (or in the opposite direction).
|
||||
*/
|
||||
static float gAccelerationMultiplier = 1.125f;
|
||||
|
||||
/**
|
||||
* When flinging, if the velocity goes below this number, we just stop the
|
||||
* animation completely. This is to prevent asymptotically approaching 0
|
||||
@ -67,8 +51,6 @@ static void ReadAxisPrefs()
|
||||
{
|
||||
Preferences::AddFloatVarCache(&gMaxEventAcceleration, "apz.max_event_acceleration", gMaxEventAcceleration);
|
||||
Preferences::AddFloatVarCache(&gFlingFriction, "apz.fling_friction", gFlingFriction);
|
||||
Preferences::AddFloatVarCache(&gVelocityThreshold, "apz.velocity_threshold", gVelocityThreshold);
|
||||
Preferences::AddFloatVarCache(&gAccelerationMultiplier, "apz.acceleration_multiplier", gAccelerationMultiplier);
|
||||
Preferences::AddFloatVarCache(&gFlingStoppedThreshold, "apz.fling_stopped_threshold", gFlingStoppedThreshold);
|
||||
Preferences::AddUintVarCache(&gMaxVelocityQueueSize, "apz.max_velocity_queue_size", gMaxVelocityQueueSize);
|
||||
}
|
||||
@ -100,7 +82,6 @@ static void InitAxisPrefs()
|
||||
Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
|
||||
: mPos(0),
|
||||
mVelocity(0.0f),
|
||||
mAcceleration(0),
|
||||
mAxisLocked(false),
|
||||
mAsyncPanZoomController(aAsyncPanZoomController)
|
||||
{
|
||||
@ -110,15 +91,6 @@ Axis::Axis(AsyncPanZoomController* aAsyncPanZoomController)
|
||||
void Axis::UpdateWithTouchAtDevicePoint(int32_t aPos, const TimeDuration& aTimeDelta) {
|
||||
float newVelocity = mAxisLocked ? 0 : (mPos - aPos) / aTimeDelta.ToMilliseconds();
|
||||
|
||||
bool curVelocityBelowThreshold = fabsf(newVelocity) < gVelocityThreshold;
|
||||
bool directionChange = (mVelocity > 0) != (newVelocity > 0);
|
||||
|
||||
// If we've changed directions, or the current velocity threshold, stop any
|
||||
// acceleration we've accumulated.
|
||||
if (directionChange || curVelocityBelowThreshold) {
|
||||
mAcceleration = 0;
|
||||
}
|
||||
|
||||
mVelocity = newVelocity;
|
||||
mPos = aPos;
|
||||
|
||||
@ -145,23 +117,17 @@ float Axis::AdjustDisplacement(float aDisplacement, float& aOverscrollAmountOut,
|
||||
if (aScrollingDisabled) {
|
||||
// Scrolling is disabled on this axis, stop scrolling.
|
||||
aOverscrollAmountOut = aDisplacement;
|
||||
mAcceleration = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fabsf(mVelocity) < gVelocityThreshold) {
|
||||
mAcceleration = 0;
|
||||
}
|
||||
float displacement = aDisplacement;
|
||||
|
||||
float accelerationFactor = GetAccelerationFactor();
|
||||
float displacement = aDisplacement * accelerationFactor;
|
||||
// If this displacement will cause an overscroll, throttle it. Can potentially
|
||||
// bring it to 0 even if the velocity is high.
|
||||
if (DisplacementWillOverscroll(displacement) != OVERSCROLL_NONE) {
|
||||
// No need to have a velocity along this axis anymore; it won't take us
|
||||
// anywhere, so we're just spinning needlessly.
|
||||
mVelocity = 0.0f;
|
||||
mAcceleration = 0;
|
||||
aOverscrollAmountOut = DisplacementWillOverscrollAmount(displacement);
|
||||
displacement -= aOverscrollAmountOut;
|
||||
}
|
||||
@ -177,8 +143,6 @@ float Axis::PanDistance(float aPos) {
|
||||
}
|
||||
|
||||
void Axis::EndTouch() {
|
||||
mAcceleration++;
|
||||
|
||||
// Calculate the mean velocity and empty the queue.
|
||||
int count = mVelocityQueue.Length();
|
||||
if (count) {
|
||||
@ -193,7 +157,6 @@ void Axis::EndTouch() {
|
||||
|
||||
void Axis::CancelTouch() {
|
||||
mVelocity = 0.0f;
|
||||
mAcceleration = 0;
|
||||
while (!mVelocityQueue.IsEmpty()) {
|
||||
mVelocityQueue.RemoveElementAt(0);
|
||||
}
|
||||
@ -302,10 +265,6 @@ float Axis::GetVelocity() {
|
||||
return mAxisLocked ? 0 : mVelocity;
|
||||
}
|
||||
|
||||
float Axis::GetAccelerationFactor() {
|
||||
return powf(gAccelerationMultiplier, std::max(0, (mAcceleration - 4) * 3));
|
||||
}
|
||||
|
||||
float Axis::GetCompositionEnd() {
|
||||
return GetOrigin() + GetCompositionLength();
|
||||
}
|
||||
|
@ -70,12 +70,11 @@ public:
|
||||
void CancelTouch();
|
||||
|
||||
/**
|
||||
* Takes a requested displacement to the position of this axis, and adjusts
|
||||
* it to account for acceleration (which might increase the displacement),
|
||||
* overscroll (which might decrease the displacement; this is to prevent the
|
||||
* viewport from overscrolling the page rect), and axis locking (which might
|
||||
* prevent any displacement from happening). If overscroll ocurred, its amount
|
||||
* is written to |aOverscrollAmountOut|.
|
||||
* Takes a requested displacement to the position of this axis, and adjusts it
|
||||
* to account for overscroll (which might decrease the displacement; this is
|
||||
* to prevent the viewport from overscrolling the page rect), and axis locking
|
||||
* (which might prevent any displacement from happening). If overscroll
|
||||
* ocurred, its amount is written to |aOverscrollAmountOut|.
|
||||
* The adjusted displacement is returned.
|
||||
*
|
||||
* aScrollingDisabled is used to indicate that no scrolling should happen
|
||||
@ -128,12 +127,6 @@ public:
|
||||
*/
|
||||
float GetExcess();
|
||||
|
||||
/**
|
||||
* Gets the factor of acceleration applied to the velocity, based on the
|
||||
* amount of flings that have been done successively.
|
||||
*/
|
||||
float GetAccelerationFactor();
|
||||
|
||||
/**
|
||||
* Gets the raw velocity of this axis at this moment.
|
||||
*/
|
||||
@ -188,12 +181,6 @@ protected:
|
||||
int32_t mPos;
|
||||
int32_t mStartPos;
|
||||
float mVelocity;
|
||||
// Acceleration is represented by an int, which is the power we raise a
|
||||
// constant to and then multiply the velocity by whenever it is sampled. We do
|
||||
// this only when we detect that the user wants to do a fast fling; that is,
|
||||
// they are flinging multiple times in a row very quickly, probably trying to
|
||||
// reach one of the extremes of the page.
|
||||
int32_t mAcceleration;
|
||||
bool mAxisLocked; // Whether movement on this axis is locked.
|
||||
AsyncPanZoomController* mAsyncPanZoomController;
|
||||
nsTArray<float> mVelocityQueue;
|
||||
|
@ -581,7 +581,7 @@ bool ProcessGeneric(ots::OpenTypeFile *header, uint32_t signature,
|
||||
const std::map<uint32_t, OpenTypeTable>::const_iterator it
|
||||
= table_map.find(Tag(table_parsers[i].tag));
|
||||
|
||||
ots::TableAction action = GetTableAction(it->first);
|
||||
ots::TableAction action = GetTableAction(Tag(table_parsers[i].tag));
|
||||
if (it == table_map.end()) {
|
||||
if (table_parsers[i].required && action == ots::TABLE_ACTION_SANITIZE) {
|
||||
return OTS_FAILURE_MSG_TAG("missing required table", table_parsers[i].tag);
|
||||
|
@ -22,6 +22,10 @@
|
||||
#include "mozilla/ipc/MessageLink.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
|
||||
#if defined(ANDROID) && defined(DEBUG)
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
|
||||
// WARNING: this takes into account the private, special-message-type
|
||||
// enum in ipc_channel.h. They need to be kept in sync.
|
||||
namespace {
|
||||
|
@ -1825,11 +1825,13 @@ def _generateMessageClass(clsname, msgid, prettyName, compress):
|
||||
# generate a logging function
|
||||
# 'pfx' will be something like "[FooParent] sent"
|
||||
pfxvar = ExprVar('__pfx')
|
||||
outfvar = ExprVar('__outf')
|
||||
otherprocess = ExprVar('__otherProcess')
|
||||
receiving = ExprVar('__receiving')
|
||||
logger = MethodDefn(MethodDecl(
|
||||
'Log',
|
||||
params=([ Decl(Type('std::string', const=1, ref=1), pfxvar.name),
|
||||
Decl(Type('FILE', ptr=True), outfvar.name) ]),
|
||||
Decl(Type('base::ProcessHandle'), otherprocess.name),
|
||||
Decl(Type('bool'), receiving.name) ]),
|
||||
const=1))
|
||||
# TODO/cjones: allow selecting what information is printed to
|
||||
# the log
|
||||
@ -1843,9 +1845,12 @@ def _generateMessageClass(clsname, msgid, prettyName, compress):
|
||||
StmtExpr(ExprCall(
|
||||
ExprVar('StringAppendF'),
|
||||
args=[ ExprAddrOf(msgvar),
|
||||
ExprLiteral.String('[time:%" PRId64 "][%d]'),
|
||||
ExprLiteral.String('[time:%" PRId64 "][%d%s%d]'),
|
||||
ExprCall(ExprVar('PR_Now')),
|
||||
ExprCall(ExprVar('base::GetCurrentProcId')) ])),
|
||||
ExprCall(ExprVar('base::GetCurrentProcId')),
|
||||
ExprConditional(receiving, ExprLiteral.String('<-'),
|
||||
ExprLiteral.String('->')),
|
||||
otherprocess ])),
|
||||
appendToMsg(pfxvar),
|
||||
appendToMsg(ExprLiteral.String(clsname +'(')),
|
||||
Whitespace.NL
|
||||
@ -1855,10 +1860,21 @@ def _generateMessageClass(clsname, msgid, prettyName, compress):
|
||||
|
||||
logger.addstmt(appendToMsg(ExprLiteral.String('[TODO])\\n')))
|
||||
|
||||
logger.addstmts([
|
||||
CppDirective('ifdef', 'ANDROID'),
|
||||
StmtExpr(ExprCall(
|
||||
ExprVar('__android_log_write'),
|
||||
args=[ ExprVar('ANDROID_LOG_INFO'),
|
||||
ExprLiteral.String('GeckoIPC'),
|
||||
ExprCall(ExprSelect(msgvar, '.', 'c_str')) ])),
|
||||
CppDirective('endif')
|
||||
])
|
||||
|
||||
# and actually print the log message
|
||||
logger.addstmt(StmtExpr(ExprCall(
|
||||
ExprVar('fputs'),
|
||||
args=[ ExprCall(ExprSelect(msgvar, '.', 'c_str')), outfvar ])))
|
||||
args=[ ExprCall(ExprSelect(msgvar, '.', 'c_str')),
|
||||
ExprVar('stderr') ])))
|
||||
|
||||
cls.addstmt(logger)
|
||||
|
||||
@ -4929,7 +4945,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
+ sendstmts)
|
||||
|
||||
destmts = self.deserializeReply(
|
||||
md, ExprAddrOf(replyvar), self.side, errfnSend)
|
||||
md, ExprAddrOf(replyvar), self.side, errfnSend, actorvar)
|
||||
ifsendok = StmtIf(ExprLiteral.FALSE)
|
||||
ifsendok.addifstmts(destmts)
|
||||
ifsendok.addifstmts([ Whitespace.NL,
|
||||
@ -5165,7 +5181,8 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
'.', 'set_name'),
|
||||
args=[ ExprLiteral.String(md.prettyMsgName(self.protocol.name
|
||||
+'::')) ])),
|
||||
self.logMessage(md, md.msgCast(msgexpr), 'Received '),
|
||||
self.logMessage(md, md.msgCast(msgexpr), 'Received ',
|
||||
receiving=True),
|
||||
self.profilerLabel('Recv', md.decl.progname),
|
||||
Whitespace.NL
|
||||
])
|
||||
@ -5200,10 +5217,10 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
return stmts
|
||||
|
||||
|
||||
def deserializeReply(self, md, replyexpr, side, errfn):
|
||||
def deserializeReply(self, md, replyexpr, side, errfn, actor=None):
|
||||
stmts = [ Whitespace.NL,
|
||||
self.logMessage(md, md.replyCast(replyexpr),
|
||||
'Received reply ') ]
|
||||
'Received reply ', actor, receiving=True) ]
|
||||
if 0 == len(md.returns):
|
||||
return stmts
|
||||
|
||||
@ -5226,7 +5243,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
return (
|
||||
sendok,
|
||||
([ Whitespace.NL,
|
||||
self.logMessage(md, msgexpr, 'Sending '),
|
||||
self.logMessage(md, msgexpr, 'Sending ', actor),
|
||||
self.profilerLabel('AsyncSend', md.decl.progname) ]
|
||||
+ self.transition(md, 'out', actor)
|
||||
+ [ Whitespace.NL,
|
||||
@ -5243,7 +5260,7 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
return (
|
||||
sendok,
|
||||
([ Whitespace.NL,
|
||||
self.logMessage(md, msgexpr, 'Sending '),
|
||||
self.logMessage(md, msgexpr, 'Sending ', actor),
|
||||
self.profilerLabel('Send', md.decl.progname) ]
|
||||
+ self.transition(md, 'out', actor)
|
||||
+ [ Whitespace.NL,
|
||||
@ -5312,13 +5329,14 @@ class _GenerateProtocolActorCode(ipdl.ast.Visitor):
|
||||
decl.ret = md.actorDecl().bareType(self.side)
|
||||
return decl
|
||||
|
||||
def logMessage(self, md, msgptr, pfx):
|
||||
def logMessage(self, md, msgptr, pfx, actor=None, receiving=False):
|
||||
actorname = _actorName(self.protocol.name, self.side)
|
||||
return _ifLogging([
|
||||
StmtExpr(ExprCall(
|
||||
ExprSelect(msgptr, '->', 'Log'),
|
||||
args=[ ExprLiteral.String('['+ actorname +'] '+ pfx),
|
||||
ExprVar('stderr') ])) ])
|
||||
|
||||
return _ifLogging([ StmtExpr(ExprCall(
|
||||
ExprSelect(msgptr, '->', 'Log'),
|
||||
args=[ ExprLiteral.String('['+ actorname +'] '+ pfx),
|
||||
self.protocol.callOtherProcess(actor),
|
||||
ExprLiteral.TRUE if receiving else ExprLiteral.FALSE ])) ])
|
||||
|
||||
def profilerLabel(self, tag, msgname):
|
||||
return StmtExpr(ExprCall(ExprVar('PROFILER_LABEL'),
|
||||
|
@ -174,6 +174,9 @@ public:
|
||||
RefPtr<UnixSocketConsumer> mConsumer;
|
||||
|
||||
private:
|
||||
|
||||
void FireSocketError();
|
||||
|
||||
/**
|
||||
* libevent triggered functions that reads data from socket when available and
|
||||
* guarenteed non-blocking. Only to be called on IO thread.
|
||||
@ -487,29 +490,47 @@ void ShutdownSocketTask::Run()
|
||||
}
|
||||
|
||||
void
|
||||
UnixSocketImpl::Accept()
|
||||
UnixSocketImpl::FireSocketError()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
if (!mConnector) {
|
||||
NS_WARNING("No connector object available!");
|
||||
return;
|
||||
}
|
||||
// Clean up watchers, statuses, fds
|
||||
mReadWatcher.StopWatchingFileDescriptor();
|
||||
mWriteWatcher.StopWatchingFileDescriptor();
|
||||
mConnectionStatus = SOCKET_DISCONNECTED;
|
||||
mFd.reset(-1);
|
||||
|
||||
// Tell the main thread we've errored
|
||||
nsRefPtr<OnSocketEventTask> t =
|
||||
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
|
||||
NS_DispatchToMainThread(t);
|
||||
}
|
||||
|
||||
void
|
||||
UnixSocketImpl::Accept()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mConnector);
|
||||
|
||||
// This will set things we don't particularly care about, but it will hand
|
||||
// back the correct structure size which is what we do care about.
|
||||
if (!mConnector->CreateAddr(true, mAddrSize, mAddr, nullptr)) {
|
||||
NS_WARNING("Cannot create socket address!");
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFd.get() < 0) {
|
||||
mFd = mConnector->Create();
|
||||
if (mFd.get() < 0) {
|
||||
NS_WARNING("Cannot create socket fd!");
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SetSocketFlags()) {
|
||||
NS_WARNING("Cannot set socket flags!");
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -517,6 +538,7 @@ UnixSocketImpl::Accept()
|
||||
#ifdef DEBUG
|
||||
CHROMIUM_LOG("...bind(%d) gave errno %d", mFd.get(), errno);
|
||||
#endif
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -524,15 +546,13 @@ UnixSocketImpl::Accept()
|
||||
#ifdef DEBUG
|
||||
CHROMIUM_LOG("...listen(%d) gave errno %d", mFd.get(), errno);
|
||||
#endif
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mConnector->SetUpListenSocket(mFd)) {
|
||||
NS_WARNING("Could not set up listen socket!");
|
||||
nsRefPtr<OnSocketEventTask> t =
|
||||
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
|
||||
NS_DispatchToMainThread(t);
|
||||
mConnectionStatus = SOCKET_DISCONNECTED;
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -545,15 +565,13 @@ void
|
||||
UnixSocketImpl::Connect()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
if (!mConnector) {
|
||||
NS_WARNING("No connector object available!");
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(mConnector);
|
||||
|
||||
if (mFd.get() < 0) {
|
||||
mFd = mConnector->Create();
|
||||
if (mFd.get() < 0) {
|
||||
NS_WARNING("Cannot create socket fd!");
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -562,15 +580,14 @@ UnixSocketImpl::Connect()
|
||||
|
||||
if (!mConnector->CreateAddr(false, mAddrSize, mAddr, mAddress.get())) {
|
||||
NS_WARNING("Cannot create socket address!");
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
// Select non-blocking IO.
|
||||
if (-1 == fcntl(mFd.get(), F_SETFL, O_NONBLOCK)) {
|
||||
nsRefPtr<OnSocketEventTask> t =
|
||||
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
|
||||
NS_DispatchToMainThread(t);
|
||||
mConnectionStatus = SOCKET_DISCONNECTED;
|
||||
NS_WARNING("Cannot set nonblock!");
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -583,20 +600,12 @@ UnixSocketImpl::Connect()
|
||||
int current_opts = fcntl(mFd.get(), F_GETFL, 0);
|
||||
if (-1 == current_opts) {
|
||||
NS_WARNING("Cannot get socket opts!");
|
||||
mFd.reset(-1);
|
||||
nsRefPtr<OnSocketEventTask> t =
|
||||
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
|
||||
NS_DispatchToMainThread(t);
|
||||
mConnectionStatus = SOCKET_DISCONNECTED;
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
if (-1 == fcntl(mFd.get(), F_SETFL, current_opts & ~O_NONBLOCK)) {
|
||||
NS_WARNING("Cannot set socket opts to blocking!");
|
||||
mFd.reset(-1);
|
||||
nsRefPtr<OnSocketEventTask> t =
|
||||
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
|
||||
NS_DispatchToMainThread(t);
|
||||
mConnectionStatus = SOCKET_DISCONNECTED;
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -616,20 +625,19 @@ UnixSocketImpl::Connect()
|
||||
#if DEBUG
|
||||
CHROMIUM_LOG("Socket connect errno=%d\n", errno);
|
||||
#endif
|
||||
mFd.reset(-1);
|
||||
nsRefPtr<OnSocketEventTask> t =
|
||||
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
|
||||
NS_DispatchToMainThread(t);
|
||||
mConnectionStatus = SOCKET_DISCONNECTED;
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SetSocketFlags()) {
|
||||
NS_WARNING("Cannot set socket flags!");
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mConnector->SetUp(mFd)) {
|
||||
NS_WARNING("Could not set up socket!");
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -862,30 +870,19 @@ UnixSocketImpl::OnFileCanWriteWithoutBlocking(int aFd)
|
||||
|
||||
if (ret || error) {
|
||||
NS_WARNING("getsockopt failure on async socket connect!");
|
||||
mFd.reset(-1);
|
||||
nsRefPtr<OnSocketEventTask> t =
|
||||
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
|
||||
NS_DispatchToMainThread(t);
|
||||
mConnectionStatus = SOCKET_DISCONNECTED;
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SetSocketFlags()) {
|
||||
mFd.reset(-1);
|
||||
nsRefPtr<OnSocketEventTask> t =
|
||||
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
|
||||
NS_DispatchToMainThread(t);
|
||||
mConnectionStatus = SOCKET_DISCONNECTED;
|
||||
NS_WARNING("Cannot set socket flags!");
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mConnector->SetUp(mFd)) {
|
||||
NS_WARNING("Could not set up socket!");
|
||||
mFd.reset(-1);
|
||||
nsRefPtr<OnSocketEventTask> t =
|
||||
new OnSocketEventTask(this, OnSocketEventTask::CONNECT_ERROR);
|
||||
NS_DispatchToMainThread(t);
|
||||
mConnectionStatus = SOCKET_DISCONNECTED;
|
||||
FireSocketError();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -95,10 +95,18 @@ class CPOWProxyHandler : public BaseProxyHandler
|
||||
|
||||
CPOWProxyHandler CPOWProxyHandler::singleton;
|
||||
|
||||
#define FORWARD(call, args) \
|
||||
JavaScriptParent *parent = ParentOf(proxy); \
|
||||
if (!parent->active()) { \
|
||||
JS_ReportError(cx, "cannot use a CPOW whose process is gone"); \
|
||||
return false; \
|
||||
} \
|
||||
return parent->call args;
|
||||
|
||||
bool
|
||||
CPOWProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy)
|
||||
{
|
||||
return ParentOf(proxy)->preventExtensions(cx, proxy);
|
||||
FORWARD(preventExtensions, (cx, proxy));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -117,7 +125,7 @@ bool
|
||||
CPOWProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc, unsigned flags)
|
||||
{
|
||||
return ParentOf(proxy)->getPropertyDescriptor(cx, proxy, id, desc, flags);
|
||||
FORWARD(getPropertyDescriptor, (cx, proxy, id, desc, flags));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -145,7 +153,7 @@ CPOWProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy,
|
||||
HandleId id, MutableHandle<JSPropertyDescriptor> desc,
|
||||
unsigned flags)
|
||||
{
|
||||
return ParentOf(proxy)->getOwnPropertyDescriptor(cx, proxy, id, desc, flags);
|
||||
FORWARD(getOwnPropertyDescriptor, (cx, proxy, id, desc, flags));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -172,7 +180,7 @@ bool
|
||||
CPOWProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
MutableHandle<JSPropertyDescriptor> desc)
|
||||
{
|
||||
return ParentOf(proxy)->defineProperty(cx, proxy, id, desc);
|
||||
FORWARD(defineProperty, (cx, proxy, id, desc));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -199,7 +207,7 @@ JavaScriptParent::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
|
||||
bool
|
||||
CPOWProxyHandler::getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props)
|
||||
{
|
||||
return ParentOf(proxy)->getOwnPropertyNames(cx, proxy, props);
|
||||
FORWARD(getOwnPropertyNames, (cx, proxy, props));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -211,7 +219,7 @@ JavaScriptParent::getOwnPropertyNames(JSContext *cx, HandleObject proxy, AutoIdV
|
||||
bool
|
||||
CPOWProxyHandler::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
|
||||
{
|
||||
return ParentOf(proxy)->delete_(cx, proxy, id, bp);
|
||||
FORWARD(delete_, (cx, proxy, id, bp));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -233,7 +241,7 @@ JavaScriptParent::delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *
|
||||
bool
|
||||
CPOWProxyHandler::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props)
|
||||
{
|
||||
return ParentOf(proxy)->enumerate(cx, proxy, props);
|
||||
FORWARD(enumerate, (cx, proxy, props));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -245,7 +253,7 @@ JavaScriptParent::enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &pro
|
||||
bool
|
||||
CPOWProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
|
||||
{
|
||||
return ParentOf(proxy)->has(cx, proxy, id, bp);
|
||||
FORWARD(has, (cx, proxy, id, bp));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -267,7 +275,7 @@ JavaScriptParent::has(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
|
||||
bool
|
||||
CPOWProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp)
|
||||
{
|
||||
return ParentOf(proxy)->hasOwn(cx, proxy, id, bp);
|
||||
FORWARD(hasOwn, (cx, proxy, id, bp));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -290,7 +298,7 @@ bool
|
||||
CPOWProxyHandler::get(JSContext *cx, HandleObject proxy, HandleObject receiver,
|
||||
HandleId id, MutableHandleValue vp)
|
||||
{
|
||||
return ParentOf(proxy)->get(cx, proxy, receiver, id, vp);
|
||||
FORWARD(get, (cx, proxy, receiver, id, vp));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -319,7 +327,7 @@ bool
|
||||
CPOWProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
|
||||
JS::HandleId id, bool strict, JS::MutableHandleValue vp)
|
||||
{
|
||||
return ParentOf(proxy)->set(cx, proxy, receiver, id, strict, vp);
|
||||
FORWARD(set, (cx, proxy, receiver, id, strict, vp));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -351,7 +359,7 @@ JavaScriptParent::set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject re
|
||||
bool
|
||||
CPOWProxyHandler::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
|
||||
{
|
||||
return ParentOf(proxy)->keys(cx, proxy, props);
|
||||
FORWARD(keys, (cx, proxy, props));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -363,7 +371,7 @@ JavaScriptParent::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
|
||||
bool
|
||||
CPOWProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
|
||||
{
|
||||
return ParentOf(proxy)->isExtensible(cx, proxy, extensible);
|
||||
FORWARD(isExtensible, (cx, proxy, extensible));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -381,7 +389,7 @@ JavaScriptParent::isExtensible(JSContext *cx, HandleObject proxy, bool *extensib
|
||||
bool
|
||||
CPOWProxyHandler::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
|
||||
{
|
||||
return ParentOf(proxy)->call(cx, proxy, args);
|
||||
FORWARD(call, (cx, proxy, args));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -456,7 +464,7 @@ JavaScriptParent::call(JSContext *cx, HandleObject proxy, const CallArgs &args)
|
||||
bool
|
||||
CPOWProxyHandler::objectClassIs(HandleObject proxy, js::ESClassValue classValue, JSContext *cx)
|
||||
{
|
||||
return ParentOf(proxy)->objectClassIs(cx, proxy, classValue);
|
||||
FORWARD(objectClassIs, (cx, proxy, classValue));
|
||||
}
|
||||
|
||||
bool
|
||||
@ -476,7 +484,10 @@ JavaScriptParent::objectClassIs(JSContext *cx, HandleObject proxy, js::ESClassVa
|
||||
const char *
|
||||
CPOWProxyHandler::className(JSContext *cx, HandleObject proxy)
|
||||
{
|
||||
return ParentOf(proxy)->className(cx, proxy);
|
||||
JavaScriptParent *parent = ParentOf(proxy);
|
||||
if (!parent->active())
|
||||
return "<dead CPOW>";
|
||||
return parent->className(cx, proxy);
|
||||
}
|
||||
|
||||
const char *
|
||||
@ -486,7 +497,7 @@ JavaScriptParent::className(JSContext *cx, HandleObject proxy)
|
||||
|
||||
nsString name;
|
||||
if (!CallClassName(objId, &name))
|
||||
return nullptr;
|
||||
return "<error>";
|
||||
|
||||
return ToNewCString(name);
|
||||
}
|
||||
@ -634,10 +645,9 @@ JavaScriptParent::incref()
|
||||
}
|
||||
|
||||
void
|
||||
JavaScriptParent::destroyFromContent()
|
||||
JavaScriptParent::ActorDestroy(ActorDestroyReason why)
|
||||
{
|
||||
inactive_ = true;
|
||||
decref();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
@ -647,9 +657,12 @@ JavaScriptParent::IsCPOW(JSObject *obj)
|
||||
}
|
||||
|
||||
/* static */ nsresult
|
||||
JavaScriptParent::InstanceOf(JSObject *obj, const nsID *id, bool *bp)
|
||||
JavaScriptParent::InstanceOf(JSObject *proxy, const nsID *id, bool *bp)
|
||||
{
|
||||
return ParentOf(obj)->instanceOf(obj, id, bp);
|
||||
JavaScriptParent *parent = ParentOf(proxy);
|
||||
if (!parent->active())
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
return parent->instanceOf(proxy, id, bp);
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -671,24 +684,21 @@ JavaScriptParent::instanceOf(JSObject *obj, const nsID *id, bool *bp)
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
JavaScriptParent::DOMInstanceOf(JSObject *obj, int prototypeID, int depth, bool *bp)
|
||||
JavaScriptParent::DOMInstanceOf(JSContext *cx, JSObject *proxy, int prototypeID, int depth, bool *bp)
|
||||
{
|
||||
return ParentOf(obj)->domInstanceOf(obj, prototypeID, depth, bp);
|
||||
FORWARD(domInstanceOf, (cx, proxy, prototypeID, depth, bp));
|
||||
}
|
||||
|
||||
bool
|
||||
JavaScriptParent::domInstanceOf(JSObject *obj, int prototypeID, int depth, bool *bp)
|
||||
JavaScriptParent::domInstanceOf(JSContext *cx, JSObject *obj, int prototypeID, int depth, bool *bp)
|
||||
{
|
||||
ObjectId objId = idOf(obj);
|
||||
|
||||
ReturnStatus status;
|
||||
if (!CallDOMInstanceOf(objId, prototypeID, depth, &status, bp))
|
||||
return false;
|
||||
return ipcfail(cx);
|
||||
|
||||
if (status.type() != ReturnStatus::TReturnSuccess)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return ok(cx, status);
|
||||
}
|
||||
|
||||
mozilla::ipc::IProtocol*
|
||||
|
@ -59,9 +59,12 @@ class JavaScriptParent
|
||||
bool objectClassIs(JSContext *cx, JS::HandleObject obj, js::ESClassValue classValue);
|
||||
const char* className(JSContext *cx, JS::HandleObject proxy);
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why);
|
||||
|
||||
void decref();
|
||||
void incref();
|
||||
void destroyFromContent();
|
||||
|
||||
bool active() { return !inactive_; }
|
||||
|
||||
void drop(JSObject *obj);
|
||||
|
||||
@ -74,8 +77,8 @@ class JavaScriptParent
|
||||
* Check that |obj| is a DOM wrapper whose prototype chain contains
|
||||
* |prototypeID| at depth |depth|.
|
||||
*/
|
||||
static bool DOMInstanceOf(JSObject *obj, int prototypeID, int depth, bool *bp);
|
||||
bool domInstanceOf(JSObject *obj, int prototypeID, int depth, bool *bp);
|
||||
static bool DOMInstanceOf(JSContext *cx, JSObject *obj, int prototypeID, int depth, bool *bp);
|
||||
bool domInstanceOf(JSContext *cx, JSObject *obj, int prototypeID, int depth, bool *bp);
|
||||
|
||||
mozilla::ipc::IProtocol*
|
||||
CloneProtocol(Channel* aChannel, ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
|
||||
|
@ -497,7 +497,7 @@ class NonLocalExitScope {
|
||||
StmtInfoBCE *stmt = bce->topStmt;
|
||||
while (1) {
|
||||
JS_ASSERT(stmt);
|
||||
if (stmt->isBlockScope) {
|
||||
if (stmt->isNestedScope) {
|
||||
openScopeIndex = stmt->blockScopeIndex;
|
||||
break;
|
||||
}
|
||||
@ -512,18 +512,12 @@ class NonLocalExitScope {
|
||||
bce->stackDepth = savedDepth;
|
||||
}
|
||||
|
||||
bool popScopeForNonLocalExit(StaticBlockObject &blockObj, uint32_t blockScopeIndex) {
|
||||
bool popScopeForNonLocalExit(uint32_t blockScopeIndex) {
|
||||
uint32_t scopeObjectIndex = bce->blockScopeList.findEnclosingScope(blockScopeIndex);
|
||||
uint32_t parent = openScopeIndex;
|
||||
|
||||
if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
|
||||
return false;
|
||||
if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
|
||||
return false;
|
||||
if (blockObj.needsClone()) {
|
||||
if (Emit1(cx, bce, JSOP_POPBLOCKSCOPE) < 0)
|
||||
return false;
|
||||
}
|
||||
openScopeIndex = bce->blockScopeList.length() - 1;
|
||||
return true;
|
||||
}
|
||||
@ -550,10 +544,11 @@ NonLocalExitScope::prepareForNonLocalJump(StmtInfoBCE *toStmt)
|
||||
break;
|
||||
|
||||
case STMT_WITH:
|
||||
/* There's a With object on the stack that we need to pop. */
|
||||
FLUSH_POPS();
|
||||
if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0)
|
||||
return false;
|
||||
JS_ASSERT(stmt->isNestedScope);
|
||||
if (!popScopeForNonLocalExit(stmt->blockScopeIndex))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case STMT_FOR_OF_LOOP:
|
||||
@ -578,9 +573,16 @@ NonLocalExitScope::prepareForNonLocalJump(StmtInfoBCE *toStmt)
|
||||
}
|
||||
|
||||
if (stmt->isBlockScope) {
|
||||
JS_ASSERT(stmt->isNestedScope);
|
||||
StaticBlockObject &blockObj = stmt->staticBlock();
|
||||
if (!popScopeForNonLocalExit(blockObj, stmt->blockScopeIndex))
|
||||
if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
|
||||
return false;
|
||||
if (!popScopeForNonLocalExit(stmt->blockScopeIndex))
|
||||
return false;
|
||||
if (blockObj.needsClone()) {
|
||||
if (Emit1(cx, bce, JSOP_POPBLOCKSCOPE) < 0)
|
||||
return false;
|
||||
}
|
||||
npops += blockObj.slotCount();
|
||||
}
|
||||
}
|
||||
@ -728,6 +730,55 @@ ComputeAliasedSlots(ExclusiveContext *cx, BytecodeEmitter *bce, Handle<StaticBlo
|
||||
static bool
|
||||
EmitInternedObjectOp(ExclusiveContext *cx, uint32_t index, JSOp op, BytecodeEmitter *bce);
|
||||
|
||||
static bool
|
||||
EnterNestedScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, ObjectBox *objbox,
|
||||
StmtType stmtType)
|
||||
{
|
||||
Rooted<NestedScopeObject *> scopeObj(cx, &objbox->object->as<NestedScopeObject>());
|
||||
uint32_t scopeObjectIndex = bce->objectList.add(objbox);
|
||||
|
||||
switch (stmtType) {
|
||||
case STMT_BLOCK: {
|
||||
Rooted<StaticBlockObject *> blockObj(cx, &scopeObj->as<StaticBlockObject>());
|
||||
|
||||
if (!ComputeAliasedSlots(cx, bce, blockObj))
|
||||
return false;
|
||||
|
||||
if (blockObj->needsClone()) {
|
||||
if (!EmitInternedObjectOp(cx, scopeObjectIndex, JSOP_PUSHBLOCKSCOPE, bce))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STMT_WITH:
|
||||
JS_ASSERT(scopeObj->is<StaticWithObject>());
|
||||
if (!EmitInternedObjectOp(cx, scopeObjectIndex, JSOP_ENTERWITH, bce))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSUME_UNREACHABLE();
|
||||
}
|
||||
|
||||
uint32_t parent = BlockScopeNote::NoBlockScopeIndex;
|
||||
if (bce->staticScope) {
|
||||
StmtInfoBCE *stmt = bce->topScopeStmt;
|
||||
for (; stmt->staticScope != bce->staticScope; stmt = stmt->down) {}
|
||||
parent = stmt->blockScopeIndex;
|
||||
}
|
||||
|
||||
stmt->blockScopeIndex = bce->blockScopeList.length();
|
||||
if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
|
||||
return false;
|
||||
|
||||
PushStatementBCE(bce, stmt, stmtType, bce->offset());
|
||||
scopeObj->initEnclosingNestedScope(EnclosingStaticScope(bce));
|
||||
FinishPushNestedScope(bce, stmt, *scopeObj);
|
||||
JS_ASSERT(stmt->isNestedScope);
|
||||
stmt->isBlockScope = (stmtType == STMT_BLOCK);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ~ Block Scopes ~
|
||||
//
|
||||
// A block scope is a region of a script with an additional set of named
|
||||
@ -771,50 +822,26 @@ EmitInternedObjectOp(ExclusiveContext *cx, uint32_t index, JSOp op, BytecodeEmit
|
||||
// for-in loops.
|
||||
//
|
||||
// Summary: Enter block scopes with EnterBlockScope. It will emit
|
||||
// PUSHBLOCKSCOPE if needed. Leave them with LeaveBlockScope, which will emit
|
||||
// PUSHBLOCKSCOPE if needed. Leave them with LeaveNestedScope, which will emit
|
||||
// DEBUGLEAVEBLOCK and may emit POPBLOCKSCOPE. Pass EnterBlockScope a fresh
|
||||
// StmtInfoBCE object, and pass that same object to the corresponding
|
||||
// LeaveBlockScope. Push locals before entering a scope, and pop them
|
||||
// LeaveNestedScope. Push locals before entering a scope, and pop them
|
||||
// afterwards. Brush your teeth, and clean behind your ears!
|
||||
//
|
||||
static bool
|
||||
EnterBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, ObjectBox *objbox,
|
||||
unsigned extraSlots)
|
||||
{
|
||||
uint32_t parent = BlockScopeNote::NoBlockScopeIndex;
|
||||
if (bce->staticScope) {
|
||||
StmtInfoBCE *stmt = bce->topScopeStmt;
|
||||
for (; stmt->staticScope != bce->staticScope; stmt = stmt->down) {}
|
||||
parent = stmt->blockScopeIndex;
|
||||
}
|
||||
|
||||
Rooted<StaticBlockObject *> blockObj(cx, &objbox->object->as<StaticBlockObject>());
|
||||
|
||||
uint32_t scopeObjectIndex = bce->objectList.add(objbox);
|
||||
|
||||
// FIXME: Once bug 962599 lands, we won't care about the stack depth, so we
|
||||
// won't have extraSlots and thus invocations of EnterBlockScope can become
|
||||
// invocations of EnterNestedScope.
|
||||
int depth = bce->stackDepth - (blockObj->slotCount() + extraSlots);
|
||||
JS_ASSERT(depth >= 0);
|
||||
blockObj->setStackDepth(depth);
|
||||
|
||||
if (!ComputeAliasedSlots(cx, bce, blockObj))
|
||||
return false;
|
||||
|
||||
if (blockObj->needsClone()) {
|
||||
if (!EmitInternedObjectOp(cx, scopeObjectIndex, JSOP_PUSHBLOCKSCOPE, bce))
|
||||
return false;
|
||||
}
|
||||
|
||||
stmt->blockScopeIndex = bce->blockScopeList.length();
|
||||
if (!bce->blockScopeList.append(scopeObjectIndex, bce->offset(), parent))
|
||||
return false;
|
||||
|
||||
PushStatementBCE(bce, stmt, STMT_BLOCK, bce->offset());
|
||||
blockObj->initEnclosingNestedScope(EnclosingStaticScope(bce));
|
||||
FinishPushBlockScope(bce, stmt, *blockObj);
|
||||
|
||||
JS_ASSERT(stmt->isBlockScope);
|
||||
|
||||
return true;
|
||||
return EnterNestedScope(cx, bce, stmt, objbox, STMT_BLOCK);
|
||||
}
|
||||
|
||||
// Patches |breaks| and |continues| unless the top statement info record
|
||||
@ -835,10 +862,11 @@ PopStatementBCE(ExclusiveContext *cx, BytecodeEmitter *bce)
|
||||
}
|
||||
|
||||
static bool
|
||||
LeaveBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce)
|
||||
LeaveNestedScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt)
|
||||
{
|
||||
StmtInfoBCE *stmt = bce->topStmt;
|
||||
JS_ASSERT(stmt->isBlockScope);
|
||||
JS_ASSERT(stmt == bce->topStmt);
|
||||
JS_ASSERT(stmt->isNestedScope);
|
||||
JS_ASSERT(stmt->isBlockScope == !(stmt->type == STMT_WITH));
|
||||
uint32_t blockScopeIndex = stmt->blockScopeIndex;
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -848,20 +876,18 @@ LeaveBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce)
|
||||
NestedScopeObject *staticScope = &blockObjBox->object->as<NestedScopeObject>();
|
||||
JS_ASSERT(stmt->staticScope == staticScope);
|
||||
JS_ASSERT(staticScope == bce->staticScope);
|
||||
JS_ASSERT_IF(!stmt->isBlockScope, staticScope->is<StaticWithObject>());
|
||||
#endif
|
||||
|
||||
JS_ASSERT(bce->staticScope->is<StaticBlockObject>());
|
||||
bool blockOnChain = bce->staticScope->as<StaticBlockObject>().needsClone();
|
||||
|
||||
if (!PopStatementBCE(cx, bce))
|
||||
return false;
|
||||
|
||||
if (Emit1(cx, bce, JSOP_DEBUGLEAVEBLOCK) < 0)
|
||||
if (Emit1(cx, bce, stmt->isBlockScope ? JSOP_DEBUGLEAVEBLOCK : JSOP_LEAVEWITH) < 0)
|
||||
return false;
|
||||
|
||||
bce->blockScopeList.recordEnd(blockScopeIndex, bce->offset());
|
||||
|
||||
if (blockOnChain) {
|
||||
if (stmt->isBlockScope && stmt->staticScope->as<StaticBlockObject>().needsClone()) {
|
||||
if (Emit1(cx, bce, JSOP_POPBLOCKSCOPE) < 0)
|
||||
return false;
|
||||
}
|
||||
@ -1120,8 +1146,7 @@ EmitAliasedVarOp(ExclusiveContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *
|
||||
while (!b->containsVarAtDepth(depth)) {
|
||||
if (b->needsClone())
|
||||
skippedScopes++;
|
||||
b = b->enclosingBlock();
|
||||
JS_ASSERT(b);
|
||||
b = &b->enclosingNestedScope()->as<StaticBlockObject>();
|
||||
}
|
||||
if (!AssignHops(bce, pn, skippedScopes, &sc))
|
||||
return false;
|
||||
@ -1736,6 +1761,7 @@ CheckSideEffects(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool
|
||||
CheckSideEffects(cx, bce, pn->pn_kid3, answer);
|
||||
|
||||
case PN_BINARY:
|
||||
case PN_BINARY_OBJ:
|
||||
if (pn->isAssignment()) {
|
||||
/*
|
||||
* Assignment is presumed to be useful, even if the next operation
|
||||
@ -1888,7 +1914,7 @@ BytecodeEmitter::needsImplicitThis()
|
||||
} else {
|
||||
JSObject *scope = sc->asGlobalSharedContext()->scopeChain();
|
||||
while (scope) {
|
||||
if (scope->is<WithObject>())
|
||||
if (scope->is<DynamicWithObject>())
|
||||
return true;
|
||||
scope = scope->enclosingScope();
|
||||
}
|
||||
@ -2719,7 +2745,7 @@ EmitSwitch(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
}
|
||||
|
||||
if (pn->pn_right->isKind(PNK_LEXICALSCOPE)) {
|
||||
if (!LeaveBlockScope(cx, bce))
|
||||
if (!LeaveNestedScope(cx, bce, &stmtInfo))
|
||||
return false;
|
||||
EMIT_UINT16_IMM_OP(JSOP_POPN, blockObj->slotCount());
|
||||
} else {
|
||||
@ -4269,7 +4295,7 @@ EmitLet(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pnLet)
|
||||
if (!EmitTree(cx, bce, letBody->pn_expr))
|
||||
return false;
|
||||
|
||||
if (!LeaveBlockScope(cx, bce))
|
||||
if (!LeaveNestedScope(cx, bce, &stmtInfo))
|
||||
return false;
|
||||
|
||||
JSOp leaveOp = letBody->getOp();
|
||||
@ -4305,7 +4331,7 @@ EmitLexicalScope(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
if (!EmitTree(cx, bce, pn->pn_expr))
|
||||
return false;
|
||||
|
||||
if (!LeaveBlockScope(cx, bce))
|
||||
if (!LeaveNestedScope(cx, bce, &stmtInfo))
|
||||
return false;
|
||||
|
||||
EMIT_UINT16_IMM_OP(JSOP_POPN, slots);
|
||||
@ -4319,15 +4345,13 @@ EmitWith(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
StmtInfoBCE stmtInfo(cx);
|
||||
if (!EmitTree(cx, bce, pn->pn_left))
|
||||
return false;
|
||||
PushStatementBCE(bce, &stmtInfo, STMT_WITH, bce->offset());
|
||||
if (Emit1(cx, bce, JSOP_ENTERWITH) < 0)
|
||||
if (!EnterNestedScope(cx, bce, &stmtInfo, pn->pn_binary_obj, STMT_WITH))
|
||||
return false;
|
||||
|
||||
if (!EmitTree(cx, bce, pn->pn_right))
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0)
|
||||
if (!LeaveNestedScope(cx, bce, &stmtInfo))
|
||||
return false;
|
||||
return PopStatementBCE(cx, bce);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -4474,7 +4498,7 @@ EmitForOf(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
|
||||
return false;
|
||||
|
||||
if (letDecl) {
|
||||
if (!LeaveBlockScope(cx, bce))
|
||||
if (!LeaveNestedScope(cx, bce, &letStmt))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4637,7 +4661,7 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
|
||||
return false;
|
||||
|
||||
if (letDecl) {
|
||||
if (!LeaveBlockScope(cx, bce))
|
||||
if (!LeaveNestedScope(cx, bce, &letStmt))
|
||||
return false;
|
||||
EMIT_UINT16_IMM_OP(JSOP_POPN, blockObjCount);
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ ContainsVarOrConst(ParseNode *pn)
|
||||
return pnt;
|
||||
return ContainsVarOrConst(pn->pn_kid3);
|
||||
case PN_BINARY:
|
||||
case PN_BINARY_OBJ:
|
||||
/*
|
||||
* Limit recursion if pn is a binary expression, which can't contain a
|
||||
* var statement.
|
||||
@ -327,6 +328,7 @@ Fold(ExclusiveContext *cx, ParseNode **pnp,
|
||||
break;
|
||||
|
||||
case PN_BINARY:
|
||||
case PN_BINARY_OBJ:
|
||||
if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND)) {
|
||||
// Propagate Condition context through logical connectives.
|
||||
SyntacticContext kidsc = SyntacticContext::Other;
|
||||
|
@ -385,8 +385,10 @@ class FullParseHandler
|
||||
return new_<UnaryNode>(PNK_RETURN, JSOP_RETURN, pos, expr);
|
||||
}
|
||||
|
||||
ParseNode *newWithStatement(uint32_t begin, ParseNode *expr, ParseNode *body) {
|
||||
return new_<BinaryNode>(PNK_WITH, JSOP_NOP, TokenPos(begin, body->pn_pos.end), expr, body);
|
||||
ParseNode *newWithStatement(uint32_t begin, ParseNode *expr, ParseNode *body,
|
||||
ObjectBox *staticWith) {
|
||||
return new_<BinaryObjNode>(PNK_WITH, JSOP_NOP, TokenPos(begin, body->pn_pos.end),
|
||||
expr, body, staticWith);
|
||||
}
|
||||
|
||||
ParseNode *newLabeledStatement(PropertyName *label, ParseNode *stmt, uint32_t begin) {
|
||||
|
@ -310,6 +310,7 @@ class NameResolver
|
||||
return false;
|
||||
break;
|
||||
case PN_BINARY:
|
||||
case PN_BINARY_OBJ:
|
||||
if (!resolve(cur->pn_left, prefix))
|
||||
return false;
|
||||
|
||||
|
@ -170,6 +170,7 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
|
||||
stack->pushUnlessNull(pn->pn_kid3);
|
||||
break;
|
||||
case PN_BINARY:
|
||||
case PN_BINARY_OBJ:
|
||||
if (pn->pn_left != pn->pn_right)
|
||||
stack->pushUnlessNull(pn->pn_left);
|
||||
stack->pushUnlessNull(pn->pn_right);
|
||||
@ -382,6 +383,15 @@ Parser<FullParseHandler>::cloneParseTree(ParseNode *opn)
|
||||
pn->pn_iflags = opn->pn_iflags;
|
||||
break;
|
||||
|
||||
case PN_BINARY_OBJ:
|
||||
NULLCHECK(pn->pn_left = cloneParseTree(opn->pn_left));
|
||||
if (opn->pn_right != opn->pn_left)
|
||||
NULLCHECK(pn->pn_right = cloneParseTree(opn->pn_right));
|
||||
else
|
||||
pn->pn_right = pn->pn_left;
|
||||
pn->pn_binary_obj = opn->pn_binary_obj;
|
||||
break;
|
||||
|
||||
case PN_UNARY:
|
||||
NULLCHECK(pn->pn_kid = cloneParseTree(opn->pn_kid));
|
||||
break;
|
||||
@ -548,6 +558,9 @@ ParseNode::dump(int indent)
|
||||
case PN_BINARY:
|
||||
((BinaryNode *) this)->dump(indent);
|
||||
break;
|
||||
case PN_BINARY_OBJ:
|
||||
((BinaryObjNode *) this)->dump(indent);
|
||||
break;
|
||||
case PN_TERNARY:
|
||||
((TernaryNode *) this)->dump(indent);
|
||||
break;
|
||||
@ -618,6 +631,18 @@ BinaryNode::dump(int indent)
|
||||
fprintf(stderr, ")");
|
||||
}
|
||||
|
||||
void
|
||||
BinaryObjNode::dump(int indent)
|
||||
{
|
||||
const char *name = parseNodeNames[getKind()];
|
||||
fprintf(stderr, "(%s ", name);
|
||||
indent += strlen(name) + 2;
|
||||
DumpParseTree(pn_left, indent);
|
||||
IndentNewLine(indent);
|
||||
DumpParseTree(pn_right, indent);
|
||||
fprintf(stderr, ")");
|
||||
}
|
||||
|
||||
void
|
||||
TernaryNode::dump(int indent)
|
||||
{
|
||||
|
@ -288,7 +288,7 @@ enum ParseNodeKind
|
||||
* pn_kid3: catch block statements
|
||||
* PNK_BREAK name pn_atom: label or null
|
||||
* PNK_CONTINUE name pn_atom: label or null
|
||||
* PNK_WITH binary pn_left: head expr, pn_right: body
|
||||
* PNK_WITH binary-obj pn_left: head expr; pn_right: body; pn_binary_obj: StaticWithObject
|
||||
* PNK_VAR, list pn_head: list of PNK_NAME or PNK_ASSIGN nodes
|
||||
* PNK_CONST each name node has either
|
||||
* pn_used: false
|
||||
@ -420,6 +420,7 @@ enum ParseNodeArity
|
||||
PN_NULLARY, /* 0 kids, only pn_atom/pn_dval/etc. */
|
||||
PN_UNARY, /* one kid, plus a couple of scalars */
|
||||
PN_BINARY, /* two kids, plus a couple of scalars */
|
||||
PN_BINARY_OBJ, /* two kids, plus an objbox */
|
||||
PN_TERNARY, /* three kids */
|
||||
PN_CODE, /* module or function definition node */
|
||||
PN_LIST, /* generic singly linked list */
|
||||
@ -516,7 +517,10 @@ class ParseNode
|
||||
struct { /* two kids if binary */
|
||||
ParseNode *left;
|
||||
ParseNode *right;
|
||||
unsigned iflags; /* JSITER_* flags for PNK_FOR node */
|
||||
union {
|
||||
unsigned iflags; /* JSITER_* flags for PNK_FOR node */
|
||||
ObjectBox *objbox; /* Only for PN_BINARY_OBJ */
|
||||
};
|
||||
} binary;
|
||||
struct { /* one kid if unary */
|
||||
ParseNode *kid;
|
||||
@ -570,6 +574,7 @@ class ParseNode
|
||||
#define pn_right pn_u.binary.right
|
||||
#define pn_pval pn_u.binary.pval
|
||||
#define pn_iflags pn_u.binary.iflags
|
||||
#define pn_binary_obj pn_u.binary.objbox
|
||||
#define pn_kid pn_u.unary.kid
|
||||
#define pn_prologue pn_u.unary.prologue
|
||||
#define pn_atom pn_u.name.atom
|
||||
@ -905,6 +910,30 @@ struct BinaryNode : public ParseNode
|
||||
#endif
|
||||
};
|
||||
|
||||
struct BinaryObjNode : public ParseNode
|
||||
{
|
||||
BinaryObjNode(ParseNodeKind kind, JSOp op, const TokenPos &pos, ParseNode *left, ParseNode *right,
|
||||
ObjectBox *objbox)
|
||||
: ParseNode(kind, op, PN_BINARY_OBJ, pos)
|
||||
{
|
||||
pn_left = left;
|
||||
pn_right = right;
|
||||
pn_binary_obj = objbox;
|
||||
}
|
||||
|
||||
static inline BinaryObjNode *create(ParseNodeKind kind, FullParseHandler *handler) {
|
||||
return (BinaryObjNode *) ParseNode::create(kind, PN_BINARY_OBJ, handler);
|
||||
}
|
||||
|
||||
static bool test(const ParseNode &node) {
|
||||
return node.isArity(PN_BINARY_OBJ);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void dump(int indent);
|
||||
#endif
|
||||
};
|
||||
|
||||
struct TernaryNode : public ParseNode
|
||||
{
|
||||
TernaryNode(ParseNodeKind kind, JSOp op, ParseNode *kid1, ParseNode *kid2, ParseNode *kid3)
|
||||
|
@ -535,7 +535,7 @@ FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunct
|
||||
//
|
||||
JSObject *scope = outerpc->sc->asGlobalSharedContext()->scopeChain();
|
||||
while (scope) {
|
||||
if (scope->is<WithObject>())
|
||||
if (scope->is<DynamicWithObject>())
|
||||
inWith = true;
|
||||
scope = scope->enclosingScope();
|
||||
}
|
||||
@ -2774,7 +2774,7 @@ static void
|
||||
PopStatementPC(TokenStream &ts, ParseContext<ParseHandler> *pc)
|
||||
{
|
||||
RootedNestedScopeObject scopeObj(ts.context(), pc->topStmt->staticScope);
|
||||
JS_ASSERT(!!scopeObj == (pc->topStmt->isBlockScope));
|
||||
JS_ASSERT(!!scopeObj == pc->topStmt->isNestedScope);
|
||||
|
||||
FinishPopStatement(pc);
|
||||
|
||||
@ -3196,7 +3196,8 @@ Parser<ParseHandler>::pushLexicalScope(HandleStaticBlockObject blockObj, StmtInf
|
||||
|
||||
PushStatementPC(pc, stmt, STMT_BLOCK);
|
||||
blockObj->initEnclosingNestedScopeFromParser(pc->staticScope);
|
||||
FinishPushBlockScope(pc, stmt, *blockObj.get());
|
||||
FinishPushNestedScope(pc, stmt, *blockObj.get());
|
||||
stmt->isBlockScope = true;
|
||||
|
||||
Node pn = handler.newLexicalScope(blockbox);
|
||||
if (!pn)
|
||||
@ -3593,7 +3594,7 @@ Parser<FullParseHandler>::letDeclaration()
|
||||
* lacks the SIF_SCOPE flag, it must be a try, catch, or finally
|
||||
* block.
|
||||
*/
|
||||
stmt->isBlockScope = true;
|
||||
stmt->isBlockScope = stmt->isNestedScope = true;
|
||||
stmt->downScope = pc->topScopeStmt;
|
||||
pc->topScopeStmt = stmt;
|
||||
|
||||
@ -4896,9 +4897,16 @@ Parser<FullParseHandler>::withStatement()
|
||||
|
||||
StmtInfoPC stmtInfo(context);
|
||||
PushStatementPC(pc, &stmtInfo, STMT_WITH);
|
||||
Rooted<StaticWithObject *> staticWith(context, StaticWithObject::create(context));
|
||||
if (!staticWith)
|
||||
return null();
|
||||
staticWith->initEnclosingNestedScopeFromParser(pc->staticScope);
|
||||
FinishPushNestedScope(pc, &stmtInfo, *staticWith);
|
||||
|
||||
Node innerBlock = statement();
|
||||
if (!innerBlock)
|
||||
return null();
|
||||
|
||||
PopStatementPC(tokenStream, pc);
|
||||
|
||||
pc->sc->setBindingsAccessedDynamically();
|
||||
@ -4914,7 +4922,10 @@ Parser<FullParseHandler>::withStatement()
|
||||
handler.deoptimizeUsesWithin(lexdep, TokenPos(begin, pos().begin));
|
||||
}
|
||||
|
||||
return handler.newWithStatement(begin, objectExpr, innerBlock);
|
||||
ObjectBox *staticWithBox = newObjectBox(staticWith);
|
||||
if (!staticWithBox)
|
||||
return null();
|
||||
return handler.newWithStatement(begin, objectExpr, innerBlock, staticWithBox);
|
||||
}
|
||||
|
||||
template <>
|
||||
@ -5860,6 +5871,7 @@ CompExprTransplanter::transplant(ParseNode *pn)
|
||||
break;
|
||||
|
||||
case PN_BINARY:
|
||||
case PN_BINARY_OBJ:
|
||||
if (!transplant(pn->pn_left))
|
||||
return false;
|
||||
|
||||
|
@ -389,22 +389,30 @@ enum StmtType {
|
||||
// work with both types.
|
||||
|
||||
struct StmtInfoBase {
|
||||
uint16_t type; /* statement type */
|
||||
// Statement type (StmtType).
|
||||
uint16_t type;
|
||||
|
||||
/*
|
||||
* True if type is STMT_BLOCK, STMT_TRY, STMT_SWITCH, or
|
||||
* STMT_FINALLY and the block contains at least one let-declaration.
|
||||
*/
|
||||
// True if type is STMT_BLOCK, STMT_TRY, STMT_SWITCH, or STMT_FINALLY and
|
||||
// the block contains at least one let-declaration, or if type is
|
||||
// STMT_CATCH.
|
||||
bool isBlockScope:1;
|
||||
|
||||
/* for (let ...) induced block scope */
|
||||
// True if isBlockScope or type == STMT_WITH.
|
||||
bool isNestedScope:1;
|
||||
|
||||
// for (let ...) induced block scope
|
||||
bool isForLetBlock:1;
|
||||
|
||||
RootedAtom label; /* name of LABEL */
|
||||
Rooted<NestedScopeObject *> staticScope; /* scope object */
|
||||
// Block label.
|
||||
RootedAtom label;
|
||||
|
||||
// Compile-time scope chain node for this scope. Only set if
|
||||
// isNestedScope.
|
||||
Rooted<NestedScopeObject *> staticScope;
|
||||
|
||||
StmtInfoBase(ExclusiveContext *cx)
|
||||
: isBlockScope(false), isForLetBlock(false), label(cx), staticScope(cx)
|
||||
: isBlockScope(false), isNestedScope(false), isForLetBlock(false),
|
||||
label(cx), staticScope(cx)
|
||||
{}
|
||||
|
||||
bool maybeScope() const {
|
||||
@ -412,12 +420,12 @@ struct StmtInfoBase {
|
||||
}
|
||||
|
||||
bool linksScope() const {
|
||||
return (STMT_WITH <= type && type <= STMT_CATCH) || isBlockScope;
|
||||
return isNestedScope;
|
||||
}
|
||||
|
||||
StaticBlockObject& staticBlock() const {
|
||||
JS_ASSERT(isNestedScope);
|
||||
JS_ASSERT(isBlockScope);
|
||||
JS_ASSERT(staticScope);
|
||||
return staticScope->as<StaticBlockObject>();
|
||||
}
|
||||
|
||||
@ -437,6 +445,7 @@ PushStatement(ContextT *ct, typename ContextT::StmtInfo *stmt, StmtType type)
|
||||
{
|
||||
stmt->type = type;
|
||||
stmt->isBlockScope = false;
|
||||
stmt->isNestedScope = false;
|
||||
stmt->isForLetBlock = false;
|
||||
stmt->label = nullptr;
|
||||
stmt->staticScope = nullptr;
|
||||
@ -452,9 +461,9 @@ PushStatement(ContextT *ct, typename ContextT::StmtInfo *stmt, StmtType type)
|
||||
|
||||
template <class ContextT>
|
||||
void
|
||||
FinishPushBlockScope(ContextT *ct, typename ContextT::StmtInfo *stmt, NestedScopeObject &staticScope)
|
||||
FinishPushNestedScope(ContextT *ct, typename ContextT::StmtInfo *stmt, NestedScopeObject &staticScope)
|
||||
{
|
||||
stmt->isBlockScope = true;
|
||||
stmt->isNestedScope = true;
|
||||
stmt->downScope = ct->topScopeStmt;
|
||||
ct->topScopeStmt = stmt;
|
||||
ct->staticScope = &staticScope;
|
||||
@ -472,8 +481,10 @@ FinishPopStatement(ContextT *ct)
|
||||
ct->topStmt = stmt->down;
|
||||
if (stmt->linksScope()) {
|
||||
ct->topScopeStmt = stmt->downScope;
|
||||
if (stmt->isBlockScope)
|
||||
ct->staticScope = stmt->staticBlock().enclosingBlock();
|
||||
if (stmt->isNestedScope) {
|
||||
JS_ASSERT(stmt->staticScope);
|
||||
ct->staticScope = stmt->staticScope->enclosingNestedScope();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,6 +95,8 @@ def main(argv):
|
||||
op.add_option('--localLib', dest='local_lib', action='store',
|
||||
type='string',
|
||||
help='The location of libraries to push -- preferably stripped')
|
||||
op.add_option('--repeat', type=int, default=1,
|
||||
help='Repeat tests the given number of times.')
|
||||
|
||||
options, args = op.parse_args(argv)
|
||||
if len(args) < 1:
|
||||
|
@ -98,3 +98,4 @@ assertEq(asmLink(asmCompileCached("g","ffis", USE_ASM + "var x=ffis.x|0; functio
|
||||
var i32 = new Int32Array(4096);
|
||||
i32[4] = 42;
|
||||
assertEq(asmLink(asmCompileCached("g","ffis","buf", USE_ASM + "var i32=new g.Int32Array(buf); function f(i) { i=i|0; return i32[i>>2]|0 } return f"), this, null, i32.buffer)(4*4), 42);
|
||||
assertEq(asmLink(asmCompileCached('glob', USE_ASM + 'var x=glob.Math.PI; function f() { return +x } return f'), this)(), Math.PI);
|
||||
|
@ -86,3 +86,17 @@ assertAsmTypeFail('glob', USE_ASM + 'var im=glob.Math.imul; function f(i) { i=i|
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var im=glob.Math.imul; function f(i) { i=i|0; i = im(i,i); } return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; +abs(i|0); } return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d; abs(d)|0; } return f');
|
||||
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var tau=glob.Math.TAU; function f() {} return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var pi=glob.Math.PI; function f() { return pi | 0 } return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var pi=glob.Math.PI; function f() { return +pi() } return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var pi=glob.Math.PI; function f() { pi = +3; } return f');
|
||||
assertAsmLinkAlwaysFail(asmCompile('glob', USE_ASM + 'var pi=glob.Math.PI; function f() {} return f'), {});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var pi=glob.Math.PI; function f() {} return f'), {Math: {}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var pi=glob.Math.PI; function f() {} return f'), {Math: {PI: Math.cos}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var pi=glob.Math.PI; function f() {} return f'), {Math: {PI: Math.SQRT2}});
|
||||
|
||||
for (var c of ['E', 'LN10', 'LN2', 'LOG2E', 'LOG10E', 'PI', 'SQRT1_2', 'SQRT2']) {
|
||||
var f = asmLink(asmCompile('glob', USE_ASM + 'var x=glob.Math.' + c +'; function f() { return +x } return f'), this);
|
||||
assertEq(f(), eval('Math.' + c));
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
# include "vtune/VTuneWrapper.h"
|
||||
#endif
|
||||
|
||||
#include "jsmath.h"
|
||||
#include "jsprf.h"
|
||||
#include "jsworkers.h"
|
||||
#include "prmjtime.h"
|
||||
@ -936,7 +937,7 @@ class MOZ_STACK_CLASS ModuleCompiler
|
||||
FuncPtrTable,
|
||||
FFI,
|
||||
ArrayView,
|
||||
MathBuiltin
|
||||
MathBuiltinFunction
|
||||
};
|
||||
|
||||
private:
|
||||
@ -951,7 +952,7 @@ class MOZ_STACK_CLASS ModuleCompiler
|
||||
uint32_t funcPtrTableIndex_;
|
||||
uint32_t ffiIndex_;
|
||||
ArrayBufferView::ViewType viewType_;
|
||||
AsmJSMathBuiltin mathBuiltin_;
|
||||
AsmJSMathBuiltinFunction mathBuiltinFunc_;
|
||||
} u;
|
||||
|
||||
friend class ModuleCompiler;
|
||||
@ -994,9 +995,9 @@ class MOZ_STACK_CLASS ModuleCompiler
|
||||
JS_ASSERT(which_ == ArrayView);
|
||||
return u.viewType_;
|
||||
}
|
||||
AsmJSMathBuiltin mathBuiltin() const {
|
||||
JS_ASSERT(which_ == MathBuiltin);
|
||||
return u.mathBuiltin_;
|
||||
AsmJSMathBuiltinFunction mathBuiltinFunction() const {
|
||||
JS_ASSERT(which_ == MathBuiltinFunction);
|
||||
return u.mathBuiltinFunc_;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1063,6 +1064,25 @@ class MOZ_STACK_CLASS ModuleCompiler
|
||||
|
||||
typedef HashMap<ExitDescriptor, unsigned, ExitDescriptor> ExitMap;
|
||||
|
||||
struct MathBuiltin
|
||||
{
|
||||
enum Kind { Function, Constant };
|
||||
Kind kind;
|
||||
|
||||
union {
|
||||
double cst;
|
||||
AsmJSMathBuiltinFunction func;
|
||||
} u;
|
||||
|
||||
MathBuiltin() : kind(Kind(-1)) {}
|
||||
MathBuiltin(double cst) : kind(Constant) {
|
||||
u.cst = cst;
|
||||
}
|
||||
MathBuiltin(AsmJSMathBuiltinFunction func) : kind(Function) {
|
||||
u.func = func;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
struct SlowFunction
|
||||
{
|
||||
@ -1072,7 +1092,7 @@ class MOZ_STACK_CLASS ModuleCompiler
|
||||
unsigned column;
|
||||
};
|
||||
|
||||
typedef HashMap<PropertyName*, AsmJSMathBuiltin> MathNameMap;
|
||||
typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
|
||||
typedef HashMap<PropertyName*, Global*> GlobalMap;
|
||||
typedef js::Vector<Func*> FuncVector;
|
||||
typedef js::Vector<AsmJSGlobalAccess> GlobalAccessVector;
|
||||
@ -1106,10 +1126,18 @@ class MOZ_STACK_CLASS ModuleCompiler
|
||||
|
||||
DebugOnly<bool> finishedFunctionBodies_;
|
||||
|
||||
bool addStandardLibraryMathName(const char *name, AsmJSMathBuiltin builtin) {
|
||||
bool addStandardLibraryMathName(const char *name, AsmJSMathBuiltinFunction func) {
|
||||
JSAtom *atom = Atomize(cx_, name, strlen(name));
|
||||
if (!atom)
|
||||
return false;
|
||||
MathBuiltin builtin(func);
|
||||
return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
|
||||
}
|
||||
bool addStandardLibraryMathName(const char *name, double cst) {
|
||||
JSAtom *atom = Atomize(cx_, name, strlen(name));
|
||||
if (!atom)
|
||||
return false;
|
||||
MathBuiltin builtin(cst);
|
||||
return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
|
||||
}
|
||||
|
||||
@ -1175,7 +1203,15 @@ class MOZ_STACK_CLASS ModuleCompiler
|
||||
!addStandardLibraryMathName("abs", AsmJSMathBuiltin_abs) ||
|
||||
!addStandardLibraryMathName("atan2", AsmJSMathBuiltin_atan2) ||
|
||||
!addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul) ||
|
||||
!addStandardLibraryMathName("fround", AsmJSMathBuiltin_fround))
|
||||
!addStandardLibraryMathName("fround", AsmJSMathBuiltin_fround) ||
|
||||
!addStandardLibraryMathName("E", M_E) ||
|
||||
!addStandardLibraryMathName("LN10", M_LN10) ||
|
||||
!addStandardLibraryMathName("LN2", M_LN2) ||
|
||||
!addStandardLibraryMathName("LOG2E", M_LOG2E) ||
|
||||
!addStandardLibraryMathName("LOG10E", M_LOG10E) ||
|
||||
!addStandardLibraryMathName("PI", M_PI) ||
|
||||
!addStandardLibraryMathName("SQRT1_2", M_SQRT1_2) ||
|
||||
!addStandardLibraryMathName("SQRT2", M_SQRT2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -1289,7 +1325,7 @@ class MOZ_STACK_CLASS ModuleCompiler
|
||||
FuncPtrTable &funcPtrTable(unsigned i) {
|
||||
return funcPtrTables_[i];
|
||||
}
|
||||
bool lookupStandardLibraryMathName(PropertyName *name, AsmJSMathBuiltin *mathBuiltin) const {
|
||||
bool lookupStandardLibraryMathName(PropertyName *name, MathBuiltin *mathBuiltin) const {
|
||||
if (MathNameMap::Ptr p = standardLibraryMathNames_.lookup(name)) {
|
||||
*mathBuiltin = p->value();
|
||||
return true;
|
||||
@ -1390,18 +1426,17 @@ class MOZ_STACK_CLASS ModuleCompiler
|
||||
global->u.viewType_ = vt;
|
||||
return globals_.putNew(varName, global);
|
||||
}
|
||||
bool addMathBuiltin(PropertyName *varName, AsmJSMathBuiltin mathBuiltin, PropertyName *fieldName) {
|
||||
if (!module_->addMathBuiltin(mathBuiltin, fieldName))
|
||||
bool addMathBuiltinFunction(PropertyName *varName, AsmJSMathBuiltinFunction func, PropertyName *fieldName) {
|
||||
if (!module_->addMathBuiltinFunction(func, fieldName))
|
||||
return false;
|
||||
Global *global = moduleLifo_.new_<Global>(Global::MathBuiltin);
|
||||
Global *global = moduleLifo_.new_<Global>(Global::MathBuiltinFunction);
|
||||
if (!global)
|
||||
return false;
|
||||
global->u.mathBuiltin_ = mathBuiltin;
|
||||
global->u.mathBuiltinFunc_ = func;
|
||||
return globals_.putNew(varName, global);
|
||||
}
|
||||
bool addGlobalConstant(PropertyName *varName, double constant, PropertyName *fieldName) {
|
||||
if (!module_->addGlobalConstant(constant, fieldName))
|
||||
return false;
|
||||
private:
|
||||
bool addGlobalDoubleConstant(PropertyName *varName, double constant) {
|
||||
Global *global = moduleLifo_.new_<Global>(Global::ConstantLiteral);
|
||||
if (!global)
|
||||
return false;
|
||||
@ -1409,6 +1444,17 @@ class MOZ_STACK_CLASS ModuleCompiler
|
||||
global->u.varOrConst.type_ = VarType::Double;
|
||||
return globals_.putNew(varName, global);
|
||||
}
|
||||
public:
|
||||
bool addMathBuiltinConstant(PropertyName *varName, double constant, PropertyName *fieldName) {
|
||||
if (!module_->addMathBuiltinConstant(constant, fieldName))
|
||||
return false;
|
||||
return addGlobalDoubleConstant(varName, constant);
|
||||
}
|
||||
bool addGlobalConstant(PropertyName *varName, double constant, PropertyName *fieldName) {
|
||||
if (!module_->addGlobalConstant(constant, fieldName))
|
||||
return false;
|
||||
return addGlobalDoubleConstant(varName, constant);
|
||||
}
|
||||
bool addExportedFunction(const Func *func, PropertyName *maybeFieldName) {
|
||||
AsmJSModule::ArgCoercionVector argCoercions;
|
||||
const VarTypeVector &args = func->sig().args();
|
||||
@ -1774,8 +1820,8 @@ IsFloatCoercion(ModuleCompiler &m, ParseNode *pn, ParseNode **coercedExpr)
|
||||
|
||||
const ModuleCompiler::Global *global = m.lookupGlobal(callee->name());
|
||||
if (!global ||
|
||||
global->which() != ModuleCompiler::Global::MathBuiltin ||
|
||||
global->mathBuiltin() != AsmJSMathBuiltin_fround)
|
||||
global->which() != ModuleCompiler::Global::MathBuiltinFunction ||
|
||||
global->mathBuiltinFunction() != AsmJSMathBuiltin_fround)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -3104,11 +3150,19 @@ CheckGlobalDotImport(ModuleCompiler &m, PropertyName *varName, ParseNode *initNo
|
||||
if (!IsUseOfName(global, m.module().globalArgumentName()) || math != m.cx()->names().Math)
|
||||
return m.fail(base, "expecting global.Math");
|
||||
|
||||
AsmJSMathBuiltin mathBuiltin;
|
||||
ModuleCompiler::MathBuiltin mathBuiltin;
|
||||
if (!m.lookupStandardLibraryMathName(field, &mathBuiltin))
|
||||
return m.failName(initNode, "'%s' is not a standard Math builtin", field);
|
||||
|
||||
return m.addMathBuiltin(varName, mathBuiltin, field);
|
||||
switch (mathBuiltin.kind) {
|
||||
case ModuleCompiler::MathBuiltin::Function:
|
||||
return m.addMathBuiltinFunction(varName, mathBuiltin.u.func, field);
|
||||
case ModuleCompiler::MathBuiltin::Constant:
|
||||
return m.addMathBuiltinConstant(varName, mathBuiltin.u.cst, field);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
MOZ_ASSUME_UNREACHABLE("unexpected or uninitialized math builtin type");
|
||||
}
|
||||
|
||||
if (IsUseOfName(base, m.module().globalArgumentName())) {
|
||||
@ -3366,7 +3420,7 @@ CheckVarRef(FunctionCompiler &f, ParseNode *varRef, MDefinition **def, Type *typ
|
||||
break;
|
||||
case ModuleCompiler::Global::Function:
|
||||
case ModuleCompiler::Global::FFI:
|
||||
case ModuleCompiler::Global::MathBuiltin:
|
||||
case ModuleCompiler::Global::MathBuiltinFunction:
|
||||
case ModuleCompiler::Global::FuncPtrTable:
|
||||
case ModuleCompiler::Global::ArrayView:
|
||||
return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name);
|
||||
@ -4021,12 +4075,12 @@ CheckIsMaybeFloat(FunctionCompiler &f, ParseNode *argNode, Type type)
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltin mathBuiltin,
|
||||
CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltinFunction func,
|
||||
RetType retType, MDefinition **def, Type *type)
|
||||
{
|
||||
unsigned arity = 0;
|
||||
AsmJSImmKind doubleCallee, floatCallee;
|
||||
switch (mathBuiltin) {
|
||||
switch (func) {
|
||||
case AsmJSMathBuiltin_imul: return CheckMathIMul(f, callNode, retType, def, type);
|
||||
case AsmJSMathBuiltin_abs: return CheckMathAbs(f, callNode, retType, def, type);
|
||||
case AsmJSMathBuiltin_sqrt: return CheckMathSqrt(f, callNode, retType, def, type);
|
||||
@ -4043,7 +4097,7 @@ CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltin
|
||||
case AsmJSMathBuiltin_log: arity = 1; doubleCallee = AsmJSImm_LogD; floatCallee = AsmJSImm_LogF; break;
|
||||
case AsmJSMathBuiltin_pow: arity = 2; doubleCallee = AsmJSImm_PowD; floatCallee = AsmJSImm_Invalid; break;
|
||||
case AsmJSMathBuiltin_atan2: arity = 2; doubleCallee = AsmJSImm_ATan2D; floatCallee = AsmJSImm_Invalid; break;
|
||||
default: MOZ_ASSUME_UNREACHABLE("unexpected mathBuiltin");
|
||||
default: MOZ_ASSUME_UNREACHABLE("unexpected mathBuiltin function");
|
||||
}
|
||||
|
||||
if (retType == RetType::Float && floatCallee == AsmJSImm_Invalid)
|
||||
@ -4088,8 +4142,8 @@ CheckCall(FunctionCompiler &f, ParseNode *call, RetType retType, MDefinition **d
|
||||
switch (global->which()) {
|
||||
case ModuleCompiler::Global::FFI:
|
||||
return CheckFFICall(f, call, global->ffiIndex(), retType, def, type);
|
||||
case ModuleCompiler::Global::MathBuiltin:
|
||||
return CheckMathBuiltinCall(f, call, global->mathBuiltin(), retType, def, type);
|
||||
case ModuleCompiler::Global::MathBuiltinFunction:
|
||||
return CheckMathBuiltinCall(f, call, global->mathBuiltinFunction(), retType, def, type);
|
||||
case ModuleCompiler::Global::ConstantLiteral:
|
||||
case ModuleCompiler::Global::ConstantImport:
|
||||
case ModuleCompiler::Global::Variable:
|
||||
|
@ -143,7 +143,7 @@ ValidateArrayView(JSContext *cx, AsmJSModule::Global &global, HandleValue global
|
||||
}
|
||||
|
||||
static bool
|
||||
ValidateMathBuiltin(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
|
||||
ValidateMathBuiltinFunction(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
|
||||
{
|
||||
RootedValue v(cx);
|
||||
if (!GetDataProperty(cx, globalVal, cx->names().Math, &v))
|
||||
@ -153,7 +153,7 @@ ValidateMathBuiltin(JSContext *cx, AsmJSModule::Global &global, HandleValue glob
|
||||
return false;
|
||||
|
||||
Native native = nullptr;
|
||||
switch (global.mathBuiltin()) {
|
||||
switch (global.mathBuiltinFunction()) {
|
||||
case AsmJSMathBuiltin_sin: native = math_sin; break;
|
||||
case AsmJSMathBuiltin_cos: native = math_cos; break;
|
||||
case AsmJSMathBuiltin_tan: native = math_tan; break;
|
||||
@ -173,21 +173,26 @@ ValidateMathBuiltin(JSContext *cx, AsmJSModule::Global &global, HandleValue glob
|
||||
}
|
||||
|
||||
if (!IsNativeFunction(v, native))
|
||||
return LinkFail(cx, "bad Math.* builtin");
|
||||
return LinkFail(cx, "bad Math.* builtin function");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ValidateGlobalConstant(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
|
||||
ValidateConstant(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
|
||||
{
|
||||
RootedPropertyName field(cx, global.constantName());
|
||||
RootedValue v(cx);
|
||||
if (!GetDataProperty(cx, globalVal, field, &v))
|
||||
return false;
|
||||
RootedValue v(cx, globalVal);
|
||||
|
||||
if (global.constantKind() == AsmJSModule::Global::MathConstant) {
|
||||
if (!GetDataProperty(cx, v, cx->names().Math, &v))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetDataProperty(cx, v, field, &v))
|
||||
return false;
|
||||
if (!v.isNumber())
|
||||
return LinkFail(cx, "global constant value needs to be a number");
|
||||
return LinkFail(cx, "math / global constant value needs to be a number");
|
||||
|
||||
// NaN != NaN
|
||||
if (IsNaN(global.constantValue())) {
|
||||
@ -269,12 +274,12 @@ DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
|
||||
if (!ValidateArrayView(cx, global, globalVal, bufferVal))
|
||||
return false;
|
||||
break;
|
||||
case AsmJSModule::Global::MathBuiltin:
|
||||
if (!ValidateMathBuiltin(cx, global, globalVal))
|
||||
case AsmJSModule::Global::MathBuiltinFunction:
|
||||
if (!ValidateMathBuiltinFunction(cx, global, globalVal))
|
||||
return false;
|
||||
break;
|
||||
case AsmJSModule::Global::Constant:
|
||||
if (!ValidateGlobalConstant(cx, global, globalVal))
|
||||
if (!ValidateConstant(cx, global, globalVal))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ enum AsmJSCoercion
|
||||
};
|
||||
|
||||
// The asm.js spec recognizes this set of builtin Math functions.
|
||||
enum AsmJSMathBuiltin
|
||||
enum AsmJSMathBuiltinFunction
|
||||
{
|
||||
AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan,
|
||||
AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan,
|
||||
@ -97,8 +97,9 @@ class AsmJSModule
|
||||
class Global
|
||||
{
|
||||
public:
|
||||
enum Which { Variable, FFI, ArrayView, MathBuiltin, Constant };
|
||||
enum Which { Variable, FFI, ArrayView, MathBuiltinFunction, Constant };
|
||||
enum VarInitKind { InitConstant, InitImport };
|
||||
enum ConstantKind { GlobalConstant, MathConstant };
|
||||
|
||||
private:
|
||||
struct Pod {
|
||||
@ -114,8 +115,11 @@ class AsmJSModule
|
||||
} var;
|
||||
uint32_t ffiIndex_;
|
||||
ArrayBufferView::ViewType viewType_;
|
||||
AsmJSMathBuiltin mathBuiltin_;
|
||||
double constantValue_;
|
||||
AsmJSMathBuiltinFunction mathBuiltinFunc_;
|
||||
struct {
|
||||
ConstantKind kind_;
|
||||
double value_;
|
||||
} constant;
|
||||
} u;
|
||||
} pod;
|
||||
PropertyName *name_;
|
||||
@ -179,20 +183,24 @@ class AsmJSModule
|
||||
return pod.u.viewType_;
|
||||
}
|
||||
PropertyName *mathName() const {
|
||||
JS_ASSERT(pod.which_ == MathBuiltin);
|
||||
JS_ASSERT(pod.which_ == MathBuiltinFunction);
|
||||
return name_;
|
||||
}
|
||||
AsmJSMathBuiltin mathBuiltin() const {
|
||||
JS_ASSERT(pod.which_ == MathBuiltin);
|
||||
return pod.u.mathBuiltin_;
|
||||
AsmJSMathBuiltinFunction mathBuiltinFunction() const {
|
||||
JS_ASSERT(pod.which_ == MathBuiltinFunction);
|
||||
return pod.u.mathBuiltinFunc_;
|
||||
}
|
||||
PropertyName *constantName() const {
|
||||
JS_ASSERT(pod.which_ == Constant);
|
||||
return name_;
|
||||
}
|
||||
ConstantKind constantKind() const {
|
||||
JS_ASSERT(pod.which_ == Constant);
|
||||
return pod.u.constant.kind_;
|
||||
}
|
||||
double constantValue() const {
|
||||
JS_ASSERT(pod.which_ == Constant);
|
||||
return pod.u.constantValue_;
|
||||
return pod.u.constant.value_;
|
||||
}
|
||||
|
||||
size_t serializedSize() const;
|
||||
@ -488,14 +496,21 @@ class AsmJSModule
|
||||
g.pod.u.viewType_ = vt;
|
||||
return globals_.append(g);
|
||||
}
|
||||
bool addMathBuiltin(AsmJSMathBuiltin mathBuiltin, PropertyName *field) {
|
||||
Global g(Global::MathBuiltin, field);
|
||||
g.pod.u.mathBuiltin_ = mathBuiltin;
|
||||
bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName *field) {
|
||||
Global g(Global::MathBuiltinFunction, field);
|
||||
g.pod.u.mathBuiltinFunc_ = func;
|
||||
return globals_.append(g);
|
||||
}
|
||||
bool addMathBuiltinConstant(double value, PropertyName *field) {
|
||||
Global g(Global::Constant, field);
|
||||
g.pod.u.constant.value_ = value;
|
||||
g.pod.u.constant.kind_ = Global::MathConstant;
|
||||
return globals_.append(g);
|
||||
}
|
||||
bool addGlobalConstant(double value, PropertyName *name) {
|
||||
Global g(Global::Constant, name);
|
||||
g.pod.u.constantValue_ = value;
|
||||
g.pod.u.constant.value_ = value;
|
||||
g.pod.u.constant.kind_ = Global::GlobalConstant;
|
||||
return globals_.append(g);
|
||||
}
|
||||
bool addFuncPtrTable(unsigned numElems, uint32_t *globalDataOffset) {
|
||||
|
@ -2650,6 +2650,43 @@ BaselineCompiler::emit_JSOP_DEBUGLEAVEBLOCK()
|
||||
return callVM(DebugLeaveBlockInfo);
|
||||
}
|
||||
|
||||
typedef bool (*EnterWithFn)(JSContext *, BaselineFrame *, HandleValue, Handle<StaticWithObject *>);
|
||||
static const VMFunction EnterWithInfo = FunctionInfo<EnterWithFn>(jit::EnterWith);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_ENTERWITH()
|
||||
{
|
||||
StaticWithObject &withObj = script->getObject(pc)->as<StaticWithObject>();
|
||||
|
||||
// Pop "with" object to R0.
|
||||
frame.popRegsAndSync(1);
|
||||
|
||||
// Call a stub to push the object onto the scope chain.
|
||||
prepareVMCall();
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
|
||||
|
||||
pushArg(ImmGCPtr(&withObj));
|
||||
pushArg(R0);
|
||||
pushArg(R1.scratchReg());
|
||||
|
||||
return callVM(EnterWithInfo);
|
||||
}
|
||||
|
||||
typedef bool (*LeaveWithFn)(JSContext *, BaselineFrame *);
|
||||
static const VMFunction LeaveWithInfo = FunctionInfo<LeaveWithFn>(jit::LeaveWith);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_LEAVEWITH()
|
||||
{
|
||||
// Call a stub to pop the with object from the scope chain.
|
||||
prepareVMCall();
|
||||
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
pushArg(R0.scratchReg());
|
||||
|
||||
return callVM(LeaveWithInfo);
|
||||
}
|
||||
|
||||
typedef bool (*GetAndClearExceptionFn)(JSContext *, MutableHandleValue);
|
||||
static const VMFunction GetAndClearExceptionInfo =
|
||||
FunctionInfo<GetAndClearExceptionFn>(GetAndClearException);
|
||||
|
@ -27,6 +27,8 @@ namespace jit {
|
||||
_(JSOP_POP) \
|
||||
_(JSOP_POPN) \
|
||||
_(JSOP_POPNV) \
|
||||
_(JSOP_ENTERWITH) \
|
||||
_(JSOP_LEAVEWITH) \
|
||||
_(JSOP_DUP) \
|
||||
_(JSOP_DUP2) \
|
||||
_(JSOP_SWAP) \
|
||||
|
@ -33,6 +33,16 @@ BaselineFrame::popOffScopeChain()
|
||||
scopeChain_ = &scopeChain_->as<ScopeObject>().enclosingScope();
|
||||
}
|
||||
|
||||
inline void
|
||||
BaselineFrame::popWith(JSContext *cx)
|
||||
{
|
||||
if (MOZ_UNLIKELY(cx->compartment()->debugMode()))
|
||||
DebugScopes::onPopWith(this);
|
||||
|
||||
JS_ASSERT(scopeChain()->is<DynamicWithObject>());
|
||||
popOffScopeChain();
|
||||
}
|
||||
|
||||
inline bool
|
||||
BaselineFrame::pushBlock(JSContext *cx, Handle<StaticBlockObject *> block)
|
||||
{
|
||||
|
@ -110,6 +110,8 @@ class BaselineFrame
|
||||
inline void pushOnScopeChain(ScopeObject &scope);
|
||||
inline void popOffScopeChain();
|
||||
|
||||
inline void popWith(JSContext *cx);
|
||||
|
||||
CalleeToken calleeToken() const {
|
||||
uint8_t *pointer = (uint8_t *)this + Size() + offsetOfCalleeToken();
|
||||
return *(CalleeToken *)pointer;
|
||||
|
@ -5663,7 +5663,7 @@ TryAttachScopeNameStub(JSContext *cx, HandleScript script, ICGetName_Fallback *s
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!scopeChain->is<ScopeObject>() || scopeChain->is<WithObject>())
|
||||
if (!scopeChain->is<ScopeObject>() || scopeChain->is<DynamicWithObject>())
|
||||
return true;
|
||||
|
||||
// Check for an 'own' property on the scope. There is no need to
|
||||
|
@ -347,6 +347,9 @@ JitRuntime::handleAccessViolation(JSRuntime *rt, void *faultingAddress)
|
||||
// to SEGV while still inside the signal handler, and the process will terminate.
|
||||
JSRuntime::AutoLockForOperationCallback lock(rt);
|
||||
|
||||
// Ion code in the runtime faulted after it was made inaccessible. Reset
|
||||
// the code privileges and patch all loop backedges to perform an interrupt
|
||||
// check instead.
|
||||
ensureIonCodeAccessible(rt);
|
||||
return true;
|
||||
}
|
||||
@ -362,18 +365,14 @@ JitRuntime::ensureIonCodeAccessible(JSRuntime *rt)
|
||||
JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
|
||||
#endif
|
||||
|
||||
if (!ionCodeProtected_)
|
||||
return;
|
||||
|
||||
// Ion code in the runtime faulted after it was made inaccessible. Reset
|
||||
// the code privileges and patch all loop backedges to perform an interrupt
|
||||
// check instead.
|
||||
ionAlloc_->toggleAllCodeAsAccessible(true);
|
||||
ionCodeProtected_ = false;
|
||||
if (ionCodeProtected_) {
|
||||
ionAlloc_->toggleAllCodeAsAccessible(true);
|
||||
ionCodeProtected_ = false;
|
||||
}
|
||||
|
||||
if (rt->interrupt) {
|
||||
// The interrupt handler needs to be invoked by this thread, but we
|
||||
// are inside a signal handler and have no idea what is above us on the
|
||||
// The interrupt handler needs to be invoked by this thread, but we may
|
||||
// be inside a signal handler and have no idea what is above us on the
|
||||
// stack (probably we are executing Ion code at an arbitrary point, but
|
||||
// we could be elsewhere, say repatching a jump for an IonCache).
|
||||
// Patch all backedges in the runtime so they will invoke the interrupt
|
||||
|
@ -512,7 +512,7 @@ HandleExceptionBaseline(JSContext *cx, const IonFrameIterator &frame, ResumeFrom
|
||||
|
||||
// Unwind scope chain (pop block objects).
|
||||
if (cx->isExceptionPending())
|
||||
UnwindScope(cx, si, tn->stackDepth);
|
||||
UnwindScope(cx, si, script->main() + tn->start);
|
||||
|
||||
// Compute base pointer and stack pointer.
|
||||
rfe->framePointer = frame.fp() - BaselineFrame::FramePointerOffset;
|
||||
|
@ -735,7 +735,7 @@ DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok)
|
||||
{
|
||||
// Unwind scope chain to stack depth 0.
|
||||
ScopeIter si(frame, pc, cx);
|
||||
UnwindScope(cx, si, 0);
|
||||
UnwindScope(cx, si, frame->script()->main());
|
||||
|
||||
// If ScriptDebugEpilogue returns |true| we have to return the frame's
|
||||
// return value. If it returns |false|, the debugger threw an exception.
|
||||
@ -931,6 +931,19 @@ DebugLeaveBlock(JSContext *cx, BaselineFrame *frame, jsbytecode *pc)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
EnterWith(JSContext *cx, BaselineFrame *frame, HandleValue val, Handle<StaticWithObject *> templ)
|
||||
{
|
||||
return EnterWithOperation(cx, frame, val, templ);
|
||||
}
|
||||
|
||||
bool
|
||||
LeaveWith(JSContext *cx, BaselineFrame *frame)
|
||||
{
|
||||
frame->popWith(cx);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InitBaselineFrameForOsr(BaselineFrame *frame, StackFrame *interpFrame, uint32_t numStackValues)
|
||||
{
|
||||
|
@ -16,6 +16,7 @@ namespace js {
|
||||
|
||||
class DeclEnvObject;
|
||||
class ForkJoinContext;
|
||||
class StaticWithObject;
|
||||
|
||||
namespace jit {
|
||||
|
||||
@ -273,6 +274,7 @@ template <> struct TypeToDataType<HandleObject> { static const DataType result =
|
||||
template <> struct TypeToDataType<HandleString> { static const DataType result = Type_Handle; };
|
||||
template <> struct TypeToDataType<HandlePropertyName> { static const DataType result = Type_Handle; };
|
||||
template <> struct TypeToDataType<HandleFunction> { static const DataType result = Type_Handle; };
|
||||
template <> struct TypeToDataType<Handle<StaticWithObject *> > { static const DataType result = Type_Handle; };
|
||||
template <> struct TypeToDataType<Handle<StaticBlockObject *> > { static const DataType result = Type_Handle; };
|
||||
template <> struct TypeToDataType<HandleScript> { static const DataType result = Type_Handle; };
|
||||
template <> struct TypeToDataType<HandleValue> { static const DataType result = Type_Handle; };
|
||||
@ -298,6 +300,9 @@ template <> struct TypeToArgProperties<HandlePropertyName> {
|
||||
template <> struct TypeToArgProperties<HandleFunction> {
|
||||
static const uint32_t result = TypeToArgProperties<JSFunction *>::result | VMFunction::ByRef;
|
||||
};
|
||||
template <> struct TypeToArgProperties<Handle<StaticWithObject *> > {
|
||||
static const uint32_t result = TypeToArgProperties<StaticWithObject *>::result | VMFunction::ByRef;
|
||||
};
|
||||
template <> struct TypeToArgProperties<Handle<StaticBlockObject *> > {
|
||||
static const uint32_t result = TypeToArgProperties<StaticBlockObject *>::result | VMFunction::ByRef;
|
||||
};
|
||||
@ -652,6 +657,10 @@ JSObject *InitRestParameter(JSContext *cx, uint32_t length, Value *rest, HandleO
|
||||
bool HandleDebugTrap(JSContext *cx, BaselineFrame *frame, uint8_t *retAddr, bool *mustReturn);
|
||||
bool OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn);
|
||||
|
||||
bool EnterWith(JSContext *cx, BaselineFrame *frame, HandleValue val,
|
||||
Handle<StaticWithObject *> templ);
|
||||
bool LeaveWith(JSContext *cx, BaselineFrame *frame);
|
||||
|
||||
bool PushBlockScope(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block);
|
||||
bool PopBlockScope(JSContext *cx, BaselineFrame *frame);
|
||||
bool DebugLeaveBlock(JSContext *cx, BaselineFrame *frame, jsbytecode *pc);
|
||||
|
@ -3777,7 +3777,9 @@ MacroAssemblerARMCompat::callWithABI(void *fun, MoveOp::Type result)
|
||||
case MoveOp::FLOAT32: passedArgTypes_ |= ArgType_Float32; break;
|
||||
default: MOZ_ASSUME_UNREACHABLE("Invalid return type");
|
||||
}
|
||||
#ifdef DEBUG
|
||||
AssertValidABIFunctionType(passedArgTypes_);
|
||||
#endif
|
||||
ABIFunctionType type = ABIFunctionType(passedArgTypes_);
|
||||
fun = Simulator::RedirectNativeFunction(fun, type);
|
||||
#endif
|
||||
|
@ -3679,24 +3679,30 @@ JSScript::makeTypes(JSContext *cx)
|
||||
|
||||
unsigned count = TypeScript::NumTypeSets(this);
|
||||
|
||||
types = (TypeScript *) cx->calloc_(sizeof(TypeScript) + (sizeof(StackTypeSet) * count));
|
||||
if (!types) {
|
||||
TypeScript *typeScript = (TypeScript *) cx->calloc_(sizeof(TypeScript) + (sizeof(StackTypeSet) * count));
|
||||
if (!typeScript) {
|
||||
cx->compartment()->types.setPendingNukeTypes(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
new(types) TypeScript();
|
||||
new(typeScript) TypeScript();
|
||||
|
||||
TypeSet *typeArray = types->typeArray();
|
||||
TypeSet *typeArray = typeScript->typeArray();
|
||||
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
new (&typeArray[i]) StackTypeSet();
|
||||
|
||||
{
|
||||
AutoLockForCompilation lock(cx);
|
||||
types = typeScript;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
for (unsigned i = 0; i < nTypeSets(); i++)
|
||||
for (unsigned i = 0; i < nTypeSets(); i++) {
|
||||
InferSpew(ISpewOps, "typeSet: %sT%p%s bytecode%u #%u",
|
||||
InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(),
|
||||
i, id());
|
||||
}
|
||||
TypeSet *thisTypes = TypeScript::ThisTypes(this);
|
||||
InferSpew(ISpewOps, "typeSet: %sT%p%s this #%u",
|
||||
InferSpewColor(thisTypes), thisTypes, InferSpewColorReset(),
|
||||
|
@ -597,7 +597,7 @@ TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *hint, TYPE
|
||||
uint32_t offset = script->pcToOffset(pc);
|
||||
|
||||
// See if this pc is the next typeset opcode after the last one looked up.
|
||||
if (bytecodeMap[*hint + 1] == offset && (*hint + 1) < script->nTypeSets()) {
|
||||
if ((*hint + 1) < script->nTypeSets() && bytecodeMap[*hint + 1] == offset) {
|
||||
(*hint)++;
|
||||
return typeArray + *hint;
|
||||
}
|
||||
|
@ -49,28 +49,6 @@ using mozilla::SpecificNaN;
|
||||
using JS::ToNumber;
|
||||
using JS::GenericNaN;
|
||||
|
||||
#ifndef M_E
|
||||
#define M_E 2.7182818284590452354
|
||||
#endif
|
||||
#ifndef M_LOG2E
|
||||
#define M_LOG2E 1.4426950408889634074
|
||||
#endif
|
||||
#ifndef M_LOG10E
|
||||
#define M_LOG10E 0.43429448190325182765
|
||||
#endif
|
||||
#ifndef M_LN2
|
||||
#define M_LN2 0.69314718055994530942
|
||||
#endif
|
||||
#ifndef M_LN10
|
||||
#define M_LN10 2.30258509299404568402
|
||||
#endif
|
||||
#ifndef M_SQRT2
|
||||
#define M_SQRT2 1.41421356237309504880
|
||||
#endif
|
||||
#ifndef M_SQRT1_2
|
||||
#define M_SQRT1_2 0.70710678118654752440
|
||||
#endif
|
||||
|
||||
static const JSConstDoubleSpec math_constants[] = {
|
||||
{M_E, "E", 0, {0,0,0}},
|
||||
{M_LOG2E, "LOG2E", 0, {0,0,0}},
|
||||
|
@ -11,6 +11,31 @@
|
||||
|
||||
#include "NamespaceImports.h"
|
||||
|
||||
#ifndef M_PI
|
||||
# define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
#ifndef M_E
|
||||
# define M_E 2.7182818284590452354
|
||||
#endif
|
||||
#ifndef M_LOG2E
|
||||
# define M_LOG2E 1.4426950408889634074
|
||||
#endif
|
||||
#ifndef M_LOG10E
|
||||
# define M_LOG10E 0.43429448190325182765
|
||||
#endif
|
||||
#ifndef M_LN2
|
||||
# define M_LN2 0.69314718055994530942
|
||||
#endif
|
||||
#ifndef M_LN10
|
||||
# define M_LN10 2.30258509299404568402
|
||||
#endif
|
||||
#ifndef M_SQRT2
|
||||
# define M_SQRT2 1.41421356237309504880
|
||||
#endif
|
||||
#ifndef M_SQRT1_2
|
||||
# define M_SQRT1_2 0.70710678118654752440
|
||||
#endif
|
||||
|
||||
namespace js {
|
||||
|
||||
typedef double (*UnaryFunType)(double);
|
||||
|
@ -5952,4 +5952,3 @@ JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Objects
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,8 +433,9 @@ SaveSharedScriptData(ExclusiveContext *, Handle<JSScript *>, SharedScriptData *,
|
||||
|
||||
enum XDRClassKind {
|
||||
CK_BlockObject = 0,
|
||||
CK_JSFunction = 1,
|
||||
CK_JSObject = 2
|
||||
CK_WithObject = 1,
|
||||
CK_JSFunction = 2,
|
||||
CK_JSObject = 3
|
||||
};
|
||||
|
||||
template<XDRMode mode>
|
||||
@ -766,6 +767,8 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
|
||||
JSObject *obj = *objp;
|
||||
if (obj->is<BlockObject>())
|
||||
classk = CK_BlockObject;
|
||||
else if (obj->is<StaticWithObject>())
|
||||
classk = CK_WithObject;
|
||||
else if (obj->is<JSFunction>())
|
||||
classk = CK_JSFunction;
|
||||
else if (obj->is<JSObject>() || obj->is<ArrayObject>())
|
||||
@ -778,32 +781,40 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
|
||||
return false;
|
||||
|
||||
switch (classk) {
|
||||
case CK_BlockObject: {
|
||||
case CK_BlockObject:
|
||||
case CK_WithObject: {
|
||||
/* Code the nested block's enclosing scope. */
|
||||
uint32_t blockEnclosingScopeIndex = 0;
|
||||
uint32_t enclosingStaticScopeIndex = 0;
|
||||
if (mode == XDR_ENCODE) {
|
||||
NestedScopeObject &scope = (*objp)->as<NestedScopeObject>();
|
||||
if (NestedScopeObject *enclosing = scope.enclosingNestedScope())
|
||||
blockEnclosingScopeIndex = FindScopeObjectIndex(script, *enclosing);
|
||||
enclosingStaticScopeIndex = FindScopeObjectIndex(script, *enclosing);
|
||||
else
|
||||
blockEnclosingScopeIndex = UINT32_MAX;
|
||||
enclosingStaticScopeIndex = UINT32_MAX;
|
||||
}
|
||||
if (!xdr->codeUint32(&blockEnclosingScopeIndex))
|
||||
if (!xdr->codeUint32(&enclosingStaticScopeIndex))
|
||||
return false;
|
||||
Rooted<JSObject*> blockEnclosingScope(cx);
|
||||
Rooted<JSObject*> enclosingStaticScope(cx);
|
||||
if (mode == XDR_DECODE) {
|
||||
if (blockEnclosingScopeIndex != UINT32_MAX) {
|
||||
JS_ASSERT(blockEnclosingScopeIndex < i);
|
||||
blockEnclosingScope = script->objects()->vector[blockEnclosingScopeIndex];
|
||||
if (enclosingStaticScopeIndex != UINT32_MAX) {
|
||||
JS_ASSERT(enclosingStaticScopeIndex < i);
|
||||
enclosingStaticScope = script->objects()->vector[enclosingStaticScopeIndex];
|
||||
} else {
|
||||
blockEnclosingScope = fun;
|
||||
enclosingStaticScope = fun;
|
||||
}
|
||||
}
|
||||
|
||||
Rooted<StaticBlockObject*> tmp(cx, static_cast<StaticBlockObject *>(objp->get()));
|
||||
if (!XDRStaticBlockObject(xdr, blockEnclosingScope, tmp.address()))
|
||||
return false;
|
||||
*objp = tmp;
|
||||
if (classk == CK_BlockObject) {
|
||||
Rooted<StaticBlockObject*> tmp(cx, static_cast<StaticBlockObject *>(objp->get()));
|
||||
if (!XDRStaticBlockObject(xdr, enclosingStaticScope, tmp.address()))
|
||||
return false;
|
||||
*objp = tmp;
|
||||
} else {
|
||||
Rooted<StaticWithObject*> tmp(cx, static_cast<StaticWithObject *>(objp->get()));
|
||||
if (!XDRStaticWithObject(xdr, enclosingStaticScope, tmp.address()))
|
||||
return false;
|
||||
*objp = tmp;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -396,7 +396,8 @@ class ReferenceFinder {
|
||||
/* Certain classes of object are for internal use only. */
|
||||
if (object->is<BlockObject>() ||
|
||||
object->is<CallObject>() ||
|
||||
object->is<WithObject>() ||
|
||||
object->is<StaticWithObject>() ||
|
||||
object->is<DynamicWithObject>() ||
|
||||
object->is<DeclEnvObject>()) {
|
||||
return JSVAL_VOID;
|
||||
}
|
||||
|
@ -416,10 +416,11 @@ def run_tests_parallel(tests, prefix, options):
|
||||
|
||||
# This queue will contain the return value of the function
|
||||
# processing the test results.
|
||||
total_tests = len(tests) * options.repeat
|
||||
result_process_return_queue = queue_manager.Queue()
|
||||
result_process = Process(target=process_test_results_parallel,
|
||||
args=(async_test_result_queue, result_process_return_queue,
|
||||
notify_queue, len(tests), options))
|
||||
notify_queue, total_tests, options))
|
||||
result_process.start()
|
||||
|
||||
# Ensure that a SIGTERM is handled the same way as SIGINT
|
||||
@ -441,15 +442,16 @@ def run_tests_parallel(tests, prefix, options):
|
||||
try:
|
||||
testcnt = 0
|
||||
# Initially start as many jobs as allowed to run parallel
|
||||
for i in range(min(options.max_jobs,len(tests))):
|
||||
for i in range(min(options.max_jobs,total_tests)):
|
||||
notify_queue.put(True)
|
||||
|
||||
# For every item in the notify queue, start one new worker.
|
||||
# Every completed worker adds a new item to this queue.
|
||||
while notify_queue.get():
|
||||
if (testcnt < len(tests)):
|
||||
if (testcnt < total_tests):
|
||||
# Start one new worker
|
||||
worker_process = Process(target=wrap_parallel_run_test, args=(tests[testcnt], prefix, async_test_result_queue, options))
|
||||
test = tests[testcnt % len(tests)]
|
||||
worker_process = Process(target=wrap_parallel_run_test, args=(test, prefix, async_test_result_queue, options))
|
||||
worker_processes.append(worker_process)
|
||||
worker_process.start()
|
||||
testcnt += 1
|
||||
@ -604,17 +606,19 @@ def process_test_results(results, num_tests, options):
|
||||
return print_test_summary(num_tests, failures, complete, doing, options)
|
||||
|
||||
def get_serial_results(tests, prefix, options):
|
||||
for test in tests:
|
||||
yield run_test(test, prefix, options)
|
||||
for i in xrange(0, options.repeat):
|
||||
for test in tests:
|
||||
yield run_test(test, prefix, options)
|
||||
|
||||
def run_tests(tests, prefix, options):
|
||||
gen = get_serial_results(tests, prefix, options)
|
||||
ok = process_test_results(gen, len(tests), options)
|
||||
ok = process_test_results(gen, len(tests) * options.repeat, options)
|
||||
return ok
|
||||
|
||||
def get_remote_results(tests, device, prefix, options):
|
||||
for test in tests:
|
||||
yield run_test_remote(test, device, prefix, options)
|
||||
for i in xrange(0, options.repeat):
|
||||
for test in tests:
|
||||
yield run_test_remote(test, device, prefix, options)
|
||||
|
||||
def push_libs(options, device):
|
||||
# This saves considerable time in pushing unnecessary libraries
|
||||
@ -668,7 +672,7 @@ def run_tests_remote(tests, prefix, options):
|
||||
|
||||
# Run all tests.
|
||||
gen = get_remote_results(tests, dm, prefix, options)
|
||||
ok = process_test_results(gen, len(tests), options)
|
||||
ok = process_test_results(gen, len(tests) * options.repeat, options)
|
||||
return ok
|
||||
|
||||
def parse_jitflags(options):
|
||||
|
@ -5591,7 +5591,8 @@ IsDeclarative(Env *env)
|
||||
static bool
|
||||
IsWith(Env *env)
|
||||
{
|
||||
return env->is<DebugScopeObject>() && env->as<DebugScopeObject>().scope().is<WithObject>();
|
||||
return env->is<DebugScopeObject>() &&
|
||||
env->as<DebugScopeObject>().scope().is<DynamicWithObject>();
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -5641,7 +5642,7 @@ DebuggerEnv_getObject(JSContext *cx, unsigned argc, Value *vp)
|
||||
|
||||
JSObject *obj;
|
||||
if (IsWith(env)) {
|
||||
obj = &env->as<DebugScopeObject>().scope().as<WithObject>().object();
|
||||
obj = &env->as<DebugScopeObject>().scope().as<DynamicWithObject>().object();
|
||||
} else {
|
||||
obj = env;
|
||||
JS_ASSERT(!obj->is<DebugScopeObject>());
|
||||
|
@ -183,8 +183,8 @@ FetchName(JSContext *cx, HandleObject obj, HandleObject obj2, HandlePropertyName
|
||||
return false;
|
||||
} else {
|
||||
Rooted<JSObject*> normalized(cx, obj);
|
||||
if (normalized->getClass() == &WithObject::class_ && !shape->hasDefaultGetter())
|
||||
normalized = &normalized->as<WithObject>().object();
|
||||
if (normalized->is<DynamicWithObject>() && !shape->hasDefaultGetter())
|
||||
normalized = &normalized->as<DynamicWithObject>().object();
|
||||
if (shape->isDataDescriptor() && shape->hasDefaultGetter()) {
|
||||
/* Fast path for Object instance properties. */
|
||||
JS_ASSERT(shape->hasSlot());
|
||||
|
@ -831,9 +831,11 @@ js::TypeOfValue(const Value &v)
|
||||
* Enter the new with scope using an object at sp[-1] and associate the depth
|
||||
* of the with block with sp + stackIndex.
|
||||
*/
|
||||
static bool
|
||||
EnterWith(JSContext *cx, AbstractFramePtr frame, HandleValue val, uint32_t stackDepth)
|
||||
bool
|
||||
js::EnterWithOperation(JSContext *cx, AbstractFramePtr frame, HandleValue val,
|
||||
HandleObject staticWith)
|
||||
{
|
||||
JS_ASSERT(staticWith->is<StaticWithObject>());
|
||||
RootedObject obj(cx);
|
||||
if (val.isObject()) {
|
||||
obj = &val.toObject();
|
||||
@ -844,7 +846,7 @@ EnterWith(JSContext *cx, AbstractFramePtr frame, HandleValue val, uint32_t stack
|
||||
}
|
||||
|
||||
RootedObject scopeChain(cx, frame.scopeChain());
|
||||
WithObject *withobj = WithObject::create(cx, obj, scopeChain, stackDepth);
|
||||
DynamicWithObject *withobj = DynamicWithObject::create(cx, obj, scopeChain, staticWith);
|
||||
if (!withobj)
|
||||
return false;
|
||||
|
||||
@ -852,23 +854,25 @@ EnterWith(JSContext *cx, AbstractFramePtr frame, HandleValue val, uint32_t stack
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Unwind block and scope chains to match the given depth. */
|
||||
// Unwind scope chain and iterator to match the static scope corresponding to
|
||||
// the given bytecode position.
|
||||
void
|
||||
js::UnwindScope(JSContext *cx, ScopeIter &si, uint32_t stackDepth)
|
||||
js::UnwindScope(JSContext *cx, ScopeIter &si, jsbytecode *pc)
|
||||
{
|
||||
for (; !si.done(); ++si) {
|
||||
if (si.done())
|
||||
return;
|
||||
|
||||
Rooted<NestedScopeObject *> staticScope(cx, si.frame().script()->getStaticScope(pc));
|
||||
|
||||
for (; si.staticScope() != staticScope; ++si) {
|
||||
switch (si.type()) {
|
||||
case ScopeIter::Block:
|
||||
if (si.staticBlock().stackDepth() < stackDepth)
|
||||
return;
|
||||
if (cx->compartment()->debugMode())
|
||||
DebugScopes::onPopBlock(cx, si);
|
||||
if (si.staticBlock().needsClone())
|
||||
si.frame().popBlock(cx);
|
||||
break;
|
||||
case ScopeIter::With:
|
||||
if (si.scope().as<WithObject>().stackDepth() < stackDepth)
|
||||
return;
|
||||
si.frame().popWith(cx);
|
||||
break;
|
||||
case ScopeIter::Call:
|
||||
@ -881,7 +885,7 @@ js::UnwindScope(JSContext *cx, ScopeIter &si, uint32_t stackDepth)
|
||||
static void
|
||||
ForcedReturn(JSContext *cx, ScopeIter &si, FrameRegs ®s)
|
||||
{
|
||||
UnwindScope(cx, si, 0);
|
||||
UnwindScope(cx, si, regs.fp()->script()->main());
|
||||
regs.setToEndOfScript();
|
||||
}
|
||||
|
||||
@ -1006,7 +1010,7 @@ HandleError(JSContext *cx, FrameRegs ®s)
|
||||
for (TryNoteIter tni(cx, regs); !tni.done(); ++tni) {
|
||||
JSTryNote *tn = *tni;
|
||||
|
||||
UnwindScope(cx, si, tn->stackDepth);
|
||||
UnwindScope(cx, si, regs.fp()->script()->main() + tn->start);
|
||||
|
||||
/*
|
||||
* Set pc to the first bytecode after the the try note to point
|
||||
@ -1727,13 +1731,6 @@ END_CASE(JSOP_POP)
|
||||
CASE(JSOP_POPN)
|
||||
JS_ASSERT(GET_UINT16(REGS.pc) <= REGS.stackDepth());
|
||||
REGS.sp -= GET_UINT16(REGS.pc);
|
||||
#ifdef DEBUG
|
||||
if (NestedScopeObject *scope = script->getStaticScope(REGS.pc + JSOP_POPN_LENGTH)) {
|
||||
JS_ASSERT(scope->is<StaticBlockObject>());
|
||||
StaticBlockObject &blockObj = scope->as<StaticBlockObject>();
|
||||
JS_ASSERT(REGS.stackDepth() >= blockObj.stackDepth() + blockObj.slotCount());
|
||||
}
|
||||
#endif
|
||||
END_CASE(JSOP_POPN)
|
||||
|
||||
CASE(JSOP_POPNV)
|
||||
@ -1742,13 +1739,6 @@ CASE(JSOP_POPNV)
|
||||
Value val = REGS.sp[-1];
|
||||
REGS.sp -= GET_UINT16(REGS.pc);
|
||||
REGS.sp[-1] = val;
|
||||
#ifdef DEBUG
|
||||
if (NestedScopeObject *scope = script->getStaticScope(REGS.pc + JSOP_POPNV_LENGTH)) {
|
||||
JS_ASSERT(scope->is<StaticBlockObject>());
|
||||
StaticBlockObject &blockObj = scope->as<StaticBlockObject>();
|
||||
JS_ASSERT(REGS.stackDepth() >= blockObj.stackDepth() + blockObj.slotCount());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
END_CASE(JSOP_POPNV)
|
||||
|
||||
@ -1759,28 +1749,18 @@ END_CASE(JSOP_SETRVAL)
|
||||
CASE(JSOP_ENTERWITH)
|
||||
{
|
||||
RootedValue &val = rootValue0;
|
||||
RootedObject &staticWith = rootObject0;
|
||||
val = REGS.sp[-1];
|
||||
REGS.sp--;
|
||||
staticWith = script->getObject(REGS.pc);
|
||||
|
||||
if (!EnterWith(cx, REGS.fp(), val, REGS.stackDepth() - 1))
|
||||
if (!EnterWithOperation(cx, REGS.fp(), val, staticWith))
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* We must ensure that different "with" blocks have different stack depth
|
||||
* associated with them. This allows the try handler search to properly
|
||||
* recover the scope chain. Thus we must keep the stack at least at the
|
||||
* current level.
|
||||
*
|
||||
* We set sp[-1] to the current "with" object to help asserting the
|
||||
* enter/leave balance in [leavewith].
|
||||
*/
|
||||
REGS.sp[-1].setObject(*REGS.fp()->scopeChain());
|
||||
}
|
||||
END_CASE(JSOP_ENTERWITH)
|
||||
|
||||
CASE(JSOP_LEAVEWITH)
|
||||
JS_ASSERT(REGS.sp[-1].toObject() == *REGS.fp()->scopeChain());
|
||||
REGS.fp()->popWith(cx);
|
||||
REGS.sp--;
|
||||
END_CASE(JSOP_LEAVEWITH)
|
||||
|
||||
CASE(JSOP_RETURN)
|
||||
|
@ -318,9 +318,10 @@ TypeOfValue(const Value &v);
|
||||
extern bool
|
||||
HasInstance(JSContext *cx, HandleObject obj, HandleValue v, bool *bp);
|
||||
|
||||
/* Unwind block and scope chains to match the given depth. */
|
||||
// Unwind scope chain and iterator to match the static scope corresponding to
|
||||
// the given bytecode position.
|
||||
extern void
|
||||
UnwindScope(JSContext *cx, ScopeIter &si, uint32_t stackDepth);
|
||||
UnwindScope(JSContext *cx, ScopeIter &si, jsbytecode *pc);
|
||||
|
||||
/*
|
||||
* Unwind for an uncatchable exception. This means not running finalizers, etc;
|
||||
@ -450,6 +451,10 @@ bool
|
||||
InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandlePropertyName name,
|
||||
HandleObject val);
|
||||
|
||||
bool
|
||||
EnterWithOperation(JSContext *cx, AbstractFramePtr frame, HandleValue val, HandleObject staticWith);
|
||||
|
||||
|
||||
bool
|
||||
InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleValue idval,
|
||||
HandleObject val);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user