mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Merge m-c to fx-team.
--HG-- rename : content/media/mediasource/SourceBuffer.cpp => content/media/mediasource/ContainerParser.cpp extra : rebase_source : 8e7ce8e6b3b7dd41e6691465e6e65437d694a3ab
This commit is contained in:
commit
93d73ce5fb
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
@ -135,9 +135,9 @@
|
||||
<project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
|
||||
<project name="kernel/common" path="kernel" revision="28aab3bd1139b6beea545f50dee8903c0634de84"/>
|
||||
<project name="platform/system/core" path="system/core" revision="53d584d4a4b4316e4de9ee5f210d662f89b44e7e"/>
|
||||
<project name="u-boot" path="u-boot" revision="2d7a801a3e002078f885e8085fad374a564682e5"/>
|
||||
<project name="u-boot" path="u-boot" revision="982c1fd67b89d5573317c1796cf5b0143de44e8a"/>
|
||||
<project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="6974f8e771d4d8e910357a6739ab124768891e8f"/>
|
||||
<project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="69c8c336794666b010e34b2f501d89118513c546"/>
|
||||
<project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="1d4697b16ed039fd1de0a23bda150523e743e2ad"/>
|
||||
<project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/>
|
||||
<project name="vendor/sprd/proprietories" path="vendor/sprd/proprietories" revision="d2466593022f7078aaaf69026adf3367c2adb7bb"/>
|
||||
</manifest>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<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="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</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="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<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="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="fe92ddd450e03b38edb2d465de7897971d68ac68">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
@ -132,7 +132,7 @@
|
||||
<!-- Flame specific things -->
|
||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="1bb28abbc215f45220620af5cd60a8ac1be93722"/>
|
||||
<project name="device/qcom/common" path="device/qcom/common" revision="54c32c2ddef066fbdf611d29e4b7c47e0363599e"/>
|
||||
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="6f72b9d7a2322043fd0c4ba889ad689b084081c5"/>
|
||||
<project name="device-flame" path="device/t2m/flame" remote="b2g" revision="960533f716ce31dfad357e87fa2f1d9ee5e94674"/>
|
||||
<project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="893238eb1215f8fd4f3747169170cc5e1cc33969"/>
|
||||
<project name="kernel_lk" path="bootable/bootloader/lk" remote="b2g" revision="fda40423ffa573dc6cafd3780515010cb2a086be"/>
|
||||
<project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="b2af89ae378a119819a9c86d9a12e573c7130459"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "98ad49da1fb006f0dcd8ed1af37382ab531ef016",
|
||||
"revision": "02fabec9910191bf6f99cb9879a1e6603d5ed7c3",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
<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="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<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="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</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="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f3e998242fb9a857cf50f5bf3a02304a530ea617"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<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="d170091ba1b5597b05f37fb259f6b8eb02568798"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="12d634c63ec8a41553a9f150c38260c992dc0020"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f293787d4a86257c9e78a35bd3f73b31b706e2"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -1469,11 +1469,16 @@ notification[value="translation"] menulist > .menulist-dropmarker {
|
||||
|
||||
.ac-result-type-keyword,
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
|
||||
list-style-image: url(moz-icon://stock/gtk-find?size=menu);
|
||||
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.ac-result-type-keyword[selected="true"],
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
|
||||
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
|
||||
}
|
||||
|
||||
.ac-result-type-tag,
|
||||
.autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
|
||||
list-style-image: url("chrome://browser/skin/places/tag.png");
|
||||
|
@ -2175,10 +2175,14 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
|
||||
|
||||
.ac-result-type-keyword,
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
|
||||
list-style-image: url(chrome://global/skin/icons/search-textbox.png);
|
||||
margin: 2px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.ac-result-type-keyword[selected="true"],
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
|
||||
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
|
||||
}
|
||||
|
||||
richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-bookmark,
|
||||
|
@ -1395,12 +1395,22 @@ richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-
|
||||
|
||||
.ac-result-type-keyword,
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
|
||||
list-style-image: url(chrome://global/skin/icons/Search-glass.png);
|
||||
-moz-image-region: rect(0px 32px 16px 16px);
|
||||
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
%ifdef WINDOWS_AERO
|
||||
@media not all and (-moz-windows-default-theme) {
|
||||
%endif
|
||||
.ac-result-type-keyword[selected="true"],
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
|
||||
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
|
||||
}
|
||||
%ifdef WINDOWS_AERO
|
||||
}
|
||||
%endif
|
||||
|
||||
.ac-result-type-tag,
|
||||
.autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
|
||||
list-style-image: url("chrome://browser/skin/places/tag.png");
|
||||
|
@ -320,12 +320,15 @@ GetDirectionFromText(const char16_t* aText, const uint32_t aLength,
|
||||
current++;
|
||||
}
|
||||
|
||||
Directionality dir = GetDirectionFromChar(ch);
|
||||
if (dir != eDir_NotSet) {
|
||||
if (aFirstStrong) {
|
||||
*aFirstStrong = current;
|
||||
// Just ignore lone surrogates
|
||||
if (!IS_SURROGATE(ch)) {
|
||||
Directionality dir = GetDirectionFromChar(ch);
|
||||
if (dir != eDir_NotSet) {
|
||||
if (aFirstStrong) {
|
||||
*aFirstStrong = current;
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "DOMMediaStream.h"
|
||||
#include "EncodedBufferCache.h"
|
||||
#include "MediaEncoder.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/AudioStreamTrack.h"
|
||||
@ -35,6 +36,73 @@ namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
|
||||
/**
|
||||
+ * MediaRecorderReporter measures memory being used by the Media Recorder.
|
||||
+ *
|
||||
+ * It is a singleton reporter and the single class object lives as long as at
|
||||
+ * least one Recorder is registered. In MediaRecorder, the reporter is unregistered
|
||||
+ * when it is destroyed.
|
||||
+ */
|
||||
class MediaRecorderReporter MOZ_FINAL : public nsIMemoryReporter
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
MediaRecorderReporter() {};
|
||||
static MediaRecorderReporter* UniqueInstance();
|
||||
void InitMemoryReporter();
|
||||
|
||||
static void AddMediaRecorder(MediaRecorder *aRecorder)
|
||||
{
|
||||
GetRecorders().AppendElement(aRecorder);
|
||||
}
|
||||
|
||||
static void RemoveMediaRecorder(MediaRecorder *aRecorder)
|
||||
{
|
||||
RecordersArray& recorders = GetRecorders();
|
||||
recorders.RemoveElement(aRecorder);
|
||||
if (recorders.IsEmpty()) {
|
||||
sUniqueInstance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_METHOD
|
||||
CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData, bool aAnonymize)
|
||||
{
|
||||
int64_t amount = 0;
|
||||
RecordersArray& recorders = GetRecorders();
|
||||
for (size_t i = 0; i < recorders.Length(); ++i) {
|
||||
amount += recorders[i]->SizeOfExcludingThis(MallocSizeOf);
|
||||
}
|
||||
|
||||
#define MEMREPORT(_path, _amount, _desc) \
|
||||
do { \
|
||||
nsresult rv; \
|
||||
rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
|
||||
KIND_HEAP, UNITS_BYTES, _amount, \
|
||||
NS_LITERAL_CSTRING(_desc), aData); \
|
||||
NS_ENSURE_SUCCESS(rv, rv); \
|
||||
} while (0)
|
||||
|
||||
MEMREPORT("explicit/media/recorder", amount,
|
||||
"Memory used by media recorder.");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
|
||||
virtual ~MediaRecorderReporter();
|
||||
static StaticRefPtr<MediaRecorderReporter> sUniqueInstance;
|
||||
typedef nsTArray<MediaRecorder*> RecordersArray;
|
||||
static RecordersArray& GetRecorders()
|
||||
{
|
||||
return UniqueInstance()->mRecorders;
|
||||
}
|
||||
RecordersArray mRecorders;
|
||||
};
|
||||
NS_IMPL_ISUPPORTS(MediaRecorderReporter, nsIMemoryReporter);
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaRecorder, DOMEventTargetHelper,
|
||||
mDOMStream, mAudioNode)
|
||||
|
||||
@ -332,6 +400,14 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t
|
||||
SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
size_t amount = mEncoder->SizeOfExcludingThis(aMallocSizeOf);
|
||||
return amount;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
// Only DestroyRunnable is allowed to delete Session object.
|
||||
virtual ~Session()
|
||||
@ -681,7 +757,7 @@ MediaRecorder::Start(const Optional<int32_t>& aTimeSlice, ErrorResult& aResult)
|
||||
|
||||
timeSlice = aTimeSlice.Value();
|
||||
}
|
||||
|
||||
MediaRecorderReporter::AddMediaRecorder(this);
|
||||
mState = RecordingState::Recording;
|
||||
// Start a session.
|
||||
mSessions.AppendElement();
|
||||
@ -693,6 +769,7 @@ void
|
||||
MediaRecorder::Stop(ErrorResult& aResult)
|
||||
{
|
||||
LOG(PR_LOG_DEBUG, ("MediaRecorder.Stop %p", this));
|
||||
MediaRecorderReporter::RemoveMediaRecorder(this);
|
||||
if (mState == RecordingState::Inactive) {
|
||||
aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
@ -988,5 +1065,36 @@ MediaRecorder::GetSourcePrincipal()
|
||||
return doc ? doc->NodePrincipal() : nullptr;
|
||||
}
|
||||
|
||||
size_t
|
||||
MediaRecorder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
size_t amount = 42;
|
||||
for (size_t i = 0; i < mSessions.Length(); ++i) {
|
||||
amount += mSessions[i]->SizeOfExcludingThis(aMallocSizeOf);
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
|
||||
StaticRefPtr<MediaRecorderReporter> MediaRecorderReporter::sUniqueInstance;
|
||||
|
||||
MediaRecorderReporter* MediaRecorderReporter::UniqueInstance()
|
||||
{
|
||||
if (!sUniqueInstance) {
|
||||
sUniqueInstance = new MediaRecorderReporter();
|
||||
sUniqueInstance->InitMemoryReporter();
|
||||
}
|
||||
return sUniqueInstance;
|
||||
}
|
||||
|
||||
void MediaRecorderReporter::InitMemoryReporter()
|
||||
{
|
||||
RegisterWeakMemoryReporter(this);
|
||||
}
|
||||
|
||||
MediaRecorderReporter::~MediaRecorderReporter()
|
||||
{
|
||||
UnregisterWeakMemoryReporter(this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "mozilla/dom/MediaRecorderBinding.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "nsIDocumentActivity.h"
|
||||
|
||||
// Max size for allowing queue encoded data in memory
|
||||
@ -90,6 +91,11 @@ public:
|
||||
const MediaRecorderOptions& aInitDict,
|
||||
ErrorResult& aRv);
|
||||
|
||||
/*
|
||||
* Measure the size of the buffer, and memory occupied by mAudioEncoder
|
||||
* and mVideoEncoder
|
||||
*/
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
// EventHandler
|
||||
IMPL_EVENT_HANDLER(dataavailable)
|
||||
IMPL_EVENT_HANDLER(error)
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "nsMimeTypes.h"
|
||||
#include "prlog.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
||||
#include "OggWriter.h"
|
||||
#ifdef MOZ_OPUS
|
||||
@ -204,6 +205,9 @@ MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
|
||||
|
||||
rv = mWriter->GetContainerData(aOutputBufs,
|
||||
ContainerWriter::GET_HEADER);
|
||||
if (aOutputBufs != nullptr) {
|
||||
mSizeOfBuffer = aOutputBufs->SizeOfExcludingThis(MallocSizeOf);
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(PR_LOG_ERROR,("Error! writer fail to generate header!"));
|
||||
mState = ENCODE_ERROR;
|
||||
@ -236,6 +240,9 @@ MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
|
||||
rv = mWriter->GetContainerData(aOutputBufs,
|
||||
isAudioCompleted && isVideoCompleted ?
|
||||
ContainerWriter::FLUSH_NEEDED : 0);
|
||||
if (aOutputBufs != nullptr) {
|
||||
mSizeOfBuffer = aOutputBufs->SizeOfExcludingThis(MallocSizeOf);
|
||||
}
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// Successfully get the copy of final container data from writer.
|
||||
reloop = false;
|
||||
@ -250,6 +257,7 @@ MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
|
||||
case ENCODE_DONE:
|
||||
case ENCODE_ERROR:
|
||||
LOG(PR_LOG_DEBUG, ("MediaEncoder has been shutdown."));
|
||||
mSizeOfBuffer = 0;
|
||||
mShutdown = true;
|
||||
reloop = false;
|
||||
break;
|
||||
@ -323,4 +331,21 @@ MediaEncoder::IsOMXEncoderEnabled()
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SizeOfExcludingThis measures memory being used by the Media Encoder.
|
||||
* Currently it measures the size of the Encoder buffer and memory occupied
|
||||
* by mAudioEncoder and mVideoEncoder.
|
||||
*/
|
||||
size_t
|
||||
MediaEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
size_t amount = 0;
|
||||
if (mState == ENCODE_TRACK) {
|
||||
amount = mSizeOfBuffer +
|
||||
(mAudioEncoder != nullptr ? mAudioEncoder->SizeOfExcludingThis(aMallocSizeOf) : 0) +
|
||||
(mVideoEncoder != nullptr ? mVideoEncoder->SizeOfExcludingThis(aMallocSizeOf) : 0);
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include "TrackEncoder.h"
|
||||
#include "ContainerWriter.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -66,6 +68,7 @@ public :
|
||||
, mVideoEncoder(aVideoEncoder)
|
||||
, mStartTime(TimeStamp::Now())
|
||||
, mMIMEType(aMIMEType)
|
||||
, mSizeOfBuffer(0)
|
||||
, mState(MediaEncoder::ENCODE_METADDATA)
|
||||
, mShutdown(false)
|
||||
{}
|
||||
@ -140,6 +143,13 @@ public :
|
||||
static bool IsOMXEncoderEnabled();
|
||||
#endif
|
||||
|
||||
MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
|
||||
/*
|
||||
* Measure the size of the buffer, and memory occupied by mAudioEncoder
|
||||
* and mVideoEncoder
|
||||
*/
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
private:
|
||||
// Get encoded data from trackEncoder and write to muxer
|
||||
nsresult WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder);
|
||||
@ -150,6 +160,7 @@ private:
|
||||
nsAutoPtr<VideoTrackEncoder> mVideoEncoder;
|
||||
TimeStamp mStartTime;
|
||||
nsString mMIMEType;
|
||||
int64_t mSizeOfBuffer;
|
||||
int mState;
|
||||
bool mShutdown;
|
||||
// Get duration from create encoder, for logging purpose
|
||||
|
@ -173,6 +173,12 @@ AudioTrackEncoder::DeInterleaveTrackData(AudioDataValue* aInput,
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
AudioTrackEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
return mRawSegment.SizeOfExcludingThis(aMallocSizeOf);
|
||||
}
|
||||
|
||||
void
|
||||
VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
|
||||
TrackID aID,
|
||||
@ -263,4 +269,10 @@ VideoTrackEncoder::NotifyEndOfStream()
|
||||
mReentrantMonitor.NotifyAll();
|
||||
}
|
||||
|
||||
size_t
|
||||
VideoTrackEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
return mRawSegment.SizeOfExcludingThis(aMallocSizeOf);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -168,6 +168,10 @@ public:
|
||||
*/
|
||||
static void DeInterleaveTrackData(AudioDataValue* aInput, int32_t aDuration,
|
||||
int32_t aChannels, AudioDataValue* aOutput);
|
||||
/**
|
||||
* Measure size of mRawSegment
|
||||
*/
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
@ -241,6 +245,10 @@ public:
|
||||
TrackTicks aTrackOffset,
|
||||
uint32_t aTrackEvents,
|
||||
const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
|
||||
/**
|
||||
* Measure size of mRawSegment
|
||||
*/
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
|
281
content/media/mediasource/ContainerParser.cpp
Normal file
281
content/media/mediasource/ContainerParser.cpp
Normal file
@ -0,0 +1,281 @@
|
||||
/* -*- 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 "ContainerParser.h"
|
||||
|
||||
#include "WebMBufferedParser.h"
|
||||
#include "mozilla/Endian.h"
|
||||
#include "mp4_demuxer/BufferStream.h"
|
||||
#include "mp4_demuxer/MoofParser.h"
|
||||
#include "prlog.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* GetMediaSourceLog();
|
||||
extern PRLogModuleInfo* GetMediaSourceAPILog();
|
||||
|
||||
#define MSE_DEBUG(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#define MSE_DEBUGV(...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG+1, (__VA_ARGS__))
|
||||
#define MSE_API(...) PR_LOG(GetMediaSourceAPILog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#else
|
||||
#define MSE_DEBUG(...)
|
||||
#define MSE_DEBUGV(...)
|
||||
#define MSE_API(...)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
bool
|
||||
ContainerParser::IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
MSE_DEBUG("ContainerParser(%p)::IsInitSegmentPresent aLength=%u [%x%x%x%x]",
|
||||
this, aLength,
|
||||
aLength > 0 ? aData[0] : 0,
|
||||
aLength > 1 ? aData[1] : 0,
|
||||
aLength > 2 ? aData[2] : 0,
|
||||
aLength > 3 ? aData[3] : 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ContainerParser::IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
MSE_DEBUG("ContainerParser(%p)::IsMediaSegmentPresent aLength=%u [%x%x%x%x]",
|
||||
this, aLength,
|
||||
aLength > 0 ? aData[0] : 0,
|
||||
aLength > 1 ? aData[1] : 0,
|
||||
aLength > 2 ? aData[2] : 0,
|
||||
aLength > 3 ? aData[3] : 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ContainerParser::ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
|
||||
int64_t& aStart, int64_t& aEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ContainerParser::TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
|
||||
{
|
||||
NS_WARNING("Using default ContainerParser::TimestampFuzzyEquals implementation");
|
||||
return aLhs == aRhs;
|
||||
}
|
||||
|
||||
const nsTArray<uint8_t>&
|
||||
ContainerParser::InitData()
|
||||
{
|
||||
MOZ_ASSERT(mHasInitData);
|
||||
return mInitData;
|
||||
}
|
||||
|
||||
class WebMContainerParser : public ContainerParser {
|
||||
public:
|
||||
WebMContainerParser()
|
||||
: mParser(0), mOffset(0)
|
||||
{}
|
||||
|
||||
static const unsigned NS_PER_USEC = 1000;
|
||||
|
||||
bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
ContainerParser::IsInitSegmentPresent(aData, aLength);
|
||||
// XXX: This is overly primitive, needs to collect data as it's appended
|
||||
// to the SB and handle, rather than assuming everything is present in a
|
||||
// single aData segment.
|
||||
// 0x1a45dfa3 // EBML
|
||||
// ...
|
||||
// DocType == "webm"
|
||||
// ...
|
||||
// 0x18538067 // Segment (must be "unknown" size)
|
||||
// 0x1549a966 // -> Segment Info
|
||||
// 0x1654ae6b // -> One or more Tracks
|
||||
if (aLength >= 4 &&
|
||||
aData[0] == 0x1a && aData[1] == 0x45 && aData[2] == 0xdf && aData[3] == 0xa3) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
ContainerParser::IsMediaSegmentPresent(aData, aLength);
|
||||
// XXX: This is overly primitive, needs to collect data as it's appended
|
||||
// to the SB and handle, rather than assuming everything is present in a
|
||||
// single aData segment.
|
||||
// 0x1a45dfa3 // EBML
|
||||
// ...
|
||||
// DocType == "webm"
|
||||
// ...
|
||||
// 0x18538067 // Segment (must be "unknown" size)
|
||||
// 0x1549a966 // -> Segment Info
|
||||
// 0x1654ae6b // -> One or more Tracks
|
||||
if (aLength >= 4 &&
|
||||
aData[0] == 0x1f && aData[1] == 0x43 && aData[2] == 0xb6 && aData[3] == 0x75) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
|
||||
int64_t& aStart, int64_t& aEnd)
|
||||
{
|
||||
bool initSegment = IsInitSegmentPresent(aData, aLength);
|
||||
if (initSegment) {
|
||||
mOffset = 0;
|
||||
mParser = WebMBufferedParser(0);
|
||||
mOverlappedMapping.Clear();
|
||||
}
|
||||
|
||||
// XXX if it only adds new mappings, overlapped but not available
|
||||
// (e.g. overlap < 0) frames are "lost" from the reported mappings here.
|
||||
nsTArray<WebMTimeDataOffset> mapping;
|
||||
mapping.AppendElements(mOverlappedMapping);
|
||||
mOverlappedMapping.Clear();
|
||||
ReentrantMonitor dummy("dummy");
|
||||
mParser.Append(aData, aLength, mapping, dummy);
|
||||
|
||||
// XXX This is a bit of a hack. Assume if there are no timecodes
|
||||
// present and it's an init segment that it's _just_ an init segment.
|
||||
// We should be more precise.
|
||||
if (initSegment) {
|
||||
uint32_t length = aLength;
|
||||
if (!mapping.IsEmpty()) {
|
||||
length = mapping[0].mSyncOffset;
|
||||
MOZ_ASSERT(length <= aLength);
|
||||
}
|
||||
MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
|
||||
this, length);
|
||||
|
||||
mInitData.ReplaceElementsAt(0, mInitData.Length(), aData, length);
|
||||
mHasInitData = true;
|
||||
}
|
||||
mOffset += aLength;
|
||||
|
||||
if (mapping.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Exclude frames that we don't enough data to cover the end of.
|
||||
uint32_t endIdx = mapping.Length() - 1;
|
||||
while (mOffset < mapping[endIdx].mEndOffset && endIdx > 0) {
|
||||
endIdx -= 1;
|
||||
}
|
||||
|
||||
if (endIdx == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t frameDuration = mapping[endIdx].mTimecode - mapping[endIdx - 1].mTimecode;
|
||||
aStart = mapping[0].mTimecode / NS_PER_USEC;
|
||||
aEnd = (mapping[endIdx].mTimecode + frameDuration) / NS_PER_USEC;
|
||||
|
||||
MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld] [fso=%lld, leo=%lld, l=%u endIdx=%u]",
|
||||
this, aStart, aEnd, mapping[0].mSyncOffset, mapping[endIdx].mEndOffset, mapping.Length(), endIdx);
|
||||
|
||||
mapping.RemoveElementsAt(0, endIdx + 1);
|
||||
mOverlappedMapping.AppendElements(mapping);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
|
||||
{
|
||||
int64_t error = mParser.GetTimecodeScale() / NS_PER_USEC;
|
||||
return llabs(aLhs - aRhs) <= error * 2;
|
||||
}
|
||||
|
||||
private:
|
||||
WebMBufferedParser mParser;
|
||||
nsTArray<WebMTimeDataOffset> mOverlappedMapping;
|
||||
int64_t mOffset;
|
||||
};
|
||||
|
||||
class MP4ContainerParser : public ContainerParser {
|
||||
public:
|
||||
MP4ContainerParser() {}
|
||||
|
||||
bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
ContainerParser::IsInitSegmentPresent(aData, aLength);
|
||||
// Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
|
||||
// file is the 'ftyp' atom followed by a file type. We just check for a
|
||||
// vaguely valid 'ftyp' atom.
|
||||
|
||||
if (aLength < 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t chunk_size = BigEndian::readUint32(aData);
|
||||
if (chunk_size < 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return aData[4] == 'f' && aData[5] == 't' && aData[6] == 'y' &&
|
||||
aData[7] == 'p';
|
||||
}
|
||||
|
||||
bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
|
||||
int64_t& aStart, int64_t& aEnd)
|
||||
{
|
||||
bool initSegment = IsInitSegmentPresent(aData, aLength);
|
||||
if (initSegment) {
|
||||
mStream = new mp4_demuxer::BufferStream();
|
||||
mParser = new mp4_demuxer::MoofParser(mStream, 0);
|
||||
} else if (!mStream || !mParser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mStream->AppendBytes(aData, aLength);
|
||||
nsTArray<MediaByteRange> byteRanges;
|
||||
byteRanges.AppendElement(mStream->GetByteRange());
|
||||
mParser->RebuildFragmentedIndex(byteRanges);
|
||||
|
||||
if (initSegment) {
|
||||
const MediaByteRange& range = mParser->mInitRange;
|
||||
MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
|
||||
this, range.mEnd - range.mStart);
|
||||
|
||||
mInitData.ReplaceElementsAt(0, mInitData.Length(),
|
||||
aData + range.mStart,
|
||||
range.mEnd - range.mStart);
|
||||
mHasInitData = true;
|
||||
}
|
||||
|
||||
mp4_demuxer::Interval<mp4_demuxer::Microseconds> compositionRange =
|
||||
mParser->GetCompositionRange(byteRanges);
|
||||
|
||||
mStream->DiscardBefore(mParser->mOffset);
|
||||
|
||||
if (compositionRange.IsNull()) {
|
||||
return false;
|
||||
}
|
||||
aStart = compositionRange.start;
|
||||
aEnd = compositionRange.end;
|
||||
MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld]",
|
||||
this, aStart, aEnd);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<mp4_demuxer::BufferStream> mStream;
|
||||
nsAutoPtr<mp4_demuxer::MoofParser> mParser;
|
||||
};
|
||||
|
||||
/*static*/ ContainerParser*
|
||||
ContainerParser::CreateForMIMEType(const nsACString& aType)
|
||||
{
|
||||
if (aType.LowerCaseEqualsLiteral("video/webm") || aType.LowerCaseEqualsLiteral("audio/webm")) {
|
||||
return new WebMContainerParser();
|
||||
}
|
||||
|
||||
if (aType.LowerCaseEqualsLiteral("video/mp4") || aType.LowerCaseEqualsLiteral("audio/mp4")) {
|
||||
return new MP4ContainerParser();
|
||||
}
|
||||
return new ContainerParser();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
55
content/media/mediasource/ContainerParser.h
Normal file
55
content/media/mediasource/ContainerParser.h
Normal file
@ -0,0 +1,55 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MOZILLA_CONTAINERPARSER_H_
|
||||
#define MOZILLA_CONTAINERPARSER_H_
|
||||
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ContainerParser {
|
||||
public:
|
||||
ContainerParser() : mHasInitData(false) {}
|
||||
virtual ~ContainerParser() {}
|
||||
|
||||
// Return true if aData starts with an initialization segment.
|
||||
// The base implementation exists only for debug logging and is expected
|
||||
// to be called first from the overriding implementation.
|
||||
virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength);
|
||||
|
||||
// Return true if aData starts with a media segment.
|
||||
// The base implementation exists only for debug logging and is expected
|
||||
// to be called first from the overriding implementation.
|
||||
virtual bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength);
|
||||
|
||||
// Parse aData to extract the start and end frame times from the media
|
||||
// segment. aData may not start on a parser sync boundary. Return true
|
||||
// if aStart and aEnd have been updated.
|
||||
virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
|
||||
int64_t& aStart, int64_t& aEnd);
|
||||
|
||||
// Compare aLhs and rHs, considering any error that may exist in the
|
||||
// timestamps from the format's base representation. Return true if aLhs
|
||||
// == aRhs within the error epsilon.
|
||||
virtual bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs);
|
||||
|
||||
const nsTArray<uint8_t>& InitData();
|
||||
|
||||
bool HasInitData()
|
||||
{
|
||||
return mHasInitData;
|
||||
}
|
||||
|
||||
static ContainerParser* CreateForMIMEType(const nsACString& aType);
|
||||
|
||||
protected:
|
||||
nsTArray<uint8_t> mInitData;
|
||||
bool mHasInitData;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
#endif /* MOZILLA_CONTAINERPARSER_H_ */
|
@ -9,15 +9,11 @@
|
||||
#include "AsyncEventRunner.h"
|
||||
#include "MediaSourceUtils.h"
|
||||
#include "TrackBuffer.h"
|
||||
#include "WebMBufferedParser.h"
|
||||
#include "mozilla/Endian.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/MediaSourceBinding.h"
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
#include "mp4_demuxer/BufferStream.h"
|
||||
#include "mp4_demuxer/MoofParser.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIRunnable.h"
|
||||
@ -42,273 +38,6 @@ extern PRLogModuleInfo* GetMediaSourceAPILog();
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ContainerParser {
|
||||
public:
|
||||
virtual ~ContainerParser() {}
|
||||
|
||||
// Return true if aData starts with an initialization segment.
|
||||
// The base implementation exists only for debug logging and is expected
|
||||
// to be called first from the overriding implementation.
|
||||
virtual bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
MSE_DEBUG("ContainerParser(%p)::IsInitSegmentPresent aLength=%u [%x%x%x%x]",
|
||||
this, aLength,
|
||||
aLength > 0 ? aData[0] : 0,
|
||||
aLength > 1 ? aData[1] : 0,
|
||||
aLength > 2 ? aData[2] : 0,
|
||||
aLength > 3 ? aData[3] : 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true if aData starts with a media segment.
|
||||
// The base implementation exists only for debug logging and is expected
|
||||
// to be called first from the overriding implementation.
|
||||
virtual bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
MSE_DEBUG("ContainerParser(%p)::IsMediaSegmentPresent aLength=%u [%x%x%x%x]",
|
||||
this, aLength,
|
||||
aLength > 0 ? aData[0] : 0,
|
||||
aLength > 1 ? aData[1] : 0,
|
||||
aLength > 2 ? aData[2] : 0,
|
||||
aLength > 3 ? aData[3] : 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse aData to extract the start and end frame times from the media
|
||||
// segment. aData may not start on a parser sync boundary. Return true
|
||||
// if aStart and aEnd have been updated.
|
||||
virtual bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
|
||||
int64_t& aStart, int64_t& aEnd)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare aLhs and rHs, considering any error that may exist in the
|
||||
// timestamps from the format's base representation. Return true if aLhs
|
||||
// == aRhs within the error epsilon.
|
||||
virtual bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
|
||||
{
|
||||
NS_WARNING("Using default ContainerParser::TimestampFuzzyEquals implementation");
|
||||
return aLhs == aRhs;
|
||||
}
|
||||
|
||||
virtual const nsTArray<uint8_t>& InitData()
|
||||
{
|
||||
MOZ_ASSERT(mInitData.Length() > 0);
|
||||
return mInitData;
|
||||
}
|
||||
|
||||
static ContainerParser* CreateForMIMEType(const nsACString& aType);
|
||||
|
||||
protected:
|
||||
nsTArray<uint8_t> mInitData;
|
||||
};
|
||||
|
||||
class WebMContainerParser : public ContainerParser {
|
||||
public:
|
||||
WebMContainerParser()
|
||||
: mParser(0), mOffset(0)
|
||||
{}
|
||||
|
||||
static const unsigned NS_PER_USEC = 1000;
|
||||
|
||||
bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
ContainerParser::IsInitSegmentPresent(aData, aLength);
|
||||
// XXX: This is overly primitive, needs to collect data as it's appended
|
||||
// to the SB and handle, rather than assuming everything is present in a
|
||||
// single aData segment.
|
||||
// 0x1a45dfa3 // EBML
|
||||
// ...
|
||||
// DocType == "webm"
|
||||
// ...
|
||||
// 0x18538067 // Segment (must be "unknown" size)
|
||||
// 0x1549a966 // -> Segment Info
|
||||
// 0x1654ae6b // -> One or more Tracks
|
||||
if (aLength >= 4 &&
|
||||
aData[0] == 0x1a && aData[1] == 0x45 && aData[2] == 0xdf && aData[3] == 0xa3) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsMediaSegmentPresent(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
ContainerParser::IsMediaSegmentPresent(aData, aLength);
|
||||
// XXX: This is overly primitive, needs to collect data as it's appended
|
||||
// to the SB and handle, rather than assuming everything is present in a
|
||||
// single aData segment.
|
||||
// 0x1a45dfa3 // EBML
|
||||
// ...
|
||||
// DocType == "webm"
|
||||
// ...
|
||||
// 0x18538067 // Segment (must be "unknown" size)
|
||||
// 0x1549a966 // -> Segment Info
|
||||
// 0x1654ae6b // -> One or more Tracks
|
||||
if (aLength >= 4 &&
|
||||
aData[0] == 0x1f && aData[1] == 0x43 && aData[2] == 0xb6 && aData[3] == 0x75) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
|
||||
int64_t& aStart, int64_t& aEnd)
|
||||
{
|
||||
bool initSegment = IsInitSegmentPresent(aData, aLength);
|
||||
if (initSegment) {
|
||||
mOffset = 0;
|
||||
mParser = WebMBufferedParser(0);
|
||||
mOverlappedMapping.Clear();
|
||||
}
|
||||
|
||||
// XXX if it only adds new mappings, overlapped but not available
|
||||
// (e.g. overlap < 0) frames are "lost" from the reported mappings here.
|
||||
nsTArray<WebMTimeDataOffset> mapping;
|
||||
mapping.AppendElements(mOverlappedMapping);
|
||||
mOverlappedMapping.Clear();
|
||||
ReentrantMonitor dummy("dummy");
|
||||
mParser.Append(aData, aLength, mapping, dummy);
|
||||
|
||||
// XXX This is a bit of a hack. Assume if there are no timecodes
|
||||
// present and it's an init segment that it's _just_ an init segment.
|
||||
// We should be more precise.
|
||||
if (initSegment) {
|
||||
uint32_t length = aLength;
|
||||
if (!mapping.IsEmpty()) {
|
||||
length = mapping[0].mSyncOffset;
|
||||
MOZ_ASSERT(length <= aLength);
|
||||
}
|
||||
MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
|
||||
this, length);
|
||||
|
||||
mInitData.ReplaceElementsAt(0, mInitData.Length(), aData, length);
|
||||
}
|
||||
mOffset += aLength;
|
||||
|
||||
if (mapping.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Exclude frames that we don't enough data to cover the end of.
|
||||
uint32_t endIdx = mapping.Length() - 1;
|
||||
while (mOffset < mapping[endIdx].mEndOffset && endIdx > 0) {
|
||||
endIdx -= 1;
|
||||
}
|
||||
|
||||
if (endIdx == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t frameDuration = mapping[endIdx].mTimecode - mapping[endIdx - 1].mTimecode;
|
||||
aStart = mapping[0].mTimecode / NS_PER_USEC;
|
||||
aEnd = (mapping[endIdx].mTimecode + frameDuration) / NS_PER_USEC;
|
||||
|
||||
MSE_DEBUG("WebMContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld] [fso=%lld, leo=%lld, l=%u endIdx=%u]",
|
||||
this, aStart, aEnd, mapping[0].mSyncOffset, mapping[endIdx].mEndOffset, mapping.Length(), endIdx);
|
||||
|
||||
mapping.RemoveElementsAt(0, endIdx + 1);
|
||||
mOverlappedMapping.AppendElements(mapping);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TimestampsFuzzyEqual(int64_t aLhs, int64_t aRhs)
|
||||
{
|
||||
int64_t error = mParser.GetTimecodeScale() / NS_PER_USEC;
|
||||
return llabs(aLhs - aRhs) <= error * 2;
|
||||
}
|
||||
|
||||
private:
|
||||
WebMBufferedParser mParser;
|
||||
nsTArray<WebMTimeDataOffset> mOverlappedMapping;
|
||||
int64_t mOffset;
|
||||
};
|
||||
|
||||
class MP4ContainerParser : public ContainerParser {
|
||||
public:
|
||||
MP4ContainerParser() {}
|
||||
|
||||
bool IsInitSegmentPresent(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
ContainerParser::IsInitSegmentPresent(aData, aLength);
|
||||
// Each MP4 atom has a chunk size and chunk type. The root chunk in an MP4
|
||||
// file is the 'ftyp' atom followed by a file type. We just check for a
|
||||
// vaguely valid 'ftyp' atom.
|
||||
|
||||
if (aLength < 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t chunk_size = BigEndian::readUint32(aData);
|
||||
if (chunk_size < 8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return aData[4] == 'f' && aData[5] == 't' && aData[6] == 'y' &&
|
||||
aData[7] == 'p';
|
||||
}
|
||||
|
||||
bool ParseStartAndEndTimestamps(const uint8_t* aData, uint32_t aLength,
|
||||
int64_t& aStart, int64_t& aEnd)
|
||||
{
|
||||
bool initSegment = IsInitSegmentPresent(aData, aLength);
|
||||
if (initSegment) {
|
||||
mStream = new mp4_demuxer::BufferStream();
|
||||
mParser = new mp4_demuxer::MoofParser(mStream, 0);
|
||||
} else if (!mStream || !mParser) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mStream->AppendBytes(aData, aLength);
|
||||
nsTArray<MediaByteRange> byteRanges;
|
||||
byteRanges.AppendElement(mStream->GetByteRange());
|
||||
mParser->RebuildFragmentedIndex(byteRanges);
|
||||
|
||||
if (initSegment) {
|
||||
const MediaByteRange& range = mParser->mInitRange;
|
||||
MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: Stashed init of %u bytes.",
|
||||
this, range.mEnd - range.mStart);
|
||||
|
||||
mInitData.ReplaceElementsAt(0, mInitData.Length(),
|
||||
aData + range.mStart,
|
||||
range.mEnd - range.mStart);
|
||||
}
|
||||
|
||||
mp4_demuxer::Interval<mp4_demuxer::Microseconds> compositionRange =
|
||||
mParser->GetCompositionRange(byteRanges);
|
||||
|
||||
mStream->DiscardBefore(mParser->mOffset);
|
||||
|
||||
if (compositionRange.IsNull()) {
|
||||
return false;
|
||||
}
|
||||
aStart = compositionRange.start;
|
||||
aEnd = compositionRange.end;
|
||||
MSE_DEBUG("MP4ContainerParser(%p)::ParseStartAndEndTimestamps: [%lld, %lld]",
|
||||
this, aStart, aEnd);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<mp4_demuxer::BufferStream> mStream;
|
||||
nsAutoPtr<mp4_demuxer::MoofParser> mParser;
|
||||
};
|
||||
|
||||
|
||||
/*static*/ ContainerParser*
|
||||
ContainerParser::CreateForMIMEType(const nsACString& aType)
|
||||
{
|
||||
if (aType.LowerCaseEqualsLiteral("video/webm") || aType.LowerCaseEqualsLiteral("audio/webm")) {
|
||||
return new WebMContainerParser();
|
||||
}
|
||||
|
||||
if (aType.LowerCaseEqualsLiteral("video/mp4") || aType.LowerCaseEqualsLiteral("audio/mp4")) {
|
||||
return new MP4ContainerParser();
|
||||
}
|
||||
return new ContainerParser();
|
||||
}
|
||||
|
||||
namespace dom {
|
||||
|
||||
void
|
||||
@ -497,7 +226,6 @@ SourceBuffer::Ended()
|
||||
SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
|
||||
: DOMEventTargetHelper(aMediaSource->GetParentObject())
|
||||
, mMediaSource(aMediaSource)
|
||||
, mType(aType)
|
||||
, mAppendWindowStart(0)
|
||||
, mAppendWindowEnd(PositiveInfinity<double>())
|
||||
, mTimestampOffset(0)
|
||||
@ -508,10 +236,9 @@ SourceBuffer::SourceBuffer(MediaSource* aMediaSource, const nsACString& aType)
|
||||
MOZ_ASSERT(aMediaSource);
|
||||
mEvictionThreshold = Preferences::GetUint("media.mediasource.eviction_threshold",
|
||||
75 * (1 << 20));
|
||||
mParser = ContainerParser::CreateForMIMEType(aType);
|
||||
mTrackBuffer = new TrackBuffer(aMediaSource->GetDecoder(), aType);
|
||||
MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Create mParser=%p mTrackBuffer=%p",
|
||||
this, mParser.get(), mTrackBuffer.get());
|
||||
MSE_DEBUG("SourceBuffer(%p)::SourceBuffer: Create mTrackBuffer=%p",
|
||||
this, mTrackBuffer.get());
|
||||
}
|
||||
|
||||
SourceBuffer::~SourceBuffer()
|
||||
@ -586,52 +313,7 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
||||
return;
|
||||
}
|
||||
StartUpdating();
|
||||
// TODO: Run more of the buffer append algorithm asynchronously.
|
||||
if (mParser->IsInitSegmentPresent(aData, aLength)) {
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendData: New initialization segment.", this);
|
||||
mMediaSource->QueueInitializationEvent();
|
||||
mTrackBuffer->DiscardDecoder();
|
||||
if (!mTrackBuffer->NewDecoder()) {
|
||||
aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
|
||||
return;
|
||||
}
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
|
||||
} else if (!mTrackBuffer->HasInitSegment()) {
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
|
||||
Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
|
||||
ErrorResult dummy;
|
||||
mMediaSource->EndOfStream(decodeError, dummy);
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
int64_t start, end;
|
||||
if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
|
||||
int64_t lastStart, lastEnd;
|
||||
mTrackBuffer->LastTimestamp(lastStart, lastEnd);
|
||||
if (mParser->IsMediaSegmentPresent(aData, aLength) &&
|
||||
!mParser->TimestampsFuzzyEqual(start, lastEnd)) {
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
|
||||
this, lastStart, lastEnd, start, end);
|
||||
|
||||
// This data is earlier in the timeline than data we have already
|
||||
// processed, so we must create a new decoder to handle the decoding.
|
||||
mTrackBuffer->DiscardDecoder();
|
||||
|
||||
// If we've got a decoder here, it's not initialized, so we can use it
|
||||
// rather than creating a new one.
|
||||
if (!mTrackBuffer->NewDecoder()) {
|
||||
aRv.Throw(NS_ERROR_FAILURE); // XXX: Review error handling.
|
||||
return;
|
||||
}
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendData: Decoder marked as initialized.", this);
|
||||
const nsTArray<uint8_t>& initData = mParser->InitData();
|
||||
mTrackBuffer->AppendData(initData.Elements(), initData.Length());
|
||||
mTrackBuffer->SetLastStartTimestamp(start);
|
||||
}
|
||||
mTrackBuffer->SetLastEndTimestamp(end);
|
||||
MSE_DEBUG("SourceBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
|
||||
this, lastStart, lastEnd, start, end);
|
||||
}
|
||||
if (!mTrackBuffer->AppendData(aData, aLength)) {
|
||||
Optional<MediaSourceEndOfStreamError> decodeError(MediaSourceEndOfStreamError::Decode);
|
||||
ErrorResult dummy;
|
||||
@ -640,9 +322,9 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
|
||||
return;
|
||||
}
|
||||
|
||||
// Schedule the state machine thread to ensure playback starts
|
||||
// if required when data is appended.
|
||||
mMediaSource->GetDecoder()->ScheduleStateMachineThread();
|
||||
if (mTrackBuffer->HasInitSegment()) {
|
||||
mMediaSource->QueueInitializationEvent();
|
||||
}
|
||||
|
||||
// Run the final step of the buffer append algorithm asynchronously to
|
||||
// ensure the SourceBuffer's updating flag transition behaves as required
|
||||
|
@ -28,7 +28,6 @@ struct JSContext;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ContainerParser;
|
||||
class ErrorResult;
|
||||
class TrackBuffer;
|
||||
template <typename T> class AsyncEventRunner;
|
||||
@ -120,13 +119,6 @@ private:
|
||||
void DispatchSimpleEvent(const char* aName);
|
||||
void QueueAsyncSimpleEvent(const char* aName);
|
||||
|
||||
// Create a new decoder for mType, and store the result in mDecoder.
|
||||
// Returns true if mDecoder was set.
|
||||
bool InitNewDecoder();
|
||||
|
||||
// Set mDecoder to null and reset mDecoderInitialized.
|
||||
void DiscardDecoder();
|
||||
|
||||
// Update mUpdating and fire the appropriate events.
|
||||
void StartUpdating();
|
||||
void StopUpdating();
|
||||
@ -141,12 +133,8 @@ private:
|
||||
|
||||
nsRefPtr<MediaSource> mMediaSource;
|
||||
|
||||
const nsCString mType;
|
||||
|
||||
uint32_t mEvictionThreshold;
|
||||
|
||||
nsAutoPtr<ContainerParser> mParser;
|
||||
|
||||
nsRefPtr<TrackBuffer> mTrackBuffer;
|
||||
|
||||
double mAppendWindowStart;
|
||||
|
@ -6,27 +6,19 @@
|
||||
|
||||
#include "TrackBuffer.h"
|
||||
|
||||
#include "ContainerParser.h"
|
||||
#include "MediaSourceDecoder.h"
|
||||
#include "SharedThreadPool.h"
|
||||
#include "MediaTaskQueue.h"
|
||||
#include "SourceBufferDecoder.h"
|
||||
#include "SourceBufferResource.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "prlog.h"
|
||||
|
||||
#if defined(DEBUG)
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
struct JSContext;
|
||||
class JSObject;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* GetMediaSourceLog();
|
||||
extern PRLogModuleInfo* GetMediaSourceAPILog();
|
||||
@ -47,9 +39,9 @@ TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& a
|
||||
, mType(aType)
|
||||
, mLastStartTimestamp(0)
|
||||
, mLastEndTimestamp(0)
|
||||
, mHasInit(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(TrackBuffer);
|
||||
mParser = ContainerParser::CreateForMIMEType(aType);
|
||||
mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
|
||||
aParentDecoder->AddTrackBuffer(this);
|
||||
}
|
||||
@ -102,6 +94,53 @@ TrackBuffer::Shutdown()
|
||||
|
||||
bool
|
||||
TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// TODO: Run more of the buffer append algorithm asynchronously.
|
||||
if (mParser->IsInitSegmentPresent(aData, aLength)) {
|
||||
MSE_DEBUG("TrackBuffer(%p)::AppendData: New initialization segment.", this);
|
||||
if (!NewDecoder()) {
|
||||
return false;
|
||||
}
|
||||
} else if (!mParser->HasInitData()) {
|
||||
MSE_DEBUG("TrackBuffer(%p)::AppendData: Non-init segment appended during initialization.", this);
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t start, end;
|
||||
if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
|
||||
if (mParser->IsMediaSegmentPresent(aData, aLength) &&
|
||||
!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp)) {
|
||||
MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
|
||||
this, mLastStartTimestamp, mLastEndTimestamp, start, end);
|
||||
|
||||
// This data is earlier in the timeline than data we have already
|
||||
// processed, so we must create a new decoder to handle the decoding.
|
||||
if (!NewDecoder()) {
|
||||
return false;
|
||||
}
|
||||
MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
|
||||
const nsTArray<uint8_t>& initData = mParser->InitData();
|
||||
AppendDataToCurrentResource(initData.Elements(), initData.Length());
|
||||
mLastStartTimestamp = start;
|
||||
}
|
||||
mLastEndTimestamp = end;
|
||||
MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
|
||||
this, mLastStartTimestamp, mLastEndTimestamp, start, end);
|
||||
}
|
||||
|
||||
if (!AppendDataToCurrentResource(aData, aLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Schedule the state machine thread to ensure playback starts if required
|
||||
// when data is appended.
|
||||
mParentDecoder->ScheduleStateMachineThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TrackBuffer::AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mCurrentDecoder) {
|
||||
@ -179,7 +218,9 @@ bool
|
||||
TrackBuffer::NewDecoder()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!mCurrentDecoder && mParentDecoder);
|
||||
MOZ_ASSERT(mParentDecoder);
|
||||
|
||||
DiscardDecoder();
|
||||
|
||||
nsRefPtr<SourceBufferDecoder> decoder = mParentDecoder->CreateSubDecoder(mType);
|
||||
if (!decoder) {
|
||||
@ -191,7 +232,6 @@ TrackBuffer::NewDecoder()
|
||||
|
||||
mLastStartTimestamp = 0;
|
||||
mLastEndTimestamp = 0;
|
||||
mHasInit = true;
|
||||
|
||||
return QueueInitializeDecoder(decoder);
|
||||
}
|
||||
@ -316,7 +356,7 @@ bool
|
||||
TrackBuffer::HasInitSegment()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
|
||||
return mHasInit;
|
||||
return mParser->HasInitData();
|
||||
}
|
||||
|
||||
bool
|
||||
@ -324,29 +364,7 @@ TrackBuffer::IsReady()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
|
||||
MOZ_ASSERT((mInfo.HasAudio() || mInfo.HasVideo()) || mInitializedDecoders.IsEmpty());
|
||||
return HasInitSegment() && (mInfo.HasAudio() || mInfo.HasVideo());
|
||||
}
|
||||
|
||||
void
|
||||
TrackBuffer::LastTimestamp(int64_t& aStart, int64_t& aEnd)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
aStart = mLastStartTimestamp;
|
||||
aEnd = mLastEndTimestamp;
|
||||
}
|
||||
|
||||
void
|
||||
TrackBuffer::SetLastStartTimestamp(int64_t aStart)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mLastStartTimestamp = aStart;
|
||||
}
|
||||
|
||||
void
|
||||
TrackBuffer::SetLastEndTimestamp(int64_t aEnd)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mLastEndTimestamp = aEnd;
|
||||
return mParser->HasInitData() && (mInfo.HasAudio() || mInfo.HasVideo());
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ContainerParser;
|
||||
class MediaSourceDecoder;
|
||||
|
||||
namespace dom {
|
||||
@ -45,11 +46,6 @@ public:
|
||||
// decoders buffered ranges in aRanges.
|
||||
double Buffered(dom::TimeRanges* aRanges);
|
||||
|
||||
// Create a new decoder, set mCurrentDecoder to the new decoder, and queue
|
||||
// the decoder for initialization. The decoder is not considered
|
||||
// initialized until it is added to mDecoders.
|
||||
bool NewDecoder();
|
||||
|
||||
// Mark the current decoder's resource as ended, clear mCurrentDecoder and
|
||||
// reset mLast{Start,End}Timestamp.
|
||||
void DiscardDecoder();
|
||||
@ -59,15 +55,10 @@ public:
|
||||
// Returns true if an init segment has been appended.
|
||||
bool HasInitSegment();
|
||||
|
||||
// Returns true iff HasInitSegment() and the decoder using that init
|
||||
// Returns true iff mParser->HasInitData() and the decoder using that init
|
||||
// segment has successfully initialized by setting mHas{Audio,Video}..
|
||||
bool IsReady();
|
||||
|
||||
// Query and update mLast{Start,End}Timestamp.
|
||||
void LastTimestamp(int64_t& aStart, int64_t& aEnd);
|
||||
void SetLastStartTimestamp(int64_t aStart);
|
||||
void SetLastEndTimestamp(int64_t aEnd);
|
||||
|
||||
// Returns true if any of the decoders managed by this track buffer
|
||||
// contain aTime in their buffered ranges.
|
||||
bool ContainsTime(int64_t aTime);
|
||||
@ -89,6 +80,15 @@ public:
|
||||
private:
|
||||
~TrackBuffer();
|
||||
|
||||
// Create a new decoder, set mCurrentDecoder to the new decoder, and queue
|
||||
// the decoder for initialization. The decoder is not considered
|
||||
// initialized until it is added to mDecoders.
|
||||
bool NewDecoder();
|
||||
|
||||
// Helper for AppendData, ensures NotifyDataArrived is called whenever
|
||||
// data is appended to the current decoder's SourceBufferResource.
|
||||
bool AppendDataToCurrentResource(const uint8_t* aData, uint32_t aLength);
|
||||
|
||||
// Queue execution of InitializeDecoder on mTaskQueue.
|
||||
bool QueueInitializeDecoder(nsRefPtr<SourceBufferDecoder> aDecoder);
|
||||
|
||||
@ -112,6 +112,8 @@ private:
|
||||
// function.
|
||||
void RemoveDecoder(nsRefPtr<SourceBufferDecoder> aDecoder);
|
||||
|
||||
nsAutoPtr<ContainerParser> mParser;
|
||||
|
||||
// A task queue using the shared media thread pool. Used exclusively to
|
||||
// initialize (i.e. call ReadMetadata on) decoders as they are created via
|
||||
// NewDecoder.
|
||||
@ -136,10 +138,6 @@ private:
|
||||
int64_t mLastStartTimestamp;
|
||||
int64_t mLastEndTimestamp;
|
||||
|
||||
// Set when the initialization segment is first seen and cached (implied
|
||||
// by new decoder creation). Protected by mParentDecoder's monitor.
|
||||
bool mHasInit;
|
||||
|
||||
// Set when the first decoder used by this TrackBuffer is initialized.
|
||||
// Protected by mParentDecoder's monitor.
|
||||
MediaInfo mInfo;
|
||||
|
@ -17,6 +17,7 @@ EXPORTS.mozilla.dom += [
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ContainerParser.cpp',
|
||||
'MediaSource.cpp',
|
||||
'MediaSourceDecoder.cpp',
|
||||
'MediaSourceReader.cpp',
|
||||
|
@ -669,10 +669,12 @@ public:
|
||||
unsigned flags,
|
||||
JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
|
||||
|
||||
static void ObjectMoved(JSObject *obj, const JSObject *old);
|
||||
|
||||
static const nsOuterWindowProxy singleton;
|
||||
|
||||
protected:
|
||||
nsGlobalWindow* GetWindow(JSObject *proxy) const
|
||||
static nsGlobalWindow* GetWindow(JSObject *proxy)
|
||||
{
|
||||
return nsGlobalWindow::FromSupports(
|
||||
static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate()));
|
||||
@ -704,7 +706,8 @@ const js::Class OuterWindowProxyClass =
|
||||
nullptr, /* outerObject */
|
||||
js::proxy_innerObject,
|
||||
nullptr, /* iteratorObject */
|
||||
false /* isWrappedNative */
|
||||
false, /* isWrappedNative */
|
||||
nsOuterWindowProxy::ObjectMoved
|
||||
));
|
||||
|
||||
bool
|
||||
@ -1024,6 +1027,15 @@ nsOuterWindowProxy::unwatch(JSContext *cx, JS::Handle<JSObject*> proxy,
|
||||
return js::UnwatchGuts(cx, proxy, id);
|
||||
}
|
||||
|
||||
void
|
||||
nsOuterWindowProxy::ObjectMoved(JSObject *obj, const JSObject *old)
|
||||
{
|
||||
nsGlobalWindow* global = GetWindow(obj);
|
||||
if (global) {
|
||||
global->UpdateWrapper(obj, old);
|
||||
}
|
||||
}
|
||||
|
||||
const nsOuterWindowProxy
|
||||
nsOuterWindowProxy::singleton;
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "js/Class.h"
|
||||
#include "js/Id.h" // must come before js/RootingAPI.h
|
||||
#include "js/Value.h" // must come before js/RootingAPI.h
|
||||
#include "js/RootingAPI.h"
|
||||
@ -44,6 +45,14 @@ class XPCWrappedNativeScope;
|
||||
*
|
||||
* The finalizer for the wrapper clears the cache.
|
||||
*
|
||||
* A compacting GC can move the wrapper object. Pointers to moved objects are
|
||||
* usually found and updated by tracing the heap, however non-preserved wrappers
|
||||
* are weak references and are not traced, so another approach is
|
||||
* necessary. Instead a class hook (objectMovedOp) is provided that is called
|
||||
* when an object is moved and is responsible for ensuring pointers are
|
||||
* updated. It does this by calling UpdateWrapper() on the wrapper
|
||||
* cache. SetWrapper() asserts that the hook is implemented for any wrapper set.
|
||||
*
|
||||
* A number of the methods are implemented in nsWrapperCacheInlines.h because we
|
||||
* have to include some JS headers that don't play nicely with the rest of the
|
||||
* codebase. Include nsWrapperCacheInlines.h if you need to call those methods.
|
||||
@ -89,6 +98,8 @@ public:
|
||||
{
|
||||
MOZ_ASSERT(!PreservingWrapper(), "Clearing a preserved wrapper!");
|
||||
MOZ_ASSERT(aWrapper, "Use ClearWrapper!");
|
||||
MOZ_ASSERT(js::HasObjectMovedOp(aWrapper),
|
||||
"Object has not provided the hook to update the wrapper if it is moved");
|
||||
|
||||
SetWrapperJSObject(aWrapper);
|
||||
}
|
||||
@ -104,6 +115,18 @@ public:
|
||||
SetWrapperJSObject(nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the wrapper if the object it contains is moved.
|
||||
*
|
||||
* This method must be called from the objectMovedOp class extension hook for
|
||||
* any wrapper cached object.
|
||||
*/
|
||||
void UpdateWrapper(JSObject* aNewObject, const JSObject* aOldObject)
|
||||
{
|
||||
MOZ_ASSERT(mWrapper == aOldObject);
|
||||
mWrapper = aNewObject;
|
||||
}
|
||||
|
||||
bool PreservingWrapper()
|
||||
{
|
||||
return HasWrapperFlag(WRAPPER_BIT_PRESERVED);
|
||||
@ -162,7 +185,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* The following methods for getting and manipulating flags allow the unused
|
||||
* bits of mFlags to be used by derived classes.
|
||||
*/
|
||||
|
@ -1213,6 +1213,24 @@ ClearWrapper(T* p, void*)
|
||||
ClearWrapper(p, cache);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void
|
||||
UpdateWrapper(T* p, nsWrapperCache* cache, JSObject* obj, const JSObject* old)
|
||||
{
|
||||
JS::AutoAssertGCCallback inCallback(obj);
|
||||
cache->UpdateWrapper(obj, old);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline void
|
||||
UpdateWrapper(T* p, void*, JSObject* obj, const JSObject* old)
|
||||
{
|
||||
JS::AutoAssertGCCallback inCallback(obj);
|
||||
nsWrapperCache* cache;
|
||||
CallQueryInterface(p, &cache);
|
||||
UpdateWrapper(p, cache, obj, old);
|
||||
}
|
||||
|
||||
// Attempt to preserve the wrapper, if any, for a Paris DOM bindings object.
|
||||
// Return true if we successfully preserved the wrapper, or there is no wrapper
|
||||
// to preserve. In the latter case we don't need to preserve the wrapper, because
|
||||
|
@ -17,6 +17,7 @@ AUTOGENERATED_WARNING_COMMENT = \
|
||||
"/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
|
||||
ADDPROPERTY_HOOK_NAME = '_addProperty'
|
||||
FINALIZE_HOOK_NAME = '_finalize'
|
||||
OBJECT_MOVED_HOOK_NAME = '_objectMoved'
|
||||
CONSTRUCT_HOOK_NAME = '_constructor'
|
||||
LEGACYCALLER_HOOK_NAME = '_legacycaller'
|
||||
HASINSTANCE_HOOK_NAME = '_hasInstance'
|
||||
@ -364,48 +365,61 @@ class CGDOMJSClass(CGThing):
|
||||
def define(self):
|
||||
traceHook = 'nullptr'
|
||||
callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
|
||||
objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
|
||||
slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots
|
||||
classFlags = "JSCLASS_IS_DOMJSCLASS | "
|
||||
classExtensionAndObjectOps = """\
|
||||
JS_NULL_CLASS_EXT,
|
||||
JS_NULL_OBJECT_OPS
|
||||
"""
|
||||
classExtensionAndObjectOps = fill(
|
||||
"""
|
||||
{
|
||||
nullptr, /* outerObject */
|
||||
nullptr, /* innerObject */
|
||||
nullptr, /* iteratorObject */
|
||||
false, /* isWrappedNative */
|
||||
nullptr, /* weakmapKeyDelegateOp */
|
||||
${objectMoved} /* objectMovedOp */
|
||||
},
|
||||
JS_NULL_OBJECT_OPS
|
||||
""",
|
||||
objectMoved=objectMovedHook)
|
||||
if self.descriptor.isGlobal():
|
||||
classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_IMPLEMENTS_BARRIERS"
|
||||
traceHook = "JS_GlobalObjectTraceHook"
|
||||
reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
|
||||
if not self.descriptor.workers:
|
||||
classExtensionAndObjectOps = """\
|
||||
{
|
||||
nsGlobalWindow::OuterObject, /* outerObject */
|
||||
nullptr, /* innerObject */
|
||||
nullptr, /* iteratorObject */
|
||||
false, /* isWrappedNative */
|
||||
nullptr /* weakmapKeyDelegateOp */
|
||||
},
|
||||
{
|
||||
nullptr, /* lookupGeneric */
|
||||
nullptr, /* lookupProperty */
|
||||
nullptr, /* lookupElement */
|
||||
nullptr, /* defineGeneric */
|
||||
nullptr, /* defineProperty */
|
||||
nullptr, /* defineElement */
|
||||
nullptr, /* getGeneric */
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* getElement */
|
||||
nullptr, /* setGeneric */
|
||||
nullptr, /* setProperty */
|
||||
nullptr, /* setElement */
|
||||
nullptr, /* getGenericAttributes */
|
||||
nullptr, /* setGenericAttributes */
|
||||
nullptr, /* deleteGeneric */
|
||||
nullptr, /* watch */
|
||||
nullptr, /* unwatch */
|
||||
nullptr, /* slice */
|
||||
nullptr, /* enumerate */
|
||||
JS_ObjectToOuterObject /* thisObject */
|
||||
}
|
||||
"""
|
||||
classExtensionAndObjectOps = fill(
|
||||
"""
|
||||
{
|
||||
nsGlobalWindow::OuterObject, /* outerObject */
|
||||
nullptr, /* innerObject */
|
||||
nullptr, /* iteratorObject */
|
||||
false, /* isWrappedNative */
|
||||
nullptr, /* weakmapKeyDelegateOp */
|
||||
${objectMoved} /* objectMovedOp */
|
||||
},
|
||||
{
|
||||
nullptr, /* lookupGeneric */
|
||||
nullptr, /* lookupProperty */
|
||||
nullptr, /* lookupElement */
|
||||
nullptr, /* defineGeneric */
|
||||
nullptr, /* defineProperty */
|
||||
nullptr, /* defineElement */
|
||||
nullptr, /* getGeneric */
|
||||
nullptr, /* getProperty */
|
||||
nullptr, /* getElement */
|
||||
nullptr, /* setGeneric */
|
||||
nullptr, /* setProperty */
|
||||
nullptr, /* setElement */
|
||||
nullptr, /* getGenericAttributes */
|
||||
nullptr, /* setGenericAttributes */
|
||||
nullptr, /* deleteGeneric */
|
||||
nullptr, /* watch */
|
||||
nullptr, /* unwatch */
|
||||
nullptr, /* slice */
|
||||
nullptr, /* enumerate */
|
||||
JS_ObjectToOuterObject /* thisObject */
|
||||
}
|
||||
""",
|
||||
objectMoved=objectMovedHook)
|
||||
else:
|
||||
classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
|
||||
reservedSlots = slotCount
|
||||
@ -481,17 +495,24 @@ class CGDOMProxyJSClass(CGThing):
|
||||
# HTMLAllCollection. So just hardcode it here.
|
||||
if self.descriptor.interface.identifier.name == "HTMLAllCollection":
|
||||
flags.append("JSCLASS_EMULATES_UNDEFINED")
|
||||
objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
|
||||
return fill(
|
||||
"""
|
||||
static const DOMJSClass Class = {
|
||||
PROXY_CLASS_DEF("${name}",
|
||||
0, /* extra slots */
|
||||
${flags}),
|
||||
PROXY_CLASS_WITH_EXT("${name}",
|
||||
0, /* extra slots */
|
||||
${flags},
|
||||
PROXY_MAKE_EXT(nullptr, /* outerObject */
|
||||
nullptr, /* innerObject */
|
||||
nullptr, /* iteratorObject */
|
||||
false, /* isWrappedNative */
|
||||
${objectMoved})),
|
||||
$*{descriptor}
|
||||
};
|
||||
""",
|
||||
name=self.descriptor.interface.identifier.name,
|
||||
flags=" | ".join(flags),
|
||||
objectMoved=objectMovedHook,
|
||||
descriptor=DOMClass(self.descriptor))
|
||||
|
||||
|
||||
@ -1549,11 +1570,28 @@ class CGClassFinalizeHook(CGAbstractClassHook):
|
||||
return finalizeHook(self.descriptor, self.name, self.args[0].name).define()
|
||||
|
||||
|
||||
class CGClassObjectMovedHook(CGAbstractClassHook):
|
||||
"""
|
||||
A hook for objectMovedOp, used to update the wrapper cache when an object it
|
||||
is holding moves.
|
||||
"""
|
||||
def __init__(self, descriptor):
|
||||
args = [Argument('JSObject*', 'obj'), Argument('const JSObject*', 'old')]
|
||||
CGAbstractClassHook.__init__(self, descriptor, OBJECT_MOVED_HOOK_NAME,
|
||||
'void', args)
|
||||
|
||||
def generate_code(self):
|
||||
assert self.descriptor.wrapperCache
|
||||
return CGIfWrapper(CGGeneric("UpdateWrapper(self, self, obj, old);\n"),
|
||||
"self").define()
|
||||
|
||||
|
||||
def JSNativeArguments():
|
||||
return [Argument('JSContext*', 'cx'),
|
||||
Argument('unsigned', 'argc'),
|
||||
Argument('JS::Value*', 'vp')]
|
||||
|
||||
|
||||
class CGClassConstructor(CGAbstractStaticMethod):
|
||||
"""
|
||||
JS-visible constructor for our objects
|
||||
@ -10919,6 +10957,9 @@ class CGDescriptor(CGThing):
|
||||
# wants a custom hook.
|
||||
cgThings.append(CGClassFinalizeHook(descriptor))
|
||||
|
||||
if descriptor.concrete and descriptor.wrapperCache:
|
||||
cgThings.append(CGClassObjectMovedHook(descriptor))
|
||||
|
||||
if len(descriptor.permissions):
|
||||
for (k, v) in sorted(descriptor.permissions.items()):
|
||||
perms = CGList((CGGeneric('"%s",' % p) for p in k), joiner="\n")
|
||||
|
@ -341,6 +341,7 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
int32_t ContentParent::sNuwaPid = 0;
|
||||
bool ContentParent::sNuwaReady = false;
|
||||
#endif
|
||||
|
||||
@ -586,6 +587,7 @@ ContentParent::RunNuwaProcess()
|
||||
/* aIsNuwaProcess = */ true);
|
||||
nuwaProcess->Init();
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
sNuwaPid = nuwaProcess->Pid();
|
||||
sNuwaReady = false;
|
||||
#endif
|
||||
return nuwaProcess.forget();
|
||||
@ -1987,6 +1989,7 @@ ContentParent::~ContentParent()
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess()) {
|
||||
sNuwaReady = false;
|
||||
sNuwaPid = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -3678,6 +3681,12 @@ ContentParent::DoSendAsyncMessage(JSContext* aCx,
|
||||
if (aCpows && !GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
|
||||
return false;
|
||||
}
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (IsNuwaProcess() && IsNuwaReady()) {
|
||||
// Nuwa won't receive frame messages after it is frozen.
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return SendAsyncMessage(nsString(aMessage), data, cpows, Principal(aPrincipal));
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,10 @@ class ContentParent MOZ_FINAL : public PContentParent
|
||||
|
||||
public:
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
static int32_t NuwaPid() {
|
||||
return sNuwaPid;
|
||||
}
|
||||
|
||||
static bool IsNuwaReady() {
|
||||
return sNuwaReady;
|
||||
}
|
||||
@ -712,6 +716,7 @@ private:
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
static int32_t sNuwaPid;
|
||||
static bool sNuwaReady;
|
||||
#endif
|
||||
};
|
||||
|
@ -1898,8 +1898,9 @@ TabChild::RecvHandleDoubleTap(const CSSPoint& aPoint, const ScrollableLayerGuid&
|
||||
bool
|
||||
TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
|
||||
{
|
||||
TABC_LOG("Handling single tap at %s with %p %p %d\n",
|
||||
Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get(), mTouchEndCancelled);
|
||||
TABC_LOG("Handling single tap at %s on %s with %p %p %d\n",
|
||||
Stringify(aPoint).c_str(), Stringify(aGuid).c_str(), mGlobal.get(),
|
||||
mTabChildGlobal.get(), mTouchEndCancelled);
|
||||
|
||||
if (!mGlobal || !mTabChildGlobal) {
|
||||
return true;
|
||||
|
@ -20,7 +20,7 @@
|
||||
var test = new PeerConnectionTest();
|
||||
// TODO: Stop using old constraint-like RTCOptions soon (Bug 1064223).
|
||||
// Watch out for case-difference when fixing: { offerToReceiveVideo: true }
|
||||
test.setOfferOptions({ optional: [{ OfferToReceiveAudio: true }] });
|
||||
test.setOfferOptions({ optional: [{ OfferToReceiveVideo: true }] });
|
||||
test.run();
|
||||
});
|
||||
</script>
|
||||
|
@ -589,8 +589,15 @@ DOMStorageDBParent::Observe(const char* aTopic,
|
||||
const nsACString& aScopePrefix)
|
||||
{
|
||||
if (mIPCOpen) {
|
||||
mozilla::unused << SendObserve(nsDependentCString(aTopic),
|
||||
nsCString(aScopePrefix));
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (!(static_cast<ContentParent*>(Manager())->IsNuwaProcess() &&
|
||||
ContentParent::IsNuwaReady())) {
|
||||
#endif
|
||||
mozilla::unused << SendObserve(nsDependentCString(aTopic),
|
||||
nsCString(aScopePrefix));
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -345,47 +345,47 @@ DOMWifiManager.prototype = {
|
||||
case "WifiManager:onconnecting":
|
||||
this._currentNetwork = this._convertWifiNetwork(msg.network);
|
||||
this._connectionStatus = "connecting";
|
||||
this._fireStatusChangeEvent();
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
|
||||
case "WifiManager:onassociate":
|
||||
this._currentNetwork = this._convertWifiNetwork(msg.network);
|
||||
this._connectionStatus = "associated";
|
||||
this._fireStatusChangeEvent();
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
|
||||
case "WifiManager:onconnect":
|
||||
this._currentNetwork = this._convertWifiNetwork(msg.network);
|
||||
this._connectionStatus = "connected";
|
||||
this._fireStatusChangeEvent();
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
|
||||
case "WifiManager:ondisconnect":
|
||||
this._currentNetwork = null;
|
||||
this._connectionStatus = "disconnected";
|
||||
this._lastConnectionInfo = null;
|
||||
this._fireStatusChangeEvent();
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
|
||||
case "WifiManager:onwpstimeout":
|
||||
this._currentNetwork = null;
|
||||
this._connectionStatus = "wps-timedout";
|
||||
this._lastConnectionInfo = null;
|
||||
this._fireStatusChangeEvent();
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
|
||||
case "WifiManager:onwpsfail":
|
||||
this._currentNetwork = null;
|
||||
this._connectionStatus = "wps-failed";
|
||||
this._lastConnectionInfo = null;
|
||||
this._fireStatusChangeEvent();
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
|
||||
case "WifiManager:onwpsoverlap":
|
||||
this._currentNetwork = null;
|
||||
this._connectionStatus = "wps-overlapped";
|
||||
this._lastConnectionInfo = null;
|
||||
this._fireStatusChangeEvent();
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
|
||||
case "WifiManager:connectioninfoupdate":
|
||||
@ -396,12 +396,12 @@ DOMWifiManager.prototype = {
|
||||
this._currentNetwork = null;
|
||||
this._connectionStatus = "connectingfailed";
|
||||
this._lastConnectionInfo = null;
|
||||
this._fireStatusChangeEvent();
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
case "WifiManager:onauthenticating":
|
||||
this._currentNetwork = msg.network;
|
||||
this._currentNetwork = this._convertWifiNetwork(msg.network);
|
||||
this._connectionStatus = "authenticating";
|
||||
this._fireStatusChangeEvent();
|
||||
this._fireStatusChangeEvent(msg.network);
|
||||
break;
|
||||
case "WifiManager:stationinfoupdate":
|
||||
this._stationNumber = msg.station;
|
||||
@ -410,9 +410,9 @@ DOMWifiManager.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_fireStatusChangeEvent: function StatusChangeEvent() {
|
||||
_fireStatusChangeEvent: function StatusChangeEvent(aNetwork) {
|
||||
var event = new this._window.MozWifiStatusChangeEvent("statuschange",
|
||||
{ network: this._currentNetwork,
|
||||
{ network: this._convertWifiNetwork(aNetwork),
|
||||
status: this._connectionStatus
|
||||
});
|
||||
this.__DOM_IMPL__.dispatchEvent(event);
|
||||
|
@ -1842,6 +1842,9 @@ function WifiWorker() {
|
||||
// Given a connection status network, takes a network from
|
||||
// self.configuredNetworks and prepares it for the DOM.
|
||||
netToDOM = function(net) {
|
||||
if (!net) {
|
||||
return null;
|
||||
}
|
||||
var ssid = dequote(net.ssid);
|
||||
var security = (net.key_mgmt === "NONE" && net.wep_key0) ? ["WEP"] :
|
||||
(net.key_mgmt && net.key_mgmt !== "NONE") ? [net.key_mgmt.split(" ")[0]] :
|
||||
@ -2026,7 +2029,7 @@ function WifiWorker() {
|
||||
WifiManager.disableNetwork(netId, function() {});
|
||||
}
|
||||
});
|
||||
self._fireEvent("onconnectingfailed", {network: self.currentNetwork});
|
||||
self._fireEvent("onconnectingfailed", {network: netToDOM(self.currentNetwork)});
|
||||
}
|
||||
|
||||
WifiManager.onstatechange = function() {
|
||||
@ -2069,7 +2072,10 @@ function WifiWorker() {
|
||||
}
|
||||
|
||||
self.currentNetwork.netId = this.id;
|
||||
WifiManager.getNetworkConfiguration(self.currentNetwork, function (){});
|
||||
WifiManager.getNetworkConfiguration(self.currentNetwork, function (){
|
||||
// Notify again because we get complete network information.
|
||||
self._fireEvent("onconnecting", { network: netToDOM(self.currentNetwork) });
|
||||
});
|
||||
break;
|
||||
case "COMPLETED":
|
||||
// Now that we've successfully completed the connection, re-enable the
|
||||
@ -2082,27 +2088,32 @@ function WifiWorker() {
|
||||
self._needToEnableNetworks = false;
|
||||
}
|
||||
|
||||
var _oncompleted = function() {
|
||||
// Update http proxy when connected to network.
|
||||
let netConnect = WifiManager.getHttpProxyNetwork(self.currentNetwork);
|
||||
if (netConnect)
|
||||
WifiManager.setHttpProxy(netConnect);
|
||||
|
||||
// The full authentication process is completed, reset the count.
|
||||
WifiManager.authenticationFailuresCount = 0;
|
||||
WifiManager.loopDetectionCount = 0;
|
||||
self._startConnectionInfoTimer();
|
||||
self._fireEvent("onassociate", { network: netToDOM(self.currentNetwork) });
|
||||
};
|
||||
|
||||
// We get the ASSOCIATED event when we've associated but not connected, so
|
||||
// wait until the handshake is complete.
|
||||
if (this.fromStatus || !self.currentNetwork) {
|
||||
// In this case, we connected to an already-connected wpa_supplicant,
|
||||
// because of that we need to gather information about the current
|
||||
// network here.
|
||||
self.currentNetwork = { ssid: quote(WifiManager.connectionInfo.ssid),
|
||||
self.currentNetwork = { bssid: WifiManager.connectionInfo.bssid,
|
||||
ssid: quote(WifiManager.connectionInfo.ssid),
|
||||
netId: WifiManager.connectionInfo.id };
|
||||
WifiManager.getNetworkConfiguration(self.currentNetwork, function(){});
|
||||
WifiManager.getNetworkConfiguration(self.currentNetwork, _oncompleted);
|
||||
} else {
|
||||
_oncompleted();
|
||||
}
|
||||
|
||||
// Update http proxy when connected to network.
|
||||
let netConnect = WifiManager.getHttpProxyNetwork(self.currentNetwork);
|
||||
if (netConnect)
|
||||
WifiManager.setHttpProxy(netConnect);
|
||||
|
||||
// The full authentication process is completed, reset the count.
|
||||
WifiManager.authenticationFailuresCount = 0;
|
||||
WifiManager.loopDetectionCount = 0;
|
||||
self._startConnectionInfoTimer();
|
||||
self._fireEvent("onassociate", { network: netToDOM(self.currentNetwork) });
|
||||
break;
|
||||
case "CONNECTED":
|
||||
// BSSID is read after connected, update it.
|
||||
@ -2121,7 +2132,7 @@ function WifiWorker() {
|
||||
return;
|
||||
}
|
||||
|
||||
self._fireEvent("ondisconnect", {});
|
||||
self._fireEvent("ondisconnect", {network: netToDOM(self.currentNetwork)});
|
||||
|
||||
// When disconnected, clear the http proxy setting if it exists.
|
||||
// Temporarily set http proxy to empty and restore user setting after setHttpProxy.
|
||||
|
@ -152,6 +152,15 @@ AppendToString(std::stringstream& aStream, const FrameMetrics& m,
|
||||
aStream << sfx;
|
||||
}
|
||||
|
||||
void
|
||||
AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s,
|
||||
const char* pfx, const char* sfx)
|
||||
{
|
||||
aStream << pfx
|
||||
<< nsPrintfCString("{ l=%llu, p=%u, v=%llu }", s.mLayersId, s.mPresShellId, s.mScrollId).get()
|
||||
<< sfx;
|
||||
}
|
||||
|
||||
void
|
||||
AppendToString(std::stringstream& aStream, const Matrix4x4& m,
|
||||
const char* pfx, const char* sfx)
|
||||
|
@ -97,6 +97,10 @@ void
|
||||
AppendToString(std::stringstream& aStream, const FrameMetrics& m,
|
||||
const char* pfx="", const char* sfx="", bool detailed = false);
|
||||
|
||||
void
|
||||
AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s,
|
||||
const char* pfx="", const char* sfx="");
|
||||
|
||||
template<class T>
|
||||
void
|
||||
AppendToString(std::stringstream& aStream, const mozilla::gfx::MarginTyped<T>& m,
|
||||
|
@ -2750,7 +2750,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
||||
// more "legitimate" sources like content scripts.
|
||||
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
|
||||
if (controller) {
|
||||
APZC_LOG("%p sending scroll update acknowledgement with gen %lu\n", this, aLayerMetrics.GetScrollGeneration());
|
||||
APZC_LOG("%p sending scroll update acknowledgement with gen %u\n", this, aLayerMetrics.GetScrollGeneration());
|
||||
controller->AcknowledgeScrollUpdate(aLayerMetrics.GetScrollId(),
|
||||
aLayerMetrics.GetScrollGeneration());
|
||||
}
|
||||
|
@ -292,6 +292,15 @@ public:
|
||||
sf->ResetScrollInfoIfGeneration(mScrollGeneration);
|
||||
}
|
||||
|
||||
// Since the APZ and content are in sync, we need to clear any callback transform
|
||||
// that might have been set on the last repaint request (which might have failed
|
||||
// due to the inflight scroll update that this message is acknowledging).
|
||||
nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(mScrollId);
|
||||
if (content) {
|
||||
content->SetProperty(nsGkAtoms::apzCallbackTransform, new CSSPoint(),
|
||||
nsINode::DeleteProperty<CSSPoint>);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -205,7 +205,6 @@
|
||||
#define SK_ALLOW_STATIC_GLOBAL_INITIALIZERS 0
|
||||
|
||||
#define SK_SUPPORT_LEGACY_GETDEVICE
|
||||
#define SK_SUPPORT_LEGACY_GETTOPDEVICE
|
||||
#define SK_IGNORE_ETC1_SUPPORT
|
||||
|
||||
#define SK_RASTERIZE_EVEN_ROUNDING
|
||||
|
@ -1106,10 +1106,9 @@ void imgLoader::GlobalInit()
|
||||
int32_t cachesize;
|
||||
rv = Preferences::GetInt("image.cache.size", &cachesize);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
sCacheMaxSize = cachesize;
|
||||
sCacheMaxSize = cachesize > 0 ? cachesize : 0;
|
||||
else
|
||||
sCacheMaxSize = 5 * 1024 * 1024;
|
||||
sCacheMaxSize = sCacheMaxSize > 0 ? sCacheMaxSize : 0;
|
||||
|
||||
sMemReporter = new imgMemoryReporter();
|
||||
RegisterStrongMemoryReporter(sMemReporter);
|
||||
|
@ -42,7 +42,7 @@ FileDescriptor::FileDescriptor(PlatformHandleType aHandle)
|
||||
void
|
||||
FileDescriptor::DuplicateInCurrentProcess(PlatformHandleType aHandle)
|
||||
{
|
||||
MOZ_ASSERT_IF(mHandleCreatedByOtherProcess,
|
||||
MOZ_ASSERT_IF(mHandleCreatedByOtherProcess && IsValid(),
|
||||
mHandleCreatedByOtherProcessWasUsed);
|
||||
|
||||
if (IsValid(aHandle)) {
|
||||
@ -65,7 +65,7 @@ FileDescriptor::DuplicateInCurrentProcess(PlatformHandleType aHandle)
|
||||
void
|
||||
FileDescriptor::CloseCurrentProcessHandle()
|
||||
{
|
||||
MOZ_ASSERT_IF(mHandleCreatedByOtherProcess,
|
||||
MOZ_ASSERT_IF(mHandleCreatedByOtherProcess && IsValid(),
|
||||
mHandleCreatedByOtherProcessWasUsed);
|
||||
|
||||
// Don't actually close handles created by another process.
|
||||
|
@ -13,6 +13,7 @@
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
#include "ipc/Nuwa.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
@ -70,6 +71,9 @@ ProcessLink::ProcessLink(MessageChannel *aChan)
|
||||
, mTransport(nullptr)
|
||||
, mIOLoop(nullptr)
|
||||
, mExistingListener(nullptr)
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
, mIsToNuwaProcess(false)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
@ -168,6 +172,20 @@ ProcessLink::SendMessage(Message *msg)
|
||||
mChan->AssertWorkerThread();
|
||||
mChan->mMonitor->AssertCurrentThreadOwns();
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
if (mIsToNuwaProcess && mozilla::dom::ContentParent::IsNuwaReady()) {
|
||||
switch (msg->type()) {
|
||||
case mozilla::dom::PContent::Msg_NuwaFork__ID:
|
||||
case mozilla::dom::PContent::Reply_AddNewProcess__ID:
|
||||
case mozilla::dom::PContent::Msg_NotifyPhoneStateChange__ID:
|
||||
case GOODBYE_MESSAGE_TYPE:
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
mIOLoop->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableMethod(mTransport, &Transport::Send, msg));
|
||||
@ -360,6 +378,10 @@ ProcessLink::OnChannelConnected(int32_t peer_pid)
|
||||
if (mExistingListener)
|
||||
mExistingListener->OnChannelConnected(peer_pid);
|
||||
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
mIsToNuwaProcess = (peer_pid == mozilla::dom::ContentParent::NuwaPid());
|
||||
#endif
|
||||
|
||||
if (notifyChannel) {
|
||||
mChan->OnChannelConnected(peer_pid);
|
||||
}
|
||||
|
@ -170,6 +170,9 @@ class ProcessLink
|
||||
Transport* mTransport;
|
||||
MessageLoop* mIOLoop; // thread where IO happens
|
||||
Transport::Listener* mExistingListener; // channel's previous listener
|
||||
#ifdef MOZ_NUWA_PROCESS
|
||||
bool mIsToNuwaProcess;
|
||||
#endif
|
||||
};
|
||||
|
||||
class ThreadLink : public MessageLink
|
||||
|
@ -393,10 +393,32 @@ class JS_PUBLIC_API(AutoAssertOnGC)
|
||||
};
|
||||
|
||||
/*
|
||||
* Disable the static rooting hazard analysis in the live region, but assert if
|
||||
* any GC occurs while this guard object is live. This is most useful to help
|
||||
* the exact rooting hazard analysis in complex regions, since it cannot
|
||||
* understand dataflow.
|
||||
* Assert if an allocation of a GC thing occurs while this class is live. This
|
||||
* class does not disable the static rooting hazard analysis.
|
||||
*/
|
||||
class JS_PUBLIC_API(AutoAssertNoAlloc)
|
||||
{
|
||||
#ifdef JS_DEBUG
|
||||
js::gc::GCRuntime *gc;
|
||||
|
||||
public:
|
||||
AutoAssertNoAlloc() : gc(nullptr) {}
|
||||
explicit AutoAssertNoAlloc(JSRuntime *rt);
|
||||
void disallowAlloc(JSRuntime *rt);
|
||||
~AutoAssertNoAlloc();
|
||||
#else
|
||||
public:
|
||||
AutoAssertNoAlloc() {}
|
||||
explicit AutoAssertNoAlloc(JSRuntime *rt) {}
|
||||
void disallowAlloc(JSRuntime *rt) {}
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Disable the static rooting hazard analysis in the live region and assert if
|
||||
* any allocation that could potentially trigger a GC occurs while this guard
|
||||
* object is live. This is most useful to help the exact rooting hazard analysis
|
||||
* in complex regions, since it cannot understand dataflow.
|
||||
*
|
||||
* Note: GC behavior is unpredictable even when deterministice and is generally
|
||||
* non-deterministic in practice. The fact that this guard has not
|
||||
@ -406,11 +428,25 @@ class JS_PUBLIC_API(AutoAssertOnGC)
|
||||
* that the hazard analysis is correct for that code, rather than relying
|
||||
* on this class.
|
||||
*/
|
||||
class JS_PUBLIC_API(AutoSuppressGCAnalysis) : public AutoAssertOnGC
|
||||
class JS_PUBLIC_API(AutoSuppressGCAnalysis) : public AutoAssertNoAlloc
|
||||
{
|
||||
public:
|
||||
AutoSuppressGCAnalysis() : AutoAssertOnGC() {}
|
||||
explicit AutoSuppressGCAnalysis(JSRuntime *rt) : AutoAssertOnGC(rt) {}
|
||||
AutoSuppressGCAnalysis() : AutoAssertNoAlloc() {}
|
||||
explicit AutoSuppressGCAnalysis(JSRuntime *rt) : AutoAssertNoAlloc(rt) {}
|
||||
};
|
||||
|
||||
/*
|
||||
* Assert that code is only ever called from a GC callback, disable the static
|
||||
* rooting hazard analysis and assert if any allocation that could potentially
|
||||
* trigger a GC occurs while this guard object is live.
|
||||
*
|
||||
* This is useful to make the static analysis ignore code that runs in GC
|
||||
* callbacks.
|
||||
*/
|
||||
class JS_PUBLIC_API(AutoAssertGCCallback) : public AutoSuppressGCAnalysis
|
||||
{
|
||||
public:
|
||||
explicit AutoAssertGCCallback(JSObject *obj);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -360,6 +360,10 @@ ValidateSimdOperation(JSContext *cx, AsmJSModule::Global &global, HandleValue gl
|
||||
case AsmJSSimdOperation_xor: native = simd_int32x4_xor; break;
|
||||
case AsmJSSimdOperation_select: native = simd_int32x4_select; break;
|
||||
case AsmJSSimdOperation_splat: native = simd_int32x4_splat; break;
|
||||
case AsmJSSimdOperation_withX: native = simd_int32x4_withX; break;
|
||||
case AsmJSSimdOperation_withY: native = simd_int32x4_withY; break;
|
||||
case AsmJSSimdOperation_withZ: native = simd_int32x4_withZ; break;
|
||||
case AsmJSSimdOperation_withW: native = simd_int32x4_withW; break;
|
||||
case AsmJSSimdOperation_lessThanOrEqual:
|
||||
case AsmJSSimdOperation_greaterThanOrEqual:
|
||||
case AsmJSSimdOperation_notEqual:
|
||||
@ -390,6 +394,10 @@ ValidateSimdOperation(JSContext *cx, AsmJSModule::Global &global, HandleValue gl
|
||||
case AsmJSSimdOperation_xor: native = simd_float32x4_xor; break;
|
||||
case AsmJSSimdOperation_select: native = simd_float32x4_select; break;
|
||||
case AsmJSSimdOperation_splat: native = simd_float32x4_splat; break;
|
||||
case AsmJSSimdOperation_withX: native = simd_float32x4_withX; break;
|
||||
case AsmJSSimdOperation_withY: native = simd_float32x4_withY; break;
|
||||
case AsmJSSimdOperation_withZ: native = simd_float32x4_withZ; break;
|
||||
case AsmJSSimdOperation_withW: native = simd_float32x4_withW; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -92,7 +92,11 @@ enum AsmJSSimdOperation
|
||||
AsmJSSimdOperation_or,
|
||||
AsmJSSimdOperation_xor,
|
||||
AsmJSSimdOperation_select,
|
||||
AsmJSSimdOperation_splat
|
||||
AsmJSSimdOperation_splat,
|
||||
AsmJSSimdOperation_withX,
|
||||
AsmJSSimdOperation_withY,
|
||||
AsmJSSimdOperation_withZ,
|
||||
AsmJSSimdOperation_withW
|
||||
};
|
||||
|
||||
// These labels describe positions in the prologue/epilogue of functions while
|
||||
|
@ -1423,7 +1423,11 @@ class MOZ_STACK_CLASS ModuleCompiler
|
||||
!addStandardLibrarySimdOpName("select", AsmJSSimdOperation_select) ||
|
||||
!addStandardLibrarySimdOpName("splat", AsmJSSimdOperation_splat) ||
|
||||
!addStandardLibrarySimdOpName("max", AsmJSSimdOperation_max) ||
|
||||
!addStandardLibrarySimdOpName("min", AsmJSSimdOperation_min))
|
||||
!addStandardLibrarySimdOpName("min", AsmJSSimdOperation_min) ||
|
||||
!addStandardLibrarySimdOpName("withX", AsmJSSimdOperation_withX) ||
|
||||
!addStandardLibrarySimdOpName("withY", AsmJSSimdOperation_withY) ||
|
||||
!addStandardLibrarySimdOpName("withZ", AsmJSSimdOperation_withZ) ||
|
||||
!addStandardLibrarySimdOpName("withW", AsmJSSimdOperation_withW))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -2478,6 +2482,18 @@ class FunctionCompiler
|
||||
return ins;
|
||||
}
|
||||
|
||||
MDefinition *insertElementSimd(MDefinition *vec, MDefinition *val, SimdLane lane, MIRType type)
|
||||
{
|
||||
if (inDeadCode())
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(IsSimdType(vec->type()) && vec->type() == type);
|
||||
MOZ_ASSERT(!IsSimdType(val->type()));
|
||||
MSimdInsertElement *ins = MSimdInsertElement::NewAsmJS(alloc(), vec, val, type, lane);
|
||||
curBlock_->add(ins);
|
||||
return ins;
|
||||
}
|
||||
|
||||
MDefinition *ternarySimd(MDefinition *mask, MDefinition *lhs, MDefinition *rhs,
|
||||
MSimdTernaryBitwise::Operation op, MIRType type)
|
||||
{
|
||||
@ -3567,6 +3583,10 @@ IsSimdValidOperationType(AsmJSSimdType type, AsmJSSimdOperation op)
|
||||
case AsmJSSimdOperation_xor:
|
||||
case AsmJSSimdOperation_select:
|
||||
case AsmJSSimdOperation_splat:
|
||||
case AsmJSSimdOperation_withX:
|
||||
case AsmJSSimdOperation_withY:
|
||||
case AsmJSSimdOperation_withZ:
|
||||
case AsmJSSimdOperation_withW:
|
||||
return true;
|
||||
case AsmJSSimdOperation_mul:
|
||||
case AsmJSSimdOperation_div:
|
||||
@ -4353,13 +4373,21 @@ CheckMathMinMax(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, boo
|
||||
if (!CheckExpr(f, firstArg, &firstDef, &firstType))
|
||||
return false;
|
||||
|
||||
bool opIsDouble = firstType.isMaybeDouble();
|
||||
bool opIsInteger = firstType.isInt();
|
||||
if (firstType.isMaybeDouble()) {
|
||||
*type = MathRetType::Double;
|
||||
firstType = Type::MaybeDouble;
|
||||
} else if (firstType.isMaybeFloat()) {
|
||||
*type = MathRetType::Float;
|
||||
firstType = Type::MaybeFloat;
|
||||
} else if (firstType.isInt()) {
|
||||
*type = MathRetType::Signed;
|
||||
firstType = Type::Int;
|
||||
} else {
|
||||
return f.failf(firstArg, "%s is not a subtype of double?, float? or int",
|
||||
firstType.toChars());
|
||||
}
|
||||
|
||||
MIRType opType = firstType.toMIRType();
|
||||
|
||||
if (!opIsDouble && !opIsInteger)
|
||||
return f.failf(firstArg, "%s is not a subtype of double? or int", firstType.toChars());
|
||||
|
||||
MDefinition *lastDef = firstDef;
|
||||
ParseNode *nextArg = NextNode(firstArg);
|
||||
for (unsigned i = 1; i < CallArgListLength(callNode); i++, nextArg = NextNode(nextArg)) {
|
||||
@ -4368,15 +4396,12 @@ CheckMathMinMax(FunctionCompiler &f, ParseNode *callNode, MDefinition **def, boo
|
||||
if (!CheckExpr(f, nextArg, &nextDef, &nextType))
|
||||
return false;
|
||||
|
||||
if (opIsDouble && !nextType.isMaybeDouble())
|
||||
return f.failf(nextArg, "%s is not a subtype of double?", nextType.toChars());
|
||||
if (opIsInteger && !nextType.isInt())
|
||||
return f.failf(nextArg, "%s is not a subtype of int", nextType.toChars());
|
||||
if (!(nextType <= firstType))
|
||||
return f.failf(nextArg, "%s is not a subtype of %s", nextType.toChars(), firstType.toChars());
|
||||
|
||||
lastDef = f.minMax(lastDef, nextDef, opType, isMax);
|
||||
}
|
||||
|
||||
*type = MathRetType(opIsDouble ? MathRetType::Double : MathRetType::Signed);
|
||||
*def = lastDef;
|
||||
return true;
|
||||
}
|
||||
@ -4818,6 +4843,35 @@ class CheckSimdSelectArgs
|
||||
}
|
||||
};
|
||||
|
||||
class CheckSimdVectorScalarArgs
|
||||
{
|
||||
Type formalType_;
|
||||
|
||||
public:
|
||||
explicit CheckSimdVectorScalarArgs(Type t) : formalType_(t) {}
|
||||
|
||||
bool operator()(FunctionCompiler &f, ParseNode *arg, unsigned argIndex, Type actualType) const
|
||||
{
|
||||
MOZ_ASSERT(argIndex < 2);
|
||||
if (argIndex == 0) {
|
||||
// First argument is the vector
|
||||
if (!(actualType <= formalType_)) {
|
||||
return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
|
||||
formalType_.toChars());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Second argument is the scalar
|
||||
Type coercedFormalType = formalType_.simdToCoercedScalarType();
|
||||
if (!(actualType <= coercedFormalType)) {
|
||||
return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
|
||||
coercedFormalType.toChars());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
template<class OpEnum>
|
||||
@ -4847,6 +4901,18 @@ CheckSimdBinary<MSimdBinaryComp::Operation>(FunctionCompiler &f, ParseNode *call
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckSimdWith(FunctionCompiler &f, ParseNode *call, Type retType, SimdLane lane, MDefinition **def,
|
||||
Type *type)
|
||||
{
|
||||
DefinitionVector defs;
|
||||
if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(retType), &defs))
|
||||
return false;
|
||||
*def = f.insertElementSimd(defs[0], defs[1], lane, retType.toMIRType());
|
||||
*type = retType;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global,
|
||||
MDefinition **def, Type *type)
|
||||
@ -4889,6 +4955,15 @@ CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompile
|
||||
case AsmJSSimdOperation_xor:
|
||||
return CheckSimdBinary(f, call, retType, MSimdBinaryBitwise::xor_, def, type);
|
||||
|
||||
case AsmJSSimdOperation_withX:
|
||||
return CheckSimdWith(f, call, retType, SimdLane::LaneX, def, type);
|
||||
case AsmJSSimdOperation_withY:
|
||||
return CheckSimdWith(f, call, retType, SimdLane::LaneY, def, type);
|
||||
case AsmJSSimdOperation_withZ:
|
||||
return CheckSimdWith(f, call, retType, SimdLane::LaneZ, def, type);
|
||||
case AsmJSSimdOperation_withW:
|
||||
return CheckSimdWith(f, call, retType, SimdLane::LaneW, def, type);
|
||||
|
||||
case AsmJSSimdOperation_splat: {
|
||||
DefinitionVector defs;
|
||||
Type formalType = retType.simdToCoercedScalarType();
|
||||
|
@ -222,7 +222,7 @@ GC(JSContext *cx, unsigned argc, jsval *vp)
|
||||
* scheduled for GC). Otherwise, we collect all compartments.
|
||||
*/
|
||||
bool compartment = false;
|
||||
if (args.length() == 1) {
|
||||
if (args.length() >= 1) {
|
||||
Value arg = args[0];
|
||||
if (arg.isString()) {
|
||||
if (!JS_StringEqualsAscii(cx, arg.toString(), "compartment", &compartment))
|
||||
@ -233,6 +233,15 @@ GC(JSContext *cx, unsigned argc, jsval *vp)
|
||||
}
|
||||
}
|
||||
|
||||
bool shrinking = false;
|
||||
if (args.length() >= 2) {
|
||||
Value arg = args[1];
|
||||
if (arg.isString()) {
|
||||
if (!JS_StringEqualsAscii(cx, arg.toString(), "shrinking", &shrinking))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef JS_MORE_DETERMINISTIC
|
||||
size_t preBytes = cx->runtime()->gc.usage.gcBytes();
|
||||
#endif
|
||||
@ -241,7 +250,11 @@ GC(JSContext *cx, unsigned argc, jsval *vp)
|
||||
PrepareForDebugGC(cx->runtime());
|
||||
else
|
||||
PrepareForFullGC(cx->runtime());
|
||||
GCForReason(cx->runtime(), gcreason::API);
|
||||
|
||||
if (shrinking)
|
||||
ShrinkingGC(cx->runtime(), gcreason::API);
|
||||
else
|
||||
GCForReason(cx->runtime(), gcreason::API);
|
||||
|
||||
char buf[256] = { '\0' };
|
||||
#ifndef JS_MORE_DETERMINISTIC
|
||||
@ -2001,10 +2014,12 @@ IsSimdAvailable(JSContext *cx, unsigned argc, Value *vp)
|
||||
|
||||
static const JSFunctionSpecWithHelp TestingFunctions[] = {
|
||||
JS_FN_HELP("gc", ::GC, 0, 0,
|
||||
"gc([obj] | 'compartment')",
|
||||
"gc([obj] | 'compartment' [, 'shrinking'])",
|
||||
" Run the garbage collector. When obj is given, GC only its compartment.\n"
|
||||
" If 'compartment' is given, GC any compartments that were scheduled for\n"
|
||||
" GC via schedulegc."),
|
||||
" GC via schedulegc.\n"
|
||||
" If 'shrinking' is passes as the optional second argument, perform a\n"
|
||||
" shrinking GC rather than a normal GC."),
|
||||
|
||||
JS_FN_HELP("minorgc", ::MinorGC, 0, 0,
|
||||
"minorgc([aboutToOverflow])",
|
||||
|
@ -2889,38 +2889,6 @@ js::ClampToUint8(ThreadSafeContext *, unsigned argc, Value *vp)
|
||||
JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ClampToUint8JitInfo, ClampToUint8JitInfo,
|
||||
js::ClampToUint8);
|
||||
|
||||
bool
|
||||
js::Memcpy(ThreadSafeContext *, unsigned argc, Value *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
JS_ASSERT(args.length() == 5);
|
||||
JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
|
||||
JS_ASSERT(args[1].isInt32());
|
||||
JS_ASSERT(args[2].isObject() && args[2].toObject().is<TypedObject>());
|
||||
JS_ASSERT(args[3].isInt32());
|
||||
JS_ASSERT(args[4].isInt32());
|
||||
|
||||
TypedObject &targetTypedObj = args[0].toObject().as<TypedObject>();
|
||||
int32_t targetOffset = args[1].toInt32();
|
||||
TypedObject &sourceTypedObj = args[2].toObject().as<TypedObject>();
|
||||
int32_t sourceOffset = args[3].toInt32();
|
||||
int32_t size = args[4].toInt32();
|
||||
|
||||
JS_ASSERT(targetOffset >= 0);
|
||||
JS_ASSERT(sourceOffset >= 0);
|
||||
JS_ASSERT(size >= 0);
|
||||
JS_ASSERT(size + targetOffset <= targetTypedObj.size());
|
||||
JS_ASSERT(size + sourceOffset <= sourceTypedObj.size());
|
||||
|
||||
uint8_t *target = targetTypedObj.typedMem(targetOffset);
|
||||
uint8_t *source = sourceTypedObj.typedMem(sourceOffset);
|
||||
memcpy(target, source, size);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::MemcpyJitInfo, MemcpyJitInfo, js::Memcpy);
|
||||
|
||||
bool
|
||||
js::GetTypedObjectModule(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
|
@ -851,20 +851,6 @@ extern const JSJitInfo TypedObjectIsAttachedJitInfo;
|
||||
bool ClampToUint8(ThreadSafeContext *cx, unsigned argc, Value *vp);
|
||||
extern const JSJitInfo ClampToUint8JitInfo;
|
||||
|
||||
/*
|
||||
* Usage: Memcpy(targetDatum, targetOffset,
|
||||
* sourceDatum, sourceOffset,
|
||||
* size)
|
||||
*
|
||||
* Intrinsic function. Copies size bytes from the data for
|
||||
* `sourceDatum` at `sourceOffset` into the data for
|
||||
* `targetDatum` at `targetOffset`.
|
||||
*
|
||||
* Both `sourceDatum` and `targetDatum` must be attached.
|
||||
*/
|
||||
bool Memcpy(ThreadSafeContext *cx, unsigned argc, Value *vp);
|
||||
extern const JSJitInfo MemcpyJitInfo;
|
||||
|
||||
/*
|
||||
* Usage: GetTypedObjectModule()
|
||||
*
|
||||
|
@ -195,20 +195,6 @@ function TypedObjectSet(descr, typedObj, offset, fromValue) {
|
||||
if (!TypedObjectIsAttached(typedObj))
|
||||
ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
|
||||
|
||||
// Fast path: `fromValue` is a typed object with same type
|
||||
// representation as the destination. In that case, we can just do a
|
||||
// memcpy.
|
||||
if (IsObject(fromValue) && ObjectIsTypedObject(fromValue)) {
|
||||
if (!descr.variable && DescrsEquiv(descr, TypedObjectTypeDescr(fromValue))) {
|
||||
if (!TypedObjectIsAttached(fromValue))
|
||||
ThrowError(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
|
||||
|
||||
var size = DESCR_SIZE(descr);
|
||||
Memcpy(typedObj, offset, fromValue, 0, size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (DESCR_KIND(descr)) {
|
||||
case JS_TYPEREPR_SCALAR_KIND:
|
||||
TypedObjectSetScalar(descr, typedObj, offset, fromValue);
|
||||
@ -343,13 +329,33 @@ function TypedObjectSetReference(descr, typedObj, offset, fromValue) {
|
||||
|
||||
// Sets `fromValue` to `this` assuming that `this` is a scalar type.
|
||||
function TypedObjectSetSimd(descr, typedObj, offset, fromValue) {
|
||||
// It is only permitted to set a float32x4/int32x4 value from another
|
||||
// float32x4/int32x4; in that case, the "fast path" that uses memcopy will
|
||||
// have already matched. So if we get to this point, we're supposed
|
||||
// to "adapt" fromValue, but there are no legal adaptions.
|
||||
ThrowError(JSMSG_CANT_CONVERT_TO,
|
||||
typeof(fromValue),
|
||||
DESCR_STRING_REPR(descr));
|
||||
if (!IsObject(fromValue) || !ObjectIsTypedObject(fromValue))
|
||||
ThrowError(JSMSG_CANT_CONVERT_TO,
|
||||
typeof(fromValue),
|
||||
DESCR_STRING_REPR(descr));
|
||||
|
||||
if (!DescrsEquiv(descr, TypedObjectTypeDescr(fromValue)))
|
||||
ThrowError(JSMSG_CANT_CONVERT_TO,
|
||||
typeof(fromValue),
|
||||
DESCR_STRING_REPR(descr));
|
||||
|
||||
var type = DESCR_TYPE(descr);
|
||||
switch (type) {
|
||||
case JS_SIMDTYPEREPR_FLOAT32:
|
||||
Store_float32(typedObj, offset + 0, Load_float32(fromValue, 0));
|
||||
Store_float32(typedObj, offset + 4, Load_float32(fromValue, 4));
|
||||
Store_float32(typedObj, offset + 8, Load_float32(fromValue, 8));
|
||||
Store_float32(typedObj, offset + 12, Load_float32(fromValue, 12));
|
||||
break;
|
||||
case JS_SIMDTYPEREPR_INT32:
|
||||
Store_int32(typedObj, offset + 0, Load_int32(fromValue, 0));
|
||||
Store_int32(typedObj, offset + 4, Load_int32(fromValue, 4));
|
||||
Store_int32(typedObj, offset + 8, Load_int32(fromValue, 8));
|
||||
Store_int32(typedObj, offset + 12, Load_int32(fromValue, 12));
|
||||
break;
|
||||
default:
|
||||
assert(false, "Unhandled Simd type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -222,6 +222,7 @@ function isRootedPointerTypeName(name)
|
||||
function isSuppressConstructor(name)
|
||||
{
|
||||
return name.indexOf("::AutoSuppressGC") != -1
|
||||
|| name.indexOf("::AutoAssertGCCallback") != -1
|
||||
|| name.indexOf("::AutoEnterAnalysis") != -1
|
||||
|| name.indexOf("::AutoSuppressGCAnalysis") != -1
|
||||
|| name.indexOf("::AutoIgnoreRootingHazards") != -1;
|
||||
|
@ -138,7 +138,7 @@ CheckHashTablesAfterMovingGC(JSRuntime *rt);
|
||||
|
||||
#ifdef JSGC_COMPACTING
|
||||
struct MovingTracer : JSTracer {
|
||||
MovingTracer(JSRuntime *rt) : JSTracer(rt, Visit, TraceWeakMapValues) {}
|
||||
MovingTracer(JSRuntime *rt) : JSTracer(rt, Visit, TraceWeakMapKeysValues) {}
|
||||
|
||||
static void Visit(JSTracer *jstrc, void **thingp, JSGCTraceKind kind);
|
||||
static void Sweep(JSTracer *jstrc);
|
||||
|
@ -624,6 +624,8 @@ struct ArenaHeader : public JS::shadow::ArenaHeader
|
||||
inline ArenaHeader *getNextAllocDuringSweep() const;
|
||||
inline void setNextAllocDuringSweep(ArenaHeader *aheader);
|
||||
inline void unsetAllocDuringSweep();
|
||||
|
||||
void unmarkAll();
|
||||
};
|
||||
|
||||
struct Arena
|
||||
|
@ -66,7 +66,10 @@ for (n of [0, 1, 2, 15, 16, Math.pow(2,31)-1, Math.pow(2,31), Math.pow(2,31)+1,
|
||||
assertEq(f(n), Math.clz32(n|0));
|
||||
|
||||
var doubleNumbers = [NaN, Infinity, -Infinity, -10000, -3.4, -0, 0, 3.4, 10000];
|
||||
var floatNumbers = [];
|
||||
for (var x of doubleNumbers) floatNumbers.push(Math.fround(x));
|
||||
var intNumbers = [-10000, -3, -1, 0, 3, 10000];
|
||||
|
||||
function testBinary(f, g, numbers) {
|
||||
for (n of numbers)
|
||||
for (o of numbers)
|
||||
@ -82,7 +85,6 @@ assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var at=glob.Math.atan2; function f(d,e) { d=+d;e=+e; return +at(d,e) } return f'), {Math:{atan2:Math.atan2}}), Math.atan2, doubleNumbers);
|
||||
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; return +min(d) } return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var f32=glob.Math.fround; var min=glob.Math.min; function f(d) { d=f32(d); return +min(d, f32(5)) } return f');
|
||||
assertAsmTypeFail('glob', 'ffi', 'heap', USE_ASM + 'var i32=new glob.Int32Array(heap); var min=glob.Math.min; function f() { return min(i32[0], 5)|0 } return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|0; return min(3 + x, 5)|0 } return f');
|
||||
assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|0; return min(5, 3 + x)|0 } return f');
|
||||
@ -90,11 +92,13 @@ assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(x) { x=x|
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:Math.sin}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:null}});
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=+d;e=+e; return +min(d,e) } return f'), {Math:{min:Math.min}}), Math.min, doubleNumbers);
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; var f32=glob.Math.fround; function f(d,e) { d=f32(d);e=f32(e); return f32(min(d,e)) } return f'), this), Math.min, floatNumbers);
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e) { d=d|0;e=e|0; return min(d,e)|0} return f'), {Math:{min:Math.min}}), Math.min, intNumbers);
|
||||
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:Math.sin}});
|
||||
assertAsmLinkFail(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:null}});
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=+d;e=+e; return +max(d,e) } return f'), {Math:{max:Math.max}}), Math.max, doubleNumbers);
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; var f32=glob.Math.fround; function f(d,e) { d=f32(d);e=f32(e); return f32(max(d,e)) } return f'), this), Math.max, floatNumbers);
|
||||
testBinary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e) { d=d|0;e=e|0; return max(d,e)|0} return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
|
||||
|
||||
function testTernary(f, g, numbers) {
|
||||
@ -110,8 +114,10 @@ assertAsmTypeFail('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) {
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return +max(d,e,g) } return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return max(d,e,g)|0 } return f'), {Math:{max:Math.max}}), Math.max, intNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d,e,g) { d=+d;e=+e;g=+g; return +max(d,e,g) } return f'), {Math:{max:Math.max}}), Math.max, doubleNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; var _=glob.Math.fround; function f(d,e,g) { d=_(d);e=_(e);g=_(g); return _(max(d,e,g)) } return f'), this), Math.max, floatNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=d|0;e=e|0;g=g|0; return min(d,e,g)|0 } return f'), {Math:{min:Math.min}}), Math.min, intNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d,e,g) { d=+d;e=+e;g=+g; return +min(d,e,g) } return f'), {Math:{min:Math.min}}), Math.min, doubleNumbers);
|
||||
testTernary(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; var _=glob.Math.fround; function f(d,e,g) { d=_(d);e=_(e);g=_(g); return _(min(d,e,g)) } return f'), this), Math.min, floatNumbers);
|
||||
|
||||
// Implicit return coercions of math functions
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i) { i=i|0; i = im(i,i); return i|0 } return f'), this)(3), 9);
|
||||
@ -134,7 +140,7 @@ assertAsmTypeFail('glob', USE_ASM + 'var sqrt=glob.Math.sqrt; function f(n) { n=
|
||||
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; d = min(d, 13.); return +d } return f'), this)(12), 12);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=d|0; d = min(d, 11); return d|0 } return f'), this)(12), 11);
|
||||
assertAsmTypeFail('glob', USE_ASM + FROUND + 'var max=glob.Math.max; function f(d) { d=fround(d); d = max(d) } return f');
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(d) { d=fround(d); d = min(d, fround(13.37)); return fround(d) } return f'), this)(14), Math.fround(13.37));
|
||||
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var sin=glob.Math.sin; function f(d) { d=+d; d = sin(d); return +d } return f'), this)(Math.PI), Math.sin(Math.PI));
|
||||
assertAsmTypeFail('glob', USE_ASM + FROUND + 'var sin=glob.Math.sin; function f(d) { d=fround(d); d = sin(d) } return f');
|
||||
@ -153,9 +159,13 @@ assertAsmTypeFail('glob', USE_ASM + 'var abs=glob.Math.abs; function f(d) { d=+d
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var im=glob.Math.imul; function f(i) { i=i|0; var d=0.0; d = +im(i,i); return +d } return f'), this)(42), Math.imul(42, 42));
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; var d=0.0; d = +abs(i|0); return +d } return f'), this)(-42), 42);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(i) { i=i|0; var d=0.0; d = +min(i, 0); return +d } return f'), this)(-42), -42);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(i) { i=i|0; var d=fround(0); d = fround(min(i, 0)); return +d } return f'), this)(-42), -42);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(i) { i=i|0; var d=0.0; d = +max(i, 0); return +d } return f'), this)(-42), 0);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var max=glob.Math.max; function f(i) { i=i|0; var d=fround(0); d = fround(max(i, 0)); return +d } return f'), this)(-42), 0);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var min=glob.Math.min; function f(d) { d=+d; var i=0; i = ~~min(d, 0.)|0; return i|0 } return f'), this)(-42), -42);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var min=glob.Math.min; function f(d) { d=fround(d); var i=0; i = ~~min(d, fround(0))|0; return i|0 } return f'), this)(-42), -42);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var max=glob.Math.max; function f(d) { d=+d; var i=0; i = ~~max(d, 0.)|0; return i|0 } return f'), this)(-42), 0);
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + FROUND + 'var max=glob.Math.max; function f(d) { d=fround(d); var i=0; i = ~~max(d, fround(0))|0; return i|0 } return f'), this)(-42), 0);
|
||||
|
||||
assertEq(asmLink(asmCompile('glob', USE_ASM + 'var abs=glob.Math.abs; function f(i) { i=i|0; var d=0.0; return +d; +abs(i|0); return 3.0;} return f'), this)(-42), 0);
|
||||
|
||||
|
@ -477,6 +477,35 @@ var f32x4 = SIMD.float32x4(0, 0, -0, NaN);
|
||||
var another = SIMD.float32x4(0, -0, 0, 0);
|
||||
assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32D + "function f(x,y) {x=f4(x); y=f4(y); x=f4d(x,y); return f4(x);} return f"), this)(f32x4, another), [NaN, NaN, NaN, NaN]);
|
||||
|
||||
// With
|
||||
const WXF = 'var w = f4.withX;';
|
||||
const WYF = 'var w = f4.withY;';
|
||||
const WZF = 'var w = f4.withZ;';
|
||||
const WWF = 'var w = f4.withW;';
|
||||
|
||||
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, 1);} return f");
|
||||
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, 1.0);} return f");
|
||||
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + "function f() {var x = f4(1,2,3,4); x = w(x, x);} return f");
|
||||
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(1, f32(1));} return f");
|
||||
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(1., f32(1));} return f");
|
||||
assertAsmTypeFail('glob', USE_ASM + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); x = w(f32(1), f32(1));} return f");
|
||||
assertAsmTypeFail('glob', USE_ASM + I32 + F32 + WXF + FROUND + "function f() {var x = f4(1,2,3,4); var y = i4(1,2,3,4); x = w(y, f32(1));} return f");
|
||||
|
||||
CheckF4(WXF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [Math.fround(13.37), 2, 3, 4]);
|
||||
CheckF4(WYF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, Math.fround(13.37), 3, 4]);
|
||||
CheckF4(WZF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, 2, Math.fround(13.37), 4]);
|
||||
CheckF4(WWF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37));', [1, 2, 3, Math.fround(13.37)]);
|
||||
CheckF4(WWF + FROUND, 'var x = f4(1,2,3,4); x = w(x, f32(13.37) + f32(6.63));', [1, 2, 3, Math.fround(Math.fround(13.37) + Math.fround(6.63))]);
|
||||
|
||||
const WXI = 'var w = i4.withX;';
|
||||
const WYI = 'var w = i4.withY;';
|
||||
const WZI = 'var w = i4.withZ;';
|
||||
const WWI = 'var w = i4.withW;';
|
||||
CheckI4(WXI, 'var x = i4(1,2,3,4); x = w(x, 42);', [42, 2, 3, 4]);
|
||||
CheckI4(WYI, 'var x = i4(1,2,3,4); x = w(x, 42);', [1, 42, 3, 4]);
|
||||
CheckI4(WZI, 'var x = i4(1,2,3,4); x = w(x, 42);', [1, 2, 42, 4]);
|
||||
CheckI4(WWI, 'var x = i4(1,2,3,4); x = w(x, 42);', [1, 2, 3, 42]);
|
||||
|
||||
// Comparisons
|
||||
// True yields all bits set to 1 (i.e as an int32, 0xFFFFFFFF === -1), false
|
||||
// yields all bits set to 0 (i.e 0).
|
||||
|
@ -461,6 +461,14 @@ function rmin_number(i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
var uceFault_min_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_min_float'));
|
||||
function rmin_float(i) {
|
||||
var x = Math.fround(Math.min(Math.fround(20), Math.fround(13.37)));
|
||||
if (uceFault_min_number(i) || uceFault_min_number(i))
|
||||
assertEq(x, Math.fround(13.37));
|
||||
return i;
|
||||
}
|
||||
|
||||
var uceFault_min_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_min_object'));
|
||||
function rmin_object(i) {
|
||||
var t = i;
|
||||
@ -480,6 +488,14 @@ function rmax_number(i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
var uceFault_max_float = eval(uneval(uceFault).replace('uceFault', 'uceFault_max_float'));
|
||||
function rmax_float(i) {
|
||||
var x = Math.fround(Math.max(Math.fround(2), Math.fround(13.37)));
|
||||
if (uceFault_max_number(i) || uceFault_max_number(i))
|
||||
assertEq(x, Math.fround(13.37));
|
||||
return i;
|
||||
}
|
||||
|
||||
var uceFault_max_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_max_object'));
|
||||
function rmax_object(i) {
|
||||
var t = i;
|
||||
@ -975,8 +991,10 @@ for (i = 0; i < 100; i++) {
|
||||
rpowhalf_number(i);
|
||||
rpowhalf_object(i);
|
||||
rmin_number(i);
|
||||
rmin_float(i);
|
||||
rmin_object(i);
|
||||
rmax_number(i);
|
||||
rmax_float(i);
|
||||
rmax_object(i);
|
||||
rabs_number(i);
|
||||
rabs_object(i);
|
||||
|
@ -89,6 +89,38 @@ function sqrt() {
|
||||
}
|
||||
test(setupSqrt, sqrt);
|
||||
|
||||
// MMinMax
|
||||
function setupMinMax() {
|
||||
f32[0] = -0;
|
||||
f32[1] = 0;
|
||||
f32[2] = 1;
|
||||
f32[3] = 4;
|
||||
f32[4] = -1;
|
||||
f32[5] = Infinity;
|
||||
f32[6] = NaN;
|
||||
f32[7] = 13.37;
|
||||
f32[8] = -Infinity;
|
||||
f32[9] = Math.pow(2,31) - 1;
|
||||
}
|
||||
function minMax() {
|
||||
for(var i = 0; i < 9; ++i) {
|
||||
for(var j = 0; j < 9; j++) {
|
||||
var minf = Math.fround(Math.min(f32[i], f32[j]));
|
||||
var mind = 1 / (1 / Math.min(f32[i], f32[j])); // force no float32 by chaining arith ops
|
||||
assertFloat32(minf, true);
|
||||
assertFloat32(mind, false);
|
||||
assertEq( minf, Math.fround(mind) );
|
||||
|
||||
var maxf = Math.fround(Math.max(f32[i], f32[j]));
|
||||
var maxd = 1 / (1 / Math.max(f32[i], f32[j])); // force no float32 by chaining arith ops
|
||||
assertFloat32(maxf, true);
|
||||
assertFloat32(maxd, false);
|
||||
assertEq( maxf, Math.fround(maxd) );
|
||||
}
|
||||
}
|
||||
}
|
||||
test(setupMinMax, minMax);
|
||||
|
||||
// MTruncateToInt32
|
||||
// The only way to get a MTruncateToInt32 with a Float32 input is to use Math.imul
|
||||
function setupTruncateToInt32() {
|
||||
|
@ -61,8 +61,8 @@ function test(f) {
|
||||
f();
|
||||
}
|
||||
|
||||
var f32 = new Float32Array(2);
|
||||
var f64 = new Float64Array(2);
|
||||
var f32 = new Float32Array(4);
|
||||
var f64 = new Float64Array(4);
|
||||
|
||||
function acceptAdd() {
|
||||
var use = f32[0] + 1;
|
||||
@ -151,6 +151,79 @@ function refuseSqrt() {
|
||||
}
|
||||
test(refuseSqrt);
|
||||
|
||||
function acceptMin() {
|
||||
var res = Math.min(f32[0], f32[1]);
|
||||
assertFloat32(res, true);
|
||||
f64[0] = res;
|
||||
}
|
||||
test(acceptMin);
|
||||
|
||||
// In theory, we could do it, as Math.min/max actually behave as a Phi (it's a
|
||||
// float32 producer iff its inputs are producers, it's a consumer iff its uses
|
||||
// are consumers). In practice, this would involve some overhead for big chains
|
||||
// of min/max.
|
||||
function refuseMinAdd() {
|
||||
var res = Math.min(f32[0], f32[1]) + f32[2];
|
||||
assertFloat32(res, false);
|
||||
f32[3] = res;
|
||||
}
|
||||
test(refuseMinAdd);
|
||||
|
||||
function acceptSeveralMinMax() {
|
||||
var x = Math.min(f32[0], f32[1]);
|
||||
var y = Math.max(f32[2], f32[3]);
|
||||
var res = Math.min(x, y);
|
||||
assertFloat32(res, true);
|
||||
f64[0] = res;
|
||||
}
|
||||
test(acceptSeveralMinMax);
|
||||
|
||||
function acceptSeveralMinMax2() {
|
||||
var res = Math.min(f32[0], f32[1], f32[2], f32[3]);
|
||||
assertFloat32(res, true);
|
||||
f64[0] = res;
|
||||
}
|
||||
test(acceptSeveralMinMax2);
|
||||
|
||||
function partialMinMax() {
|
||||
var x = Math.min(f32[0], f32[1]);
|
||||
var y = Math.min(f64[0], f32[1]);
|
||||
var res = Math.min(x, y);
|
||||
assertFloat32(x, true);
|
||||
assertFloat32(y, false);
|
||||
assertFloat32(res, false);
|
||||
f64[0] = res;
|
||||
}
|
||||
test(partialMinMax);
|
||||
|
||||
function refuseSeveralMinMax() {
|
||||
var res = Math.min(f32[0], f32[1] + f32[2], f32[2], f32[3]);
|
||||
assertFloat32(res, false);
|
||||
f64[0] = res;
|
||||
}
|
||||
test(refuseSeveralMinMax);
|
||||
|
||||
function refuseMin() {
|
||||
var res = Math.min(f32[0], 42.13 + f32[1]);
|
||||
assertFloat32(res, false);
|
||||
f64[0] = res;
|
||||
}
|
||||
test(refuseMin);
|
||||
|
||||
function acceptMax() {
|
||||
var res = Math.max(f32[0], f32[1]);
|
||||
assertFloat32(res, true);
|
||||
f64[0] = res;
|
||||
}
|
||||
test(acceptMax);
|
||||
|
||||
function refuseMax() {
|
||||
var res = Math.max(f32[0], 42.13 + f32[1]);
|
||||
assertFloat32(res, false);
|
||||
f64[0] = res;
|
||||
}
|
||||
test(refuseMax);
|
||||
|
||||
function acceptAbs() {
|
||||
var res = Math.abs(f32[0]);
|
||||
assertFloat32(res, true);
|
||||
|
@ -5,9 +5,10 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jit/JitOptions.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include "jsfun.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
@ -16,58 +17,87 @@ namespace jit {
|
||||
|
||||
JitOptions js_JitOptions;
|
||||
|
||||
template<typename T> struct IsBool : mozilla::FalseType {};
|
||||
template<> struct IsBool<bool> : mozilla::TrueType {};
|
||||
|
||||
template<typename T>
|
||||
T overrideDefault(const char *param, T dflt) {
|
||||
char *str = getenv(param);
|
||||
if (!str)
|
||||
return dflt;
|
||||
if (IsBool<T>::value) {
|
||||
if (strcmp(str, "true") == 0 ||
|
||||
strcmp(str, "yes")) {
|
||||
return true;
|
||||
}
|
||||
if (strcmp(str, "false") == 0 ||
|
||||
strcmp(str, "no")) {
|
||||
return false;
|
||||
}
|
||||
fprintf(stderr, "Warning: I didn't understand %s=\"%s\"", param, str);
|
||||
} else {
|
||||
char *endp;
|
||||
int retval = strtol(str, &endp, 0);
|
||||
if (*endp == '\0')
|
||||
return retval;
|
||||
|
||||
fprintf(stderr, "Warning: I didn't understand %s=\"%s\"", param, str);
|
||||
}
|
||||
return dflt;
|
||||
}
|
||||
#define SET_DEFAULT(var, dflt) var = overrideDefault("JIT_OPTION_" #var, dflt)
|
||||
JitOptions::JitOptions()
|
||||
{
|
||||
// Whether to perform expensive graph-consistency DEBUG-only assertions.
|
||||
// It can be useful to disable this to reduce DEBUG-compile time of large
|
||||
// asm.js programs.
|
||||
checkGraphConsistency = true;
|
||||
SET_DEFAULT(checkGraphConsistency, true);
|
||||
|
||||
#ifdef CHECK_OSIPOINT_REGISTERS
|
||||
// Emit extra code to verify live regs at the start of a VM call
|
||||
// are not modified before its OsiPoint.
|
||||
checkOsiPointRegisters = false;
|
||||
SET_DEFAULT(checkOsiPointRegisters, false);
|
||||
#endif
|
||||
|
||||
// Whether to enable extra code to perform dynamic validation of
|
||||
// RangeAnalysis results.
|
||||
checkRangeAnalysis = false;
|
||||
SET_DEFAULT(checkRangeAnalysis, false);
|
||||
|
||||
// Whether Ion should compile try-catch statements.
|
||||
compileTryCatch = true;
|
||||
SET_DEFAULT(compileTryCatch, true);
|
||||
|
||||
// Toggle whether eager scalar replacement is globally disabled.
|
||||
disableScalarReplacement = true; // experimental
|
||||
SET_DEFAULT(disableScalarReplacement, true); // experimental
|
||||
|
||||
// Toggle whether global value numbering is globally disabled.
|
||||
disableGvn = false;
|
||||
SET_DEFAULT(disableGvn, false);
|
||||
|
||||
// Toggles whether loop invariant code motion is globally disabled.
|
||||
disableLicm = false;
|
||||
SET_DEFAULT(disableLicm, false);
|
||||
|
||||
// Toggles whether inlining is globally disabled.
|
||||
disableInlining = false;
|
||||
SET_DEFAULT(disableInlining, false);
|
||||
|
||||
// Toggles whether Edge Case Analysis is gobally disabled.
|
||||
disableEdgeCaseAnalysis = false;
|
||||
SET_DEFAULT(disableEdgeCaseAnalysis, false);
|
||||
|
||||
// Toggles whether Range Analysis is globally disabled.
|
||||
disableRangeAnalysis = false;
|
||||
SET_DEFAULT(disableRangeAnalysis, false);
|
||||
|
||||
// Toggles whether Loop Unrolling is globally disabled.
|
||||
disableLoopUnrolling = true;
|
||||
SET_DEFAULT(disableLoopUnrolling, true);
|
||||
|
||||
// Toggles whether Effective Address Analysis is globally disabled.
|
||||
disableEaa = false;
|
||||
SET_DEFAULT(disableEaa, false);
|
||||
|
||||
// Whether functions are compiled immediately.
|
||||
eagerCompilation = false;
|
||||
SET_DEFAULT(eagerCompilation, false);
|
||||
|
||||
// Force how many invocation or loop iterations are needed before compiling
|
||||
// a function with the highest ionmonkey optimization level.
|
||||
// (i.e. OptimizationLevel_Normal)
|
||||
forceDefaultIonWarmUpThreshold = false;
|
||||
forcedDefaultIonWarmUpThreshold = 1000;
|
||||
SET_DEFAULT(forceDefaultIonWarmUpThreshold, false);
|
||||
SET_DEFAULT(forcedDefaultIonWarmUpThreshold, 1000);
|
||||
|
||||
// Force the used register allocator instead of letting the
|
||||
// optimization pass decide.
|
||||
@ -75,39 +105,39 @@ JitOptions::JitOptions()
|
||||
forcedRegisterAllocator = RegisterAllocator_LSRA;
|
||||
|
||||
// Toggles whether large scripts are rejected.
|
||||
limitScriptSize = true;
|
||||
SET_DEFAULT(limitScriptSize, true);
|
||||
|
||||
// Toggles whether functions may be entered at loop headers.
|
||||
osr = true;
|
||||
SET_DEFAULT(osr, true);
|
||||
|
||||
// How many invocations or loop iterations are needed before functions
|
||||
// are compiled with the baseline compiler.
|
||||
baselineWarmUpThreshold = 10;
|
||||
SET_DEFAULT(baselineWarmUpThreshold, 10);
|
||||
|
||||
// Number of exception bailouts (resuming into catch/finally block) before
|
||||
// we invalidate and forbid Ion compilation.
|
||||
exceptionBailoutThreshold = 10;
|
||||
SET_DEFAULT(exceptionBailoutThreshold, 10);
|
||||
|
||||
// Number of bailouts without invalidation before we set
|
||||
// JSScript::hadFrequentBailouts and invalidate.
|
||||
frequentBailoutThreshold = 10;
|
||||
SET_DEFAULT(frequentBailoutThreshold, 10);
|
||||
|
||||
// How many actual arguments are accepted on the C stack.
|
||||
maxStackArgs = 4096;
|
||||
SET_DEFAULT(maxStackArgs, 4096);
|
||||
|
||||
// How many times we will try to enter a script via OSR before
|
||||
// invalidating the script.
|
||||
osrPcMismatchesBeforeRecompile = 6000;
|
||||
SET_DEFAULT(osrPcMismatchesBeforeRecompile, 6000);
|
||||
|
||||
// The bytecode length limit for small function.
|
||||
//
|
||||
// The default for this was arrived at empirically via benchmarking.
|
||||
// We may want to tune it further after other optimizations have gone
|
||||
// in.
|
||||
smallFunctionMaxBytecodeLength_ = 100;
|
||||
SET_DEFAULT(smallFunctionMaxBytecodeLength_, 100);
|
||||
|
||||
// How many uses of a parallel kernel before we attempt compilation.
|
||||
compilerWarmUpThresholdPar = 1;
|
||||
SET_DEFAULT(compilerWarmUpThresholdPar, 1);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -143,42 +143,80 @@ class LSimdSplatX4 : public LInstructionHelper<1, 1, 0>
|
||||
}
|
||||
};
|
||||
|
||||
// Extracts an element from a given SIMD int32x4 lane.
|
||||
class LSimdExtractElementI : public LInstructionHelper<1, 1, 0>
|
||||
class LSimdExtractElementBase : public LInstructionHelper<1, 1, 0>
|
||||
{
|
||||
SimdLane lane_;
|
||||
|
||||
public:
|
||||
LIR_HEADER(SimdExtractElementI);
|
||||
|
||||
LSimdExtractElementI(const LAllocation &base, SimdLane lane) : lane_(lane) {
|
||||
protected:
|
||||
LSimdExtractElementBase(const LAllocation &base) {
|
||||
setOperand(0, base);
|
||||
}
|
||||
|
||||
public:
|
||||
const LAllocation *getBase() {
|
||||
return getOperand(0);
|
||||
}
|
||||
SimdLane lane() const {
|
||||
return lane_;
|
||||
return mir_->toSimdExtractElement()->lane();
|
||||
}
|
||||
};
|
||||
|
||||
// Extracts an element from a given SIMD float32x4 lane.
|
||||
class LSimdExtractElementF : public LInstructionHelper<1, 1, 0>
|
||||
// Extracts an element from a given SIMD int32x4 lane.
|
||||
class LSimdExtractElementI : public LSimdExtractElementBase
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(SimdExtractElementI);
|
||||
LSimdExtractElementI(const LAllocation &base)
|
||||
: LSimdExtractElementBase(base)
|
||||
{}
|
||||
};
|
||||
// Extracts an element from a given SIMD float32x4 lane.
|
||||
class LSimdExtractElementF : public LSimdExtractElementBase
|
||||
{
|
||||
SimdLane lane_;
|
||||
|
||||
public:
|
||||
LIR_HEADER(SimdExtractElementF);
|
||||
LSimdExtractElementF(const LAllocation &base)
|
||||
: LSimdExtractElementBase(base)
|
||||
{}
|
||||
};
|
||||
|
||||
LSimdExtractElementF(const LAllocation &base, SimdLane lane) : lane_(lane) {
|
||||
setOperand(0, base);
|
||||
class LSimdInsertElementBase : public LInstructionHelper<1, 2, 0>
|
||||
{
|
||||
protected:
|
||||
LSimdInsertElementBase(const LAllocation &vec, const LAllocation &val)
|
||||
{
|
||||
setOperand(0, vec);
|
||||
setOperand(1, val);
|
||||
}
|
||||
const LAllocation *getBase() {
|
||||
|
||||
public:
|
||||
const LAllocation *vector() {
|
||||
return getOperand(0);
|
||||
}
|
||||
SimdLane lane() const {
|
||||
return lane_;
|
||||
const LAllocation *value() {
|
||||
return getOperand(1);
|
||||
}
|
||||
const SimdLane lane() const {
|
||||
return mir_->toSimdInsertElement()->lane();
|
||||
}
|
||||
};
|
||||
|
||||
// Replace an element from a given SIMD int32x4 lane with a given value.
|
||||
class LSimdInsertElementI : public LSimdInsertElementBase
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(SimdInsertElementI);
|
||||
LSimdInsertElementI(const LAllocation &vec, const LAllocation &val)
|
||||
: LSimdInsertElementBase(vec, val)
|
||||
{}
|
||||
};
|
||||
|
||||
// Replace an element from a given SIMD float32x4 lane with a given value.
|
||||
class LSimdInsertElementF : public LSimdInsertElementBase
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(SimdInsertElementF);
|
||||
LSimdInsertElementF(const LAllocation &vec, const LAllocation &val)
|
||||
: LSimdInsertElementBase(vec, val)
|
||||
{}
|
||||
};
|
||||
|
||||
class LSimdSignMaskX4 : public LInstructionHelper<1, 1, 0>
|
||||
@ -2656,16 +2694,16 @@ class LThrow : public LCallInstructionHelper<0, BOX_PIECES, 0>
|
||||
static const size_t Value = 0;
|
||||
};
|
||||
|
||||
class LMinMaxI : public LInstructionHelper<1, 2, 0>
|
||||
class LMinMaxBase : public LInstructionHelper<1, 2, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(MinMaxI)
|
||||
LMinMaxI(const LAllocation &first, const LAllocation &second)
|
||||
protected:
|
||||
LMinMaxBase(const LAllocation &first, const LAllocation &second)
|
||||
{
|
||||
setOperand(0, first);
|
||||
setOperand(1, second);
|
||||
}
|
||||
|
||||
public:
|
||||
const LAllocation *first() {
|
||||
return this->getOperand(0);
|
||||
}
|
||||
@ -2683,31 +2721,28 @@ class LMinMaxI : public LInstructionHelper<1, 2, 0>
|
||||
}
|
||||
};
|
||||
|
||||
class LMinMaxD : public LInstructionHelper<1, 2, 0>
|
||||
class LMinMaxI : public LMinMaxBase
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(MinMaxI)
|
||||
LMinMaxI(const LAllocation &first, const LAllocation &second) : LMinMaxBase(first, second)
|
||||
{}
|
||||
};
|
||||
|
||||
class LMinMaxD : public LMinMaxBase
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(MinMaxD)
|
||||
LMinMaxD(const LAllocation &first, const LAllocation &second)
|
||||
{
|
||||
setOperand(0, first);
|
||||
setOperand(1, second);
|
||||
}
|
||||
LMinMaxD(const LAllocation &first, const LAllocation &second) : LMinMaxBase(first, second)
|
||||
{}
|
||||
};
|
||||
|
||||
const LAllocation *first() {
|
||||
return this->getOperand(0);
|
||||
}
|
||||
const LAllocation *second() {
|
||||
return this->getOperand(1);
|
||||
}
|
||||
const LDefinition *output() {
|
||||
return this->getDef(0);
|
||||
}
|
||||
MMinMax *mir() const {
|
||||
return mir_->toMinMax();
|
||||
}
|
||||
const char *extraName() const {
|
||||
return mir()->isMax() ? "Max" : "Min";
|
||||
}
|
||||
class LMinMaxF : public LMinMaxBase
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(MinMaxF)
|
||||
LMinMaxF(const LAllocation &first, const LAllocation &second) : LMinMaxBase(first, second)
|
||||
{}
|
||||
};
|
||||
|
||||
// Negative of an integer
|
||||
|
@ -21,6 +21,8 @@
|
||||
_(Float32x4) \
|
||||
_(SimdExtractElementI) \
|
||||
_(SimdExtractElementF) \
|
||||
_(SimdInsertElementI) \
|
||||
_(SimdInsertElementF) \
|
||||
_(SimdSignMaskX4) \
|
||||
_(SimdBinaryCompIx4) \
|
||||
_(SimdBinaryCompFx4) \
|
||||
@ -115,6 +117,7 @@
|
||||
_(EmulatesUndefinedAndBranch) \
|
||||
_(MinMaxI) \
|
||||
_(MinMaxD) \
|
||||
_(MinMaxF) \
|
||||
_(NegI) \
|
||||
_(NegD) \
|
||||
_(NegF) \
|
||||
|
@ -1288,6 +1288,12 @@ LIRGenerator::visitMinMax(MMinMax *ins)
|
||||
return defineReuseInput(lir, ins, 0);
|
||||
}
|
||||
|
||||
if (ins->specialization() == MIRType_Float32) {
|
||||
LMinMaxF *lir = new(alloc()) LMinMaxF(useRegisterAtStart(first), useRegister(second));
|
||||
return defineReuseInput(lir, ins, 0);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(ins->specialization() == MIRType_Double);
|
||||
LMinMaxD *lir = new(alloc()) LMinMaxD(useRegisterAtStart(first), useRegister(second));
|
||||
return defineReuseInput(lir, ins, 0);
|
||||
}
|
||||
@ -3721,17 +3727,32 @@ LIRGenerator::visitSimdExtractElement(MSimdExtractElement *ins)
|
||||
// Note: there could be int16x8 in the future, which doesn't use the
|
||||
// same instruction. We either need to pass the arity or create new LIns.
|
||||
LUse use = useRegisterAtStart(ins->input());
|
||||
return define(new(alloc()) LSimdExtractElementI(use, ins->lane()), ins);
|
||||
return define(new(alloc()) LSimdExtractElementI(use), ins);
|
||||
}
|
||||
|
||||
if (ins->input()->type() == MIRType_Float32x4) {
|
||||
LUse use = useRegisterAtStart(ins->input());
|
||||
return define(new(alloc()) LSimdExtractElementF(use, ins->lane()), ins);
|
||||
return define(new(alloc()) LSimdExtractElementF(use), ins);
|
||||
}
|
||||
|
||||
MOZ_CRASH("Unknown SIMD kind when extracting element");
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitSimdInsertElement(MSimdInsertElement *ins)
|
||||
{
|
||||
JS_ASSERT(IsSimdType(ins->type()));
|
||||
|
||||
LUse vec = useRegisterAtStart(ins->vector());
|
||||
LUse val = useRegister(ins->value());
|
||||
if (ins->type() == MIRType_Int32x4)
|
||||
return defineReuseInput(new(alloc()) LSimdInsertElementI(vec, val), ins, 0);
|
||||
if (ins->type() == MIRType_Float32x4)
|
||||
return defineReuseInput(new(alloc()) LSimdInsertElementF(vec, val), ins, 0);
|
||||
|
||||
MOZ_CRASH("Unknown SIMD kind when generating constant");
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitSimdSignMask(MSimdSignMask *ins)
|
||||
{
|
||||
|
@ -269,6 +269,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
||||
bool visitGetDOMMember(MGetDOMMember *ins);
|
||||
bool visitRecompileCheck(MRecompileCheck *ins);
|
||||
bool visitSimdExtractElement(MSimdExtractElement *ins);
|
||||
bool visitSimdInsertElement(MSimdInsertElement *ins);
|
||||
bool visitSimdSignMask(MSimdSignMask *ins);
|
||||
bool visitSimdBinaryComp(MSimdBinaryComp *ins);
|
||||
bool visitSimdBinaryArith(MSimdBinaryArith *ins);
|
||||
|
@ -1698,8 +1698,31 @@ MBinaryArithInstruction::trySpecializeFloat32(TempAllocator &alloc)
|
||||
MDefinition *left = lhs();
|
||||
MDefinition *right = rhs();
|
||||
|
||||
if (!left->canProduceFloat32() || !right->canProduceFloat32()
|
||||
|| !CheckUsesAreFloat32Consumers(this))
|
||||
if (!left->canProduceFloat32() || !right->canProduceFloat32() ||
|
||||
!CheckUsesAreFloat32Consumers(this))
|
||||
{
|
||||
if (left->type() == MIRType_Float32)
|
||||
ConvertDefinitionToDouble<0>(alloc, left, this);
|
||||
if (right->type() == MIRType_Float32)
|
||||
ConvertDefinitionToDouble<1>(alloc, right, this);
|
||||
return;
|
||||
}
|
||||
|
||||
specialization_ = MIRType_Float32;
|
||||
setResultType(MIRType_Float32);
|
||||
}
|
||||
|
||||
void
|
||||
MMinMax::trySpecializeFloat32(TempAllocator &alloc)
|
||||
{
|
||||
if (specialization_ == MIRType_Int32)
|
||||
return;
|
||||
|
||||
MDefinition *left = lhs();
|
||||
MDefinition *right = rhs();
|
||||
|
||||
if (!(left->canProduceFloat32() || (left->isMinMax() && left->type() == MIRType_Float32)) ||
|
||||
!(right->canProduceFloat32() || (right->isMinMax() && right->type() == MIRType_Float32)))
|
||||
{
|
||||
if (left->type() == MIRType_Float32)
|
||||
ConvertDefinitionToDouble<0>(alloc, left, this);
|
||||
|
@ -1402,6 +1402,50 @@ class MSimdExtractElement : public MUnaryInstruction
|
||||
}
|
||||
};
|
||||
|
||||
// Replaces the datum in the given lane by a scalar value of the same type.
|
||||
class MSimdInsertElement : public MBinaryInstruction
|
||||
{
|
||||
private:
|
||||
SimdLane lane_;
|
||||
|
||||
MSimdInsertElement(MDefinition *vec, MDefinition *val, MIRType type, SimdLane lane)
|
||||
: MBinaryInstruction(vec, val), lane_(lane)
|
||||
{
|
||||
MOZ_ASSERT(IsSimdType(type) && vec->type() == type);
|
||||
MOZ_ASSERT(SimdTypeToScalarType(type) == val->type());
|
||||
|
||||
setMovable();
|
||||
setResultType(type);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(SimdInsertElement)
|
||||
|
||||
static MSimdInsertElement *NewAsmJS(TempAllocator &alloc, MDefinition *vec, MDefinition *val,
|
||||
MIRType type, SimdLane lane)
|
||||
{
|
||||
return new(alloc) MSimdInsertElement(vec, val, type, lane);
|
||||
}
|
||||
|
||||
MDefinition *vector() {
|
||||
return getOperand(0);
|
||||
}
|
||||
MDefinition *value() {
|
||||
return getOperand(1);
|
||||
}
|
||||
SimdLane lane() const {
|
||||
return lane_;
|
||||
}
|
||||
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
|
||||
bool congruentTo(const MDefinition *ins) const {
|
||||
return binaryCongruentTo(ins) && lane_ == ins->toSimdInsertElement()->lane();
|
||||
}
|
||||
};
|
||||
|
||||
// Extracts the sign bits from a given vector, returning an MIRType_Int32.
|
||||
class MSimdSignMask : public MUnaryInstruction
|
||||
{
|
||||
@ -4539,7 +4583,7 @@ class MMinMax
|
||||
: MBinaryInstruction(left, right),
|
||||
isMax_(isMax)
|
||||
{
|
||||
JS_ASSERT(type == MIRType_Double || type == MIRType_Int32);
|
||||
JS_ASSERT(IsNumberType(type));
|
||||
setResultType(type);
|
||||
setMovable();
|
||||
specialization_ = type;
|
||||
@ -4574,6 +4618,9 @@ class MMinMax
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isFloat32Commutative() const { return true; }
|
||||
void trySpecializeFloat32(TempAllocator &alloc);
|
||||
|
||||
ALLOW_CLONE(MMinMax)
|
||||
};
|
||||
|
||||
|
@ -16,6 +16,7 @@ namespace jit {
|
||||
_(SimdSplatX4) \
|
||||
_(SimdConstant) \
|
||||
_(SimdExtractElement) \
|
||||
_(SimdInsertElement) \
|
||||
_(SimdSignMask) \
|
||||
_(SimdBinaryComp) \
|
||||
_(SimdBinaryArith) \
|
||||
|
@ -115,6 +115,7 @@ class ParallelSafetyVisitor : public MDefinitionVisitor
|
||||
SAFE_OP(SimdSplatX4)
|
||||
SAFE_OP(SimdConstant)
|
||||
SAFE_OP(SimdExtractElement)
|
||||
SAFE_OP(SimdInsertElement)
|
||||
SAFE_OP(SimdSignMask)
|
||||
SAFE_OP(SimdBinaryComp)
|
||||
SAFE_OP(SimdBinaryArith)
|
||||
|
@ -290,6 +290,55 @@ CodeGeneratorARM::visitMinMaxD(LMinMaxD *ins)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorARM::visitMinMaxF(LMinMaxF *ins)
|
||||
{
|
||||
FloatRegister first = ToFloatRegister(ins->first());
|
||||
FloatRegister second = ToFloatRegister(ins->second());
|
||||
FloatRegister output = ToFloatRegister(ins->output());
|
||||
|
||||
JS_ASSERT(first == output);
|
||||
|
||||
Assembler::Condition cond = ins->mir()->isMax()
|
||||
? Assembler::VFP_LessThanOrEqual
|
||||
: Assembler::VFP_GreaterThanOrEqual;
|
||||
Label nan, equal, returnSecond, done;
|
||||
|
||||
masm.compareFloat(first, second);
|
||||
// First or second is NaN, result is NaN.
|
||||
masm.ma_b(&nan, Assembler::VFP_Unordered);
|
||||
// Make sure we handle -0 and 0 right.
|
||||
masm.ma_b(&equal, Assembler::VFP_Equal);
|
||||
masm.ma_b(&returnSecond, cond);
|
||||
masm.ma_b(&done);
|
||||
|
||||
// Check for zero.
|
||||
masm.bind(&equal);
|
||||
masm.compareFloat(first, NoVFPRegister);
|
||||
// First wasn't 0 or -0, so just return it.
|
||||
masm.ma_b(&done, Assembler::VFP_NotEqualOrUnordered);
|
||||
// So now both operands are either -0 or 0.
|
||||
if (ins->mir()->isMax()) {
|
||||
// -0 + -0 = -0 and -0 + 0 = 0.
|
||||
masm.ma_vadd_f32(second, first, first);
|
||||
} else {
|
||||
masm.ma_vneg_f32(first, first);
|
||||
masm.ma_vsub_f32(first, second, first);
|
||||
masm.ma_vneg_f32(first, first);
|
||||
}
|
||||
masm.ma_b(&done);
|
||||
|
||||
masm.bind(&nan);
|
||||
masm.loadConstantFloat32(GenericNaN(), output);
|
||||
masm.ma_b(&done);
|
||||
|
||||
masm.bind(&returnSecond);
|
||||
masm.ma_vmov_f32(second, output);
|
||||
|
||||
masm.bind(&done);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorARM::visitAbsD(LAbsD *ins)
|
||||
{
|
||||
|
@ -102,6 +102,7 @@ class CodeGeneratorARM : public CodeGeneratorShared
|
||||
public:
|
||||
// Instruction visitors.
|
||||
virtual bool visitMinMaxD(LMinMaxD *ins);
|
||||
virtual bool visitMinMaxF(LMinMaxF *ins);
|
||||
virtual bool visitAbsD(LAbsD *ins);
|
||||
virtual bool visitAbsF(LAbsF *ins);
|
||||
virtual bool visitSqrtD(LSqrtD *ins);
|
||||
|
@ -525,7 +525,7 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
}
|
||||
}
|
||||
|
||||
// movsd and movss are only provided in load/store form since the
|
||||
// movsd is only provided in load/store form since the
|
||||
// register-to-register form has different semantics (it doesn't clobber
|
||||
// the whole output register) and isn't needed currently.
|
||||
void movsd(const Address &src, FloatRegister dest) {
|
||||
@ -540,6 +540,10 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
void movsd(FloatRegister src, const BaseIndex &dest) {
|
||||
masm.movsd_rm(src.code(), dest.offset, dest.base.code(), dest.index.code(), dest.scale);
|
||||
}
|
||||
// Although movss is not only provided in load/store form (for the same
|
||||
// reasons as movsd above), the register to register form should be only
|
||||
// used in contexts where we care about not clearing the higher lanes of
|
||||
// the FloatRegister.
|
||||
void movss(const Address &src, FloatRegister dest) {
|
||||
masm.movss_mr(src.offset, src.base.code(), dest.code());
|
||||
}
|
||||
@ -552,6 +556,9 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
void movss(FloatRegister src, const BaseIndex &dest) {
|
||||
masm.movss_rm(src.code(), dest.offset, dest.base.code(), dest.index.code(), dest.scale);
|
||||
}
|
||||
void movss(FloatRegister src, const FloatRegister &dest) {
|
||||
masm.movss_rr(src.code(), dest.code());
|
||||
}
|
||||
void movdqu(const Operand &src, FloatRegister dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
switch (src.kind()) {
|
||||
@ -1412,11 +1419,11 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
masm.unpcklps_rr(src.code(), dest.code());
|
||||
}
|
||||
void pinsrd(unsigned lane, Register src, FloatRegister dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
JS_ASSERT(HasSSE41());
|
||||
masm.pinsrd_irr(lane, src.code(), dest.code());
|
||||
}
|
||||
void pinsrd(unsigned lane, const Operand &src, FloatRegister dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
JS_ASSERT(HasSSE41());
|
||||
switch (src.kind()) {
|
||||
case Operand::REG:
|
||||
masm.pinsrd_irr(lane, src.reg(), dest.code());
|
||||
@ -1931,6 +1938,10 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.orpd_rr(src.code(), dest.code());
|
||||
}
|
||||
void orps(FloatRegister src, FloatRegister dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.orps_rr(src.code(), dest.code());
|
||||
}
|
||||
void andpd(FloatRegister src, FloatRegister dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.andpd_rr(src.code(), dest.code());
|
||||
@ -1947,18 +1958,29 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.sqrtss_rr(src.code(), dest.code());
|
||||
}
|
||||
void roundsd(FloatRegister src, FloatRegister dest,
|
||||
X86Assembler::RoundingMode mode)
|
||||
{
|
||||
void roundsd(FloatRegister src, FloatRegister dest, X86Assembler::RoundingMode mode) {
|
||||
JS_ASSERT(HasSSE41());
|
||||
masm.roundsd_rr(src.code(), dest.code(), mode);
|
||||
}
|
||||
void roundss(FloatRegister src, FloatRegister dest,
|
||||
X86Assembler::RoundingMode mode)
|
||||
{
|
||||
void roundss(FloatRegister src, FloatRegister dest, X86Assembler::RoundingMode mode) {
|
||||
JS_ASSERT(HasSSE41());
|
||||
masm.roundss_rr(src.code(), dest.code(), mode);
|
||||
}
|
||||
unsigned insertpsMask(SimdLane sourceLane, SimdLane destLane, unsigned zeroMask = 0)
|
||||
{
|
||||
// Note that the sourceLane bits are ignored in the case of a source
|
||||
// memory operand, and the source is the given 32-bits memory location.
|
||||
MOZ_ASSERT(zeroMask < 16);
|
||||
unsigned ret = zeroMask ;
|
||||
ret |= unsigned(destLane) << 4;
|
||||
ret |= unsigned(sourceLane) << 6;
|
||||
MOZ_ASSERT(ret < 256);
|
||||
return ret;
|
||||
}
|
||||
void insertps(FloatRegister src, FloatRegister dest, unsigned mask) {
|
||||
JS_ASSERT(HasSSE41());
|
||||
masm.insertps_irr(mask, src.code(), dest.code());
|
||||
}
|
||||
void minsd(FloatRegister src, FloatRegister dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.minsd_rr(src.code(), dest.code());
|
||||
@ -1976,6 +1998,10 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void minss(FloatRegister src, FloatRegister dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.minss_rr(src.code(), dest.code());
|
||||
}
|
||||
void maxsd(FloatRegister src, FloatRegister dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.maxsd_rr(src.code(), dest.code());
|
||||
@ -1993,6 +2019,10 @@ class AssemblerX86Shared : public AssemblerShared
|
||||
MOZ_CRASH("unexpected operand kind");
|
||||
}
|
||||
}
|
||||
void maxss(FloatRegister src, FloatRegister dest) {
|
||||
JS_ASSERT(HasSSE2());
|
||||
masm.maxss_rr(src.code(), dest.code());
|
||||
}
|
||||
void fisttp(const Operand &dest) {
|
||||
JS_ASSERT(HasSSE3());
|
||||
switch (dest.kind()) {
|
||||
|
@ -308,10 +308,12 @@ private:
|
||||
OP2_SUBSD_VsdWsd = 0x5C,
|
||||
OP2_SUBPS_VpsWps = 0x5C,
|
||||
OP2_MINSD_VsdWsd = 0x5D,
|
||||
OP2_MINSS_VssWss = 0x5D,
|
||||
OP2_MINPS_VpsWps = 0x5D,
|
||||
OP2_DIVSD_VsdWsd = 0x5E,
|
||||
OP2_DIVPS_VpsWps = 0x5E,
|
||||
OP2_MAXSD_VsdWsd = 0x5F,
|
||||
OP2_MAXSS_VssWss = 0x5F,
|
||||
OP2_MAXPS_VpsWps = 0x5F,
|
||||
OP2_SQRTSD_VsdWsd = 0x51,
|
||||
OP2_SQRTSS_VssWss = 0x51,
|
||||
@ -350,13 +352,15 @@ private:
|
||||
OP3_ROUNDSS_VsdWsd = 0x0A,
|
||||
OP3_ROUNDSD_VsdWsd = 0x0B,
|
||||
OP3_PTEST_VdVd = 0x17,
|
||||
OP3_INSERTPS_VpsUps = 0x21,
|
||||
OP3_PINSRD_VdqEdIb = 0x22
|
||||
} ThreeByteOpcodeID;
|
||||
|
||||
typedef enum {
|
||||
ESCAPE_PTEST = 0x38,
|
||||
ESCAPE_PINSRD = 0x3A,
|
||||
ESCAPE_ROUNDSD = 0x3A
|
||||
ESCAPE_ROUNDSD = 0x3A,
|
||||
ESCAPE_INSERTPS = 0x3A
|
||||
} ThreeByteEscape;
|
||||
|
||||
TwoByteOpcodeID jccRel32(Condition cond)
|
||||
@ -3602,6 +3606,16 @@ public:
|
||||
m_formatter.immediate8(mode); // modes are the same for roundsd and roundss
|
||||
}
|
||||
|
||||
void insertps_irr(unsigned mask, XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
MOZ_ASSERT(mask < 256);
|
||||
spew("insertps $%u, %s, %s",
|
||||
mask, nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_66);
|
||||
m_formatter.threeByteOp(OP3_INSERTPS_VpsUps, ESCAPE_INSERTPS, (RegisterID)dst, (RegisterID)src);
|
||||
m_formatter.immediate8(uint8_t(mask));
|
||||
}
|
||||
|
||||
void pinsrd_irr(unsigned lane, RegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
MOZ_ASSERT(lane < 4);
|
||||
@ -3639,6 +3653,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_MINSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void minss_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("minss %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MINSS_VssWss, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
void maxsd_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("maxsd %s, %s",
|
||||
@ -3655,6 +3677,14 @@ public:
|
||||
m_formatter.twoByteOp(OP2_MAXSD_VsdWsd, (RegisterID)dst, base, offset);
|
||||
}
|
||||
|
||||
void maxss_rr(XMMRegisterID src, XMMRegisterID dst)
|
||||
{
|
||||
spew("maxss %s, %s",
|
||||
nameFPReg(src), nameFPReg(dst));
|
||||
m_formatter.prefix(PRE_SSE_F3);
|
||||
m_formatter.twoByteOp(OP2_MAXSS_VssWss, (RegisterID)dst, (RegisterID)src);
|
||||
}
|
||||
|
||||
// Misc instructions:
|
||||
|
||||
void int3()
|
||||
|
@ -536,6 +536,58 @@ CodeGeneratorX86Shared::visitMinMaxD(LMinMaxD *ins)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorX86Shared::visitMinMaxF(LMinMaxF *ins)
|
||||
{
|
||||
FloatRegister first = ToFloatRegister(ins->first());
|
||||
FloatRegister second = ToFloatRegister(ins->second());
|
||||
#ifdef DEBUG
|
||||
FloatRegister output = ToFloatRegister(ins->output());
|
||||
JS_ASSERT(first == output);
|
||||
#endif
|
||||
|
||||
Label done, nan, minMaxInst;
|
||||
|
||||
// Do a ucomiss to catch equality and NaNs, which both require special
|
||||
// handling. If the operands are ordered and inequal, we branch straight to
|
||||
// the min/max instruction. If we wanted, we could also branch for less-than
|
||||
// or greater-than here instead of using min/max, however these conditions
|
||||
// will sometimes be hard on the branch predictor.
|
||||
masm.ucomiss(first, second);
|
||||
masm.j(Assembler::NotEqual, &minMaxInst);
|
||||
if (!ins->mir()->range() || ins->mir()->range()->canBeNaN())
|
||||
masm.j(Assembler::Parity, &nan);
|
||||
|
||||
// Ordered and equal. The operands are bit-identical unless they are zero
|
||||
// and negative zero. These instructions merge the sign bits in that
|
||||
// case, and are no-ops otherwise.
|
||||
if (ins->mir()->isMax())
|
||||
masm.andps(second, first);
|
||||
else
|
||||
masm.orps(second, first);
|
||||
masm.jump(&done);
|
||||
|
||||
// x86's min/max are not symmetric; if either operand is a NaN, they return
|
||||
// the read-only operand. We need to return a NaN if either operand is a
|
||||
// NaN, so we explicitly check for a NaN in the read-write operand.
|
||||
if (!ins->mir()->range() || ins->mir()->range()->canBeNaN()) {
|
||||
masm.bind(&nan);
|
||||
masm.ucomiss(first, first);
|
||||
masm.j(Assembler::Parity, &done);
|
||||
}
|
||||
|
||||
// When the values are inequal, or second is NaN, x86's min and max will
|
||||
// return the value we need.
|
||||
masm.bind(&minMaxInst);
|
||||
if (ins->mir()->isMax())
|
||||
masm.maxss(second, first);
|
||||
else
|
||||
masm.minss(second, first);
|
||||
|
||||
masm.bind(&done);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorX86Shared::visitAbsD(LAbsD *ins)
|
||||
{
|
||||
@ -2232,6 +2284,63 @@ CodeGeneratorX86Shared::visitSimdExtractElementF(LSimdExtractElementF *ins)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorX86Shared::visitSimdInsertElementI(LSimdInsertElementI *ins)
|
||||
{
|
||||
FloatRegister vector = ToFloatRegister(ins->vector());
|
||||
Register value = ToRegister(ins->value());
|
||||
FloatRegister output = ToFloatRegister(ins->output());
|
||||
MOZ_ASSERT(vector == output); // defineReuseInput(0)
|
||||
|
||||
unsigned component = unsigned(ins->lane());
|
||||
|
||||
// Note that, contrarily to float32x4, we cannot use movd if the inserted
|
||||
// value goes into the first component, as movd clears out the higher lanes
|
||||
// of the output.
|
||||
if (AssemblerX86Shared::HasSSE41()) {
|
||||
masm.pinsrd(component, value, output);
|
||||
return true;
|
||||
}
|
||||
|
||||
masm.reserveStack(Simd128DataSize);
|
||||
masm.storeAlignedInt32x4(vector, Address(StackPointer, 0));
|
||||
masm.store32(value, Address(StackPointer, component * sizeof(int32_t)));
|
||||
masm.loadAlignedInt32x4(Address(StackPointer, 0), output);
|
||||
masm.freeStack(Simd128DataSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorX86Shared::visitSimdInsertElementF(LSimdInsertElementF *ins)
|
||||
{
|
||||
FloatRegister vector = ToFloatRegister(ins->vector());
|
||||
FloatRegister value = ToFloatRegister(ins->value());
|
||||
FloatRegister output = ToFloatRegister(ins->output());
|
||||
MOZ_ASSERT(vector == output); // defineReuseInput(0)
|
||||
|
||||
if (ins->lane() == SimdLane::LaneX) {
|
||||
// As both operands are registers, movss doesn't modify the upper bits
|
||||
// of the destination operand.
|
||||
if (value != output)
|
||||
masm.movss(value, output);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (AssemblerX86Shared::HasSSE41()) {
|
||||
// The input value is in the low float32 of the 'value' FloatRegister.
|
||||
masm.insertps(value, output, masm.insertpsMask(SimdLane::LaneX, ins->lane()));
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned component = unsigned(ins->lane());
|
||||
masm.reserveStack(Simd128DataSize);
|
||||
masm.storeAlignedFloat32x4(vector, Address(StackPointer, 0));
|
||||
masm.storeFloat32(value, Address(StackPointer, component * sizeof(int32_t)));
|
||||
masm.loadAlignedFloat32x4(Address(StackPointer, 0), output);
|
||||
masm.freeStack(Simd128DataSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGeneratorX86Shared::visitSimdSignMaskX4(LSimdSignMaskX4 *ins)
|
||||
{
|
||||
|
@ -151,6 +151,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
|
||||
virtual bool visitDouble(LDouble *ins);
|
||||
virtual bool visitFloat32(LFloat32 *ins);
|
||||
virtual bool visitMinMaxD(LMinMaxD *ins);
|
||||
virtual bool visitMinMaxF(LMinMaxF *ins);
|
||||
virtual bool visitAbsD(LAbsD *ins);
|
||||
virtual bool visitAbsF(LAbsF *ins);
|
||||
virtual bool visitClzI(LClzI *ins);
|
||||
@ -213,6 +214,8 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared
|
||||
bool visitFloat32x4(LFloat32x4 *ins);
|
||||
bool visitSimdExtractElementI(LSimdExtractElementI *lir);
|
||||
bool visitSimdExtractElementF(LSimdExtractElementF *lir);
|
||||
bool visitSimdInsertElementI(LSimdInsertElementI *lir);
|
||||
bool visitSimdInsertElementF(LSimdInsertElementF *lir);
|
||||
bool visitSimdSignMaskX4(LSimdSignMaskX4 *ins);
|
||||
bool visitSimdBinaryCompIx4(LSimdBinaryCompIx4 *lir);
|
||||
bool visitSimdBinaryCompFx4(LSimdBinaryCompFx4 *lir);
|
||||
|
@ -23,7 +23,8 @@ const js::Class OuterWrapperClass =
|
||||
nullptr, /* outerObject */
|
||||
js::proxy_innerObject,
|
||||
nullptr, /* iteratorObject */
|
||||
false /* isWrappedNative */
|
||||
false, /* isWrappedNative */
|
||||
nullptr /* objectMoved */
|
||||
));
|
||||
|
||||
static JSObject *
|
||||
|
@ -252,14 +252,14 @@ namespace js {
|
||||
* allow for potention JSClass extensions.
|
||||
*/
|
||||
#define PROXY_MAKE_EXT(outerObject, innerObject, iteratorObject, \
|
||||
isWrappedNative) \
|
||||
isWrappedNative, objectMoved) \
|
||||
{ \
|
||||
outerObject, \
|
||||
innerObject, \
|
||||
iteratorObject, \
|
||||
isWrappedNative, \
|
||||
js::proxy_WeakmapKeyDelegate, \
|
||||
js::proxy_ObjectMoved \
|
||||
objectMoved \
|
||||
}
|
||||
|
||||
#define PROXY_CLASS_WITH_EXT(name, extraSlots, flags, ext) \
|
||||
@ -313,7 +313,8 @@ namespace js {
|
||||
nullptr, /* outerObject */ \
|
||||
nullptr, /* innerObject */ \
|
||||
nullptr, /* iteratorObject */ \
|
||||
false /* isWrappedNative */ \
|
||||
false, /* isWrappedNative */ \
|
||||
js::proxy_ObjectMoved \
|
||||
))
|
||||
|
||||
/*
|
||||
|
@ -455,6 +455,13 @@ ArenaHeader::checkSynchronizedWithFreeList() const
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
ArenaHeader::unmarkAll()
|
||||
{
|
||||
uintptr_t *word = chunk()->bitmap.arenaBits(this);
|
||||
memset(word, 0, ArenaBitmapWords * sizeof(uintptr_t));
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
Arena::staticAsserts()
|
||||
{
|
||||
@ -2492,6 +2499,9 @@ GCRuntime::releaseRelocatedArenas(ArenaHeader *relocatedList)
|
||||
ArenaHeader *aheader = relocatedList;
|
||||
relocatedList = relocatedList->next;
|
||||
|
||||
// Clear the mark bits
|
||||
aheader->unmarkAll();
|
||||
|
||||
// Mark arena as empty
|
||||
AllocKind thingKind = aheader->getAllocKind();
|
||||
size_t thingSize = aheader->getThingSize();
|
||||
@ -5642,12 +5652,12 @@ GCRuntime::collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
|
||||
JS::gcreason::Reason reason)
|
||||
{
|
||||
/* GC shouldn't be running in parallel execution mode */
|
||||
MOZ_ASSERT(!InParallelSection());
|
||||
MOZ_ALWAYS_TRUE(!InParallelSection());
|
||||
|
||||
JS_AbortIfWrongThread(rt);
|
||||
|
||||
/* If we attempt to invoke the GC while we are running in the GC, assert. */
|
||||
MOZ_ASSERT(!rt->isHeapBusy());
|
||||
MOZ_ALWAYS_TRUE(!rt->isHeapBusy());
|
||||
|
||||
/* The engine never locks across anything that could GC. */
|
||||
MOZ_ASSERT(!rt->currentThreadHasExclusiveAccess());
|
||||
@ -6359,8 +6369,33 @@ JS::AutoAssertOnGC::VerifyIsSafeToGC(JSRuntime *rt)
|
||||
if (rt->gc.isInsideUnsafeRegion())
|
||||
MOZ_CRASH("[AutoAssertOnGC] possible GC in GC-unsafe region");
|
||||
}
|
||||
|
||||
JS::AutoAssertNoAlloc::AutoAssertNoAlloc(JSRuntime *rt)
|
||||
: gc(nullptr)
|
||||
{
|
||||
disallowAlloc(rt);
|
||||
}
|
||||
|
||||
void JS::AutoAssertNoAlloc::disallowAlloc(JSRuntime *rt)
|
||||
{
|
||||
JS_ASSERT(!gc);
|
||||
gc = &rt->gc;
|
||||
gc->disallowAlloc();
|
||||
}
|
||||
|
||||
JS::AutoAssertNoAlloc::~AutoAssertNoAlloc()
|
||||
{
|
||||
if (gc)
|
||||
gc->allowAlloc();
|
||||
}
|
||||
#endif
|
||||
|
||||
JS::AutoAssertGCCallback::AutoAssertGCCallback(JSObject *obj)
|
||||
: AutoSuppressGCAnalysis()
|
||||
{
|
||||
MOZ_ASSERT(obj->runtimeFromMainThread()->isHeapMajorCollecting());
|
||||
}
|
||||
|
||||
#ifdef JSGC_HASH_TABLE_CHECKS
|
||||
void
|
||||
js::gc::CheckHashTablesAfterMovingGC(JSRuntime *rt)
|
||||
|
@ -695,10 +695,8 @@ class ArenaLists
|
||||
/* The background finalization must have stopped at this point. */
|
||||
JS_ASSERT(backgroundFinalizeState[i] == BFS_DONE ||
|
||||
backgroundFinalizeState[i] == BFS_JUST_FINISHED);
|
||||
for (ArenaHeader *aheader = arenaLists[i].head(); aheader; aheader = aheader->next) {
|
||||
uintptr_t *word = aheader->chunk()->bitmap.arenaBits(aheader);
|
||||
memset(word, 0, ArenaBitmapWords * sizeof(uintptr_t));
|
||||
}
|
||||
for (ArenaHeader *aheader = arenaLists[i].head(); aheader; aheader = aheader->next)
|
||||
aheader->unmarkAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -325,37 +325,9 @@ class ZoneCellIterUnderGC : public ZoneCellIterImpl
|
||||
}
|
||||
};
|
||||
|
||||
/* In debug builds, assert that no allocation occurs. */
|
||||
class AutoAssertNoAlloc
|
||||
{
|
||||
#ifdef JS_DEBUG
|
||||
GCRuntime *gc;
|
||||
|
||||
public:
|
||||
AutoAssertNoAlloc() : gc(nullptr) {}
|
||||
explicit AutoAssertNoAlloc(JSRuntime *rt) : gc(nullptr) {
|
||||
disallowAlloc(rt);
|
||||
}
|
||||
void disallowAlloc(JSRuntime *rt) {
|
||||
JS_ASSERT(!gc);
|
||||
gc = &rt->gc;
|
||||
gc->disallowAlloc();
|
||||
}
|
||||
~AutoAssertNoAlloc() {
|
||||
if (gc)
|
||||
gc->allowAlloc();
|
||||
}
|
||||
#else
|
||||
public:
|
||||
AutoAssertNoAlloc() {}
|
||||
explicit AutoAssertNoAlloc(JSRuntime *) {}
|
||||
void disallowAlloc(JSRuntime *rt) {}
|
||||
#endif
|
||||
};
|
||||
|
||||
class ZoneCellIter : public ZoneCellIterImpl
|
||||
{
|
||||
AutoAssertNoAlloc noAlloc;
|
||||
JS::AutoAssertNoAlloc noAlloc;
|
||||
ArenaLists *lists;
|
||||
AllocKind kind;
|
||||
|
||||
|
@ -663,6 +663,12 @@ JSObject::finish(js::FreeOp *fop)
|
||||
fop->free_(elements);
|
||||
}
|
||||
}
|
||||
|
||||
// It's possible that unreachable shapes may be marked whose listp points
|
||||
// into this object. In case this happens, null out the shape's pointer here
|
||||
// so that a moving GC will not try to access the dead object.
|
||||
if (shape_->listp == &shape_)
|
||||
shape_->listp = nullptr;
|
||||
}
|
||||
|
||||
/* static */ inline bool
|
||||
|
@ -71,8 +71,12 @@ WeakMapBase::unmarkCompartment(JSCompartment *c)
|
||||
void
|
||||
WeakMapBase::markAll(JSCompartment *c, JSTracer *tracer)
|
||||
{
|
||||
for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next)
|
||||
m->markIteratively(tracer);
|
||||
JS_ASSERT(tracer->eagerlyTraceWeakMaps() != DoNotTraceWeakMaps);
|
||||
for (WeakMapBase *m = c->gcWeakMapList; m; m = m->next) {
|
||||
m->trace(tracer);
|
||||
if (m->memberOf)
|
||||
gc::MarkObject(tracer, &m->memberOf, "memberOf");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -91,7 +91,7 @@ class WeakMapBase {
|
||||
virtual void finish() = 0;
|
||||
|
||||
// Object that this weak map is part of, if any.
|
||||
JSObject *memberOf;
|
||||
HeapPtrObject memberOf;
|
||||
|
||||
// Compartment that this weak map is part of.
|
||||
JSCompartment *compartment;
|
||||
|
@ -772,10 +772,13 @@ RegExpCompartment::sweep(JSRuntime *rt)
|
||||
bool keep = shared->marked() && !IsStringAboutToBeFinalized(shared->source.unsafeGet());
|
||||
for (size_t i = 0; i < ArrayLength(shared->compilationArray); i++) {
|
||||
RegExpShared::RegExpCompilation &compilation = shared->compilationArray[i];
|
||||
if (keep && compilation.jitCode)
|
||||
keep = !IsJitCodeAboutToBeFinalized(compilation.jitCode.unsafeGet());
|
||||
if (compilation.jitCode &&
|
||||
IsJitCodeAboutToBeFinalized(compilation.jitCode.unsafeGet()))
|
||||
{
|
||||
keep = false;
|
||||
}
|
||||
}
|
||||
if (keep) {
|
||||
if (keep || rt->isHeapCompacting()) {
|
||||
shared->clearMarked();
|
||||
} else {
|
||||
js_delete(shared);
|
||||
|
@ -199,7 +199,7 @@ class RegExpShared
|
||||
void trace(JSTracer *trc);
|
||||
|
||||
bool marked() const { return marked_; }
|
||||
void clearMarked() { JS_ASSERT(marked_); marked_ = false; }
|
||||
void clearMarked() { marked_ = false; }
|
||||
|
||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
};
|
||||
|
@ -897,9 +897,6 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
||||
JS_FNINFO("ClampToUint8",
|
||||
JSNativeThreadSafeWrapper<js::ClampToUint8>,
|
||||
&js::ClampToUint8JitInfo, 1, 0),
|
||||
JS_FNINFO("Memcpy",
|
||||
JSNativeThreadSafeWrapper<js::Memcpy>,
|
||||
&js::MemcpyJitInfo, 5, 0),
|
||||
JS_FN("GetTypedObjectModule", js::GetTypedObjectModule, 0, 0),
|
||||
JS_FN("GetFloat32x4TypeDescr", js::GetFloat32x4TypeDescr, 0, 0),
|
||||
JS_FN("GetInt32x4TypeDescr", js::GetInt32x4TypeDescr, 0, 0),
|
||||
|
@ -49,6 +49,11 @@ public:
|
||||
ClearWrapper();
|
||||
}
|
||||
|
||||
void ObjectMoved(JSObject *obj, const JSObject *old)
|
||||
{
|
||||
UpdateWrapper(obj, old);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~SandboxPrivate() { }
|
||||
|
||||
|
@ -310,7 +310,7 @@ sandbox_resolve(JSContext *cx, HandleObject obj, HandleId id)
|
||||
}
|
||||
|
||||
static void
|
||||
sandbox_finalize(JSFreeOp *fop, JSObject *obj)
|
||||
sandbox_finalize(js::FreeOp *fop, JSObject *obj)
|
||||
{
|
||||
nsIScriptObjectPrincipal *sop =
|
||||
static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(obj));
|
||||
@ -324,6 +324,15 @@ sandbox_finalize(JSFreeOp *fop, JSObject *obj)
|
||||
DestroyProtoAndIfaceCache(obj);
|
||||
}
|
||||
|
||||
static void
|
||||
sandbox_moved(JSObject *obj, const JSObject *old)
|
||||
{
|
||||
nsIScriptObjectPrincipal *sop =
|
||||
static_cast<nsIScriptObjectPrincipal *>(xpc_GetJSPrivate(obj));
|
||||
MOZ_ASSERT(sop);
|
||||
static_cast<SandboxPrivate *>(sop)->ObjectMoved(obj, old);
|
||||
}
|
||||
|
||||
static bool
|
||||
sandbox_convert(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp)
|
||||
{
|
||||
@ -443,22 +452,42 @@ sandbox_addProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleV
|
||||
|
||||
#define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
|
||||
|
||||
static const JSClass SandboxClass = {
|
||||
static const js::Class SandboxClass = {
|
||||
"Sandbox",
|
||||
XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
|
||||
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
|
||||
sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize,
|
||||
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
|
||||
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
|
||||
JS_NULL_CLASS_SPEC,
|
||||
{
|
||||
nullptr, /* outerObject */
|
||||
nullptr, /* innerObject */
|
||||
nullptr, /* iteratorObject */
|
||||
false, /* isWrappedNative */
|
||||
nullptr, /* weakmapKeyDelegateOp */
|
||||
sandbox_moved /* objectMovedOp */
|
||||
},
|
||||
JS_NULL_OBJECT_OPS
|
||||
};
|
||||
|
||||
// Note to whomever comes here to remove addProperty hooks: billm has promised
|
||||
// to do the work for this class.
|
||||
static const JSClass SandboxWriteToProtoClass = {
|
||||
static const js::Class SandboxWriteToProtoClass = {
|
||||
"Sandbox",
|
||||
XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1),
|
||||
sandbox_addProperty, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
|
||||
sandbox_enumerate, sandbox_resolve, sandbox_convert, sandbox_finalize,
|
||||
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook
|
||||
nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
|
||||
JS_NULL_CLASS_SPEC,
|
||||
{
|
||||
nullptr, /* outerObject */
|
||||
nullptr, /* innerObject */
|
||||
nullptr, /* iteratorObject */
|
||||
false, /* isWrappedNative */
|
||||
nullptr, /* weakmapKeyDelegateOp */
|
||||
sandbox_moved /* objectMovedOp */
|
||||
},
|
||||
JS_NULL_OBJECT_OPS
|
||||
};
|
||||
|
||||
static const JSFunctionSpec SandboxFunctions[] = {
|
||||
@ -471,7 +500,7 @@ static const JSFunctionSpec SandboxFunctions[] = {
|
||||
bool
|
||||
xpc::IsSandbox(JSObject *obj)
|
||||
{
|
||||
const JSClass *clasp = GetObjectJSClass(obj);
|
||||
const Class *clasp = GetObjectClass(obj);
|
||||
return clasp == &SandboxClass || clasp == &SandboxWriteToProtoClass;
|
||||
}
|
||||
|
||||
@ -861,11 +890,11 @@ xpc::CreateSandboxObject(JSContext *cx, MutableHandleValue vp, nsISupports *prin
|
||||
|
||||
compartmentOptions.setAddonId(addonId);
|
||||
|
||||
const JSClass *clasp = options.writeToGlobalPrototype
|
||||
? &SandboxWriteToProtoClass
|
||||
: &SandboxClass;
|
||||
const Class *clasp = options.writeToGlobalPrototype
|
||||
? &SandboxWriteToProtoClass
|
||||
: &SandboxClass;
|
||||
|
||||
RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, clasp,
|
||||
RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, js::Jsvalify(clasp),
|
||||
principal, compartmentOptions));
|
||||
if (!sandbox)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -991,6 +991,20 @@ XPCWrappedNative::FlatJSObjectFinalized()
|
||||
Release();
|
||||
}
|
||||
|
||||
void
|
||||
XPCWrappedNative::FlatJSObjectMoved(JSObject *obj, const JSObject *old)
|
||||
{
|
||||
JS::AutoAssertGCCallback inCallback(obj);
|
||||
MOZ_ASSERT(mFlatJSObject == old);
|
||||
|
||||
nsWrapperCache *cache = nullptr;
|
||||
CallQueryInterface(mIdentity, &cache);
|
||||
if (cache)
|
||||
cache->UpdateWrapper(obj, old);
|
||||
|
||||
mFlatJSObject = obj;
|
||||
}
|
||||
|
||||
void
|
||||
XPCWrappedNative::SystemIsBeingShutDown()
|
||||
{
|
||||
|
@ -572,6 +572,17 @@ WrappedNativeFinalize(js::FreeOp *fop, JSObject *obj, WNHelperType helperType)
|
||||
wrapper->FlatJSObjectFinalized();
|
||||
}
|
||||
|
||||
static void
|
||||
WrappedNativeObjectMoved(JSObject *obj, const JSObject *old)
|
||||
{
|
||||
nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
|
||||
wrapper->FlatJSObjectMoved(obj, old);
|
||||
}
|
||||
|
||||
static void
|
||||
XPC_WN_NoHelper_Finalize(js::FreeOp *fop, JSObject *obj)
|
||||
{
|
||||
@ -658,7 +669,9 @@ const XPCWrappedNativeJSClass XPC_WN_NoHelper_JSClass = {
|
||||
nullptr, // outerObject
|
||||
nullptr, // innerObject
|
||||
nullptr, // iteratorObject
|
||||
true, // isWrappedNative
|
||||
true, // isWrappedNative
|
||||
nullptr, // weakmapKeyDelegateOp
|
||||
WrappedNativeObjectMoved
|
||||
},
|
||||
|
||||
// ObjectOps
|
||||
@ -1165,6 +1178,7 @@ XPCNativeScriptableShared::PopulateJSClass()
|
||||
mJSClass.base.trace = XPCWrappedNative::Trace;
|
||||
|
||||
mJSClass.base.ext.isWrappedNative = true;
|
||||
mJSClass.base.ext.objectMovedOp = WrappedNativeObjectMoved;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
@ -2141,6 +2141,7 @@ public:
|
||||
nsresult RescueOrphans();
|
||||
|
||||
void FlatJSObjectFinalized();
|
||||
void FlatJSObjectMoved(JSObject *obj, const JSObject *old);
|
||||
|
||||
void SystemIsBeingShutDown();
|
||||
|
||||
|
@ -1402,7 +1402,7 @@ nsBidiPresUtils::RepositionFrame(nsIFrame* aFrame,
|
||||
}
|
||||
|
||||
nscoord start = aStart;
|
||||
nscoord frameWidth = aFrame->GetSize().width;
|
||||
nscoord frameISize = aFrame->ISize(aLineWM);
|
||||
|
||||
if (!IsBidiLeaf(aFrame))
|
||||
{
|
||||
@ -1436,7 +1436,7 @@ nsBidiPresUtils::RepositionFrame(nsIFrame* aFrame,
|
||||
iCoord,
|
||||
aContinuationStates,
|
||||
frameWM,
|
||||
frameWidth);
|
||||
frameISize);
|
||||
index++;
|
||||
frame = reverseOrder ?
|
||||
childList[childList.Length() - index - 1] :
|
||||
@ -1448,7 +1448,7 @@ nsBidiPresUtils::RepositionFrame(nsIFrame* aFrame,
|
||||
}
|
||||
aStart += iCoord;
|
||||
} else {
|
||||
aStart += frameWidth;
|
||||
aStart += frameISize;
|
||||
}
|
||||
|
||||
LogicalRect logicalRect(aLineWM, aFrame->GetRect(), aLineWidth);
|
||||
|
@ -33,23 +33,40 @@ function execTests() {
|
||||
}
|
||||
|
||||
var eatSpace;
|
||||
var deleteImmediately;
|
||||
|
||||
function getPrefs() {
|
||||
function getPrefs(branch) {
|
||||
const prefSvcContractID = "@mozilla.org/preferences-service;1";
|
||||
const prefSvcIID = Components.interfaces.nsIPrefService;
|
||||
return Components.classes[prefSvcContractID].getService(prefSvcIID)
|
||||
.getBranch("layout.word_select.");
|
||||
.getBranch(branch);
|
||||
}
|
||||
|
||||
function setPref(branch, pref, newValue) {
|
||||
getPrefs(branch).setBoolPref(pref, newValue);
|
||||
return newValue;
|
||||
}
|
||||
|
||||
function restorePref(branch, pref, newValue) {
|
||||
try {
|
||||
getPrefs(branch).clearUserPref(pref);
|
||||
} catch(ex) {}
|
||||
}
|
||||
|
||||
function setEatSpace(newValue) {
|
||||
getPrefs().setBoolPref("eat_space_to_next_word", newValue);
|
||||
eatSpace = newValue;
|
||||
eatSpace = setPref("layout.word_select.", "eat_space_to_next_word", newValue);
|
||||
}
|
||||
|
||||
function restoreEatSpace() {
|
||||
try {
|
||||
getPrefs().clearUserPref("eat_space_to_next_word");
|
||||
} catch(ex) {}
|
||||
restorePref("layout.word_select.", "eat_space_to_next_word");
|
||||
}
|
||||
|
||||
function setDeleteImmediately(newValue) {
|
||||
deleteImmediately = setPref("bidi.edit.", "delete_immediately", newValue);
|
||||
}
|
||||
|
||||
function restoreDeleteImmediately() {
|
||||
restorePref("bidi.edit.", "delete_immediately");
|
||||
}
|
||||
|
||||
function doCommand(cmd) {
|
||||
@ -86,7 +103,7 @@ function execTests() {
|
||||
|
||||
function testDelete(node, offset, text, richtext) {
|
||||
doCommand("cmd_deleteCharForward");
|
||||
var msg = "Delete broken in \"" + editor.innerHTML + "\", offset " + offset;
|
||||
var msg = "Delete broken in \"" + editor.innerHTML + "\", offset " + offset + " with deleteImmediately=" + deleteImmediately;
|
||||
if(typeof node == 'function'){
|
||||
node = node();
|
||||
}
|
||||
@ -99,7 +116,7 @@ function execTests() {
|
||||
|
||||
function testBackspace(node, offset, text) {
|
||||
doCommand("cmd_deleteCharBackward");
|
||||
var msg = "Backspace broken in \"" + editor.innerHTML + "\", offset " + offset;
|
||||
var msg = "Backspace broken in \"" + editor.innerHTML + "\", offset " + offset + " with deleteImmediately=" + deleteImmediately;
|
||||
is(sel.anchorNode, node, msg);
|
||||
|
||||
is(sel.anchorOffset, offset, msg);
|
||||
@ -195,10 +212,38 @@ function execTests() {
|
||||
|
||||
// Tests for Bug 419406
|
||||
var s = "helloשלום";
|
||||
|
||||
setDeleteImmediately(true);
|
||||
|
||||
setupTest(s, 4);
|
||||
testRight(editor.firstChild, 5);
|
||||
testDelete(editor.firstChild, 5, "helloלום");
|
||||
|
||||
setDeleteImmediately(false);
|
||||
|
||||
setupTest(s, 4);
|
||||
testRight(editor.firstChild, 5);
|
||||
testDelete(editor.firstChild, 5, "helloשלום");
|
||||
|
||||
// Tests for bug 1034337
|
||||
s = "اهلاhello";
|
||||
|
||||
setDeleteImmediately(true);
|
||||
|
||||
setupTest(s, 4);
|
||||
// first delete an ltr character to make sure that the caret is ltr
|
||||
testDelete(editor.firstChild, 4, "اهلاello");
|
||||
testBackspace(editor.firstChild, 3, "اهلello");
|
||||
|
||||
setDeleteImmediately(false);
|
||||
|
||||
setupTest(s, 4);
|
||||
// first delete an ltr character to make sure that the caret is ltr
|
||||
testDelete(editor.firstChild, 4, "اهلاello");
|
||||
testBackspace(editor.firstChild, 4, "اهلاello");
|
||||
|
||||
restoreDeleteImmediately();
|
||||
|
||||
// Tests for Bug 462188
|
||||
setupTest("You should not see this text.", 29);
|
||||
testDeletePrevWord(editor.firstChild, 24, "You should not see this ");
|
||||
|
@ -294,16 +294,18 @@ void TestBasicElements()
|
||||
|
||||
void TestStringEscaping()
|
||||
{
|
||||
// This test uses hexadecimal character escapes because UTF8 literals cause
|
||||
// problems for some compilers (see bug 1069726).
|
||||
const char* expected = "\
|
||||
{\n\
|
||||
\"ascii\": \"~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\\\"! \\u001f\\u001e\\u001d\\u001c\\u001b\\u001a\\u0019\\u0018\\u0017\\u0016\\u0015\\u0014\\u0013\\u0012\\u0011\\u0010\\u000f\\u000e\\r\\f\\u000b\\n\\t\\b\\u0007\\u0006\\u0005\\u0004\\u0003\\u0002\\u0001\",\n\
|
||||
\"مرحبا هناك\": true,\n\
|
||||
\"բարեւ չկա\": -123,\n\
|
||||
\"你好\": 1.234,\n\
|
||||
\"γεια εκεί\": \"سلام\",\n\
|
||||
\"halló þarna\": \"0x1234\",\n\
|
||||
\"こんにちは\": {\n\
|
||||
\"привет\": [\n\
|
||||
\"ascii\": \"\x7F~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\\\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\\\"! \\u001f\\u001e\\u001d\\u001c\\u001b\\u001a\\u0019\\u0018\\u0017\\u0016\\u0015\\u0014\\u0013\\u0012\\u0011\\u0010\\u000f\\u000e\\r\\f\\u000b\\n\\t\\b\\u0007\\u0006\\u0005\\u0004\\u0003\\u0002\\u0001\",\n\
|
||||
\"\xD9\x85\xD8\xB1\xD8\xAD\xD8\xA8\xD8\xA7 \xD9\x87\xD9\x86\xD8\xA7\xD9\x83\": true,\n\
|
||||
\"\xD5\xA2\xD5\xA1\xD6\x80\xD5\xA5\xD6\x82 \xD5\xB9\xD5\xAF\xD5\xA1\": -123,\n\
|
||||
\"\xE4\xBD\xA0\xE5\xA5\xBD\": 1.234,\n\
|
||||
\"\xCE\xB3\xCE\xB5\xCE\xB9\xCE\xB1 \xCE\xB5\xCE\xBA\xCE\xB5\xCE\xAF\": \"\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85\",\n\
|
||||
\"hall\xC3\xB3 \xC3\xBE" "arna\": \"0x1234\",\n\
|
||||
\"\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF\": {\n\
|
||||
\"\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82\": [\n\
|
||||
]\n\
|
||||
}\n\
|
||||
}\n\
|
||||
@ -323,14 +325,14 @@ void TestStringEscaping()
|
||||
w.StringProperty("ascii", buf);
|
||||
|
||||
// Test lots of unicode stuff. Note that this file is encoded as UTF-8.
|
||||
w.BoolProperty("مرحبا هناك", true);
|
||||
w.IntProperty("բարեւ չկա", -123);
|
||||
w.DoubleProperty("你好", 1.234);
|
||||
w.StringProperty("γεια εκεί", "سلام");
|
||||
w.PointerProperty("halló þarna", (void*)0x1234);
|
||||
w.StartObjectProperty("こんにちは");
|
||||
w.BoolProperty("\xD9\x85\xD8\xB1\xD8\xAD\xD8\xA8\xD8\xA7 \xD9\x87\xD9\x86\xD8\xA7\xD9\x83", true);
|
||||
w.IntProperty("\xD5\xA2\xD5\xA1\xD6\x80\xD5\xA5\xD6\x82 \xD5\xB9\xD5\xAF\xD5\xA1", -123);
|
||||
w.DoubleProperty("\xE4\xBD\xA0\xE5\xA5\xBD", 1.234);
|
||||
w.StringProperty("\xCE\xB3\xCE\xB5\xCE\xB9\xCE\xB1 \xCE\xB5\xCE\xBA\xCE\xB5\xCE\xAF", "\xD8\xB3\xD9\x84\xD8\xA7\xD9\x85");
|
||||
w.PointerProperty("hall\xC3\xB3 \xC3\xBE" "arna", (void*)0x1234);
|
||||
w.StartObjectProperty("\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF");
|
||||
{
|
||||
w.StartArrayProperty("привет");
|
||||
w.StartArrayProperty("\xD0\xBF\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82");
|
||||
w.EndArray();
|
||||
}
|
||||
w.EndObject();
|
||||
|
@ -319,7 +319,7 @@ nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache, nsJARInputThunk **resu
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJARChannel::LookupFile()
|
||||
nsJARChannel::LookupFile(bool aAllowAsync)
|
||||
{
|
||||
LOG(("nsJARChannel::LookupFile [this=%x %s]\n", this, mSpec.get()));
|
||||
|
||||
@ -387,6 +387,11 @@ nsJARChannel::LookupFile()
|
||||
}
|
||||
}
|
||||
|
||||
if (!aAllowAsync) {
|
||||
mJarFile = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mOpeningRemote = true;
|
||||
|
||||
#if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA)
|
||||
@ -798,7 +803,7 @@ nsJARChannel::Open(nsIInputStream **stream)
|
||||
mJarFile = nullptr;
|
||||
mIsUnsafe = true;
|
||||
|
||||
nsresult rv = LookupFile();
|
||||
nsresult rv = LookupFile(false);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
@ -835,7 +840,7 @@ nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
|
||||
// Initialize mProgressSink
|
||||
NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
|
||||
|
||||
nsresult rv = LookupFile();
|
||||
nsresult rv = LookupFile(true);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
@ -910,7 +915,7 @@ nsJARChannel::GetJarFile(nsIFile **aFile)
|
||||
NS_IMETHODIMP
|
||||
nsJARChannel::GetZipEntry(nsIZipEntry **aZipEntry)
|
||||
{
|
||||
nsresult rv = LookupFile();
|
||||
nsresult rv = LookupFile(false);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user