зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to m-c.
This commit is contained in:
Коммит
267369f360
|
@ -20,7 +20,6 @@
|
|||
#include "nsIIPCSerializableInputStream.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsIMIMEService.h"
|
||||
#include "nsIPlatformCharset.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsIUnicharInputStream.h"
|
||||
#include "nsIUnicodeDecoder.h"
|
||||
|
|
|
@ -48,11 +48,15 @@ static const uint32_t STALL_MS = 3000;
|
|||
// fluctuating bitrates.
|
||||
static const int64_t CAN_PLAY_THROUGH_MARGIN = 1;
|
||||
|
||||
// avoid redefined macro in unified build
|
||||
#undef DECODER_LOG
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo* gMediaDecoderLog;
|
||||
#define DECODER_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
|
||||
#define DECODER_LOG(type, msg, ...) \
|
||||
PR_LOG(gMediaDecoderLog, type, ("Decoder=%p " msg, this, ##__VA_ARGS__))
|
||||
#else
|
||||
#define DECODER_LOG(type, msg)
|
||||
#define DECODER_LOG(type, msg, ...)
|
||||
#endif
|
||||
|
||||
class MediaMemoryTracker : public nsIMemoryReporter
|
||||
|
@ -314,8 +318,7 @@ void MediaDecoder::RecreateDecodedStream(int64_t aStartTimeUSecs)
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
GetReentrantMonitor().AssertCurrentThreadIn();
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("MediaDecoder::RecreateDecodedStream this=%p aStartTimeUSecs=%lld!",
|
||||
this, (long long)aStartTimeUSecs));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "RecreateDecodedStream aStartTimeUSecs=%lld!", aStartTimeUSecs);
|
||||
|
||||
DestroyDecodedStream();
|
||||
|
||||
|
@ -347,8 +350,7 @@ void MediaDecoder::AddOutputStream(ProcessedMediaStream* aStream,
|
|||
bool aFinishWhenEnded)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("MediaDecoder::AddOutputStream this=%p aStream=%p!",
|
||||
this, aStream));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "AddOutputStream aStream=%p!", aStream);
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
|
@ -509,7 +511,7 @@ nsresult MediaDecoder::OpenResource(nsIStreamListener** aStreamListener)
|
|||
|
||||
nsresult rv = mResource->Open(aStreamListener);
|
||||
if (NS_FAILED(rv)) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Failed to open stream!", this));
|
||||
DECODER_LOG(PR_LOG_WARNING, "Failed to open stream!");
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
@ -526,7 +528,7 @@ nsresult MediaDecoder::Load(nsIStreamListener** aStreamListener,
|
|||
|
||||
mDecoderStateMachine = CreateStateMachine();
|
||||
if (!mDecoderStateMachine) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Failed to create state machine!", this));
|
||||
DECODER_LOG(PR_LOG_WARNING, "Failed to create state machine!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -541,7 +543,7 @@ nsresult MediaDecoder::InitializeStateMachine(MediaDecoder* aCloneDonor)
|
|||
MediaDecoder* cloneDonor = static_cast<MediaDecoder*>(aCloneDonor);
|
||||
if (NS_FAILED(mDecoderStateMachine->Init(cloneDonor ?
|
||||
cloneDonor->mDecoderStateMachine : nullptr))) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Failed to init state machine!", this));
|
||||
DECODER_LOG(PR_LOG_WARNING, "Failed to init state machine!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
{
|
||||
|
@ -1233,7 +1235,7 @@ void MediaDecoder::DurationChanged()
|
|||
SetInfinite(mDuration == -1);
|
||||
|
||||
if (mOwner && oldDuration != mDuration && !IsInfinite()) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p duration changed to %lld", this, mDuration));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Duration changed to %lld", mDuration);
|
||||
mOwner->DispatchEvent(NS_LITERAL_STRING("durationchange"));
|
||||
}
|
||||
}
|
||||
|
@ -1799,3 +1801,5 @@ MediaMemoryTracker::~MediaMemoryTracker()
|
|||
|
||||
} // namespace mozilla
|
||||
|
||||
// avoid redefined macro in unified build
|
||||
#undef DECODER_LOG
|
||||
|
|
|
@ -43,11 +43,23 @@ using namespace mozilla::layers;
|
|||
using namespace mozilla::dom;
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
// avoid redefined macro in unified build
|
||||
#undef DECODER_LOG
|
||||
#undef VERBOSE_LOG
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
extern PRLogModuleInfo* gMediaDecoderLog;
|
||||
#define DECODER_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
|
||||
#define DECODER_LOG(type, msg, ...) \
|
||||
PR_LOG(gMediaDecoderLog, type, ("Decoder=%p " msg, mDecoder.get(), ##__VA_ARGS__))
|
||||
#define VERBOSE_LOG(msg, ...) \
|
||||
PR_BEGIN_MACRO \
|
||||
if (!PR_GetEnv("MOZ_QUIET")) { \
|
||||
DECODER_LOG(PR_LOG_DEBUG, msg, ##__VA_ARGS__); \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
#else
|
||||
#define DECODER_LOG(type, msg)
|
||||
#define DECODER_LOG(type, msg, ...)
|
||||
#define VERBOSE_LOG(msg, ...)
|
||||
#endif
|
||||
|
||||
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
|
||||
|
@ -195,7 +207,8 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
|
|||
mMinimizePreroll(false),
|
||||
mDecodeThreadWaiting(false),
|
||||
mRealTime(aRealTime),
|
||||
mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED)
|
||||
mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED),
|
||||
mTimerId(0)
|
||||
{
|
||||
MOZ_COUNT_CTOR(MediaDecoderStateMachine);
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
@ -235,9 +248,8 @@ MediaDecoderStateMachine::~MediaDecoderStateMachine()
|
|||
mDecodeTaskQueue = nullptr;
|
||||
}
|
||||
|
||||
if (mTimer) {
|
||||
mTimer->Cancel();
|
||||
}
|
||||
// No need to cancel the timer here for we've done that in
|
||||
// TimeoutExpired() triggered by Shutdown()
|
||||
mTimer = nullptr;
|
||||
mReader = nullptr;
|
||||
|
||||
|
@ -299,8 +311,8 @@ void MediaDecoderStateMachine::SendStreamAudio(AudioData* aAudio,
|
|||
return;
|
||||
if (audioWrittenOffset.value() < frameOffset.value()) {
|
||||
// Write silence to catch up
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing %d frames of silence to MediaStream",
|
||||
mDecoder.get(), int32_t(frameOffset.value() - audioWrittenOffset.value())));
|
||||
VERBOSE_LOG("writing %d frames of silence to MediaStream",
|
||||
int32_t(frameOffset.value() - audioWrittenOffset.value()));
|
||||
AudioSegment silence;
|
||||
silence.InsertNullDataAtStart(frameOffset.value() - audioWrittenOffset.value());
|
||||
aStream->mAudioFramesWritten += silence.GetDuration();
|
||||
|
@ -329,8 +341,8 @@ void MediaDecoderStateMachine::SendStreamAudio(AudioData* aAudio,
|
|||
channels.AppendElement(bufferData + i*aAudio->mFrames + offset);
|
||||
}
|
||||
aOutput->AppendFrames(buffer.forget(), channels, aAudio->mFrames);
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing %d frames of data to MediaStream for AudioData at %lld",
|
||||
mDecoder.get(), aAudio->mFrames - int32_t(offset), aAudio->mTime));
|
||||
VERBOSE_LOG("writing %d frames of data to MediaStream for AudioData at %lld",
|
||||
aAudio->mFrames - int32_t(offset), aAudio->mTime);
|
||||
aStream->mAudioFramesWritten += aAudio->mFrames - int32_t(offset);
|
||||
}
|
||||
|
||||
|
@ -421,9 +433,8 @@ void MediaDecoderStateMachine::SendStreamData()
|
|||
for (uint32_t i = 0; i < video.Length(); ++i) {
|
||||
VideoData* v = video[i];
|
||||
if (stream->mNextVideoTime < v->mTime) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing last video to MediaStream %p for %lldus",
|
||||
mDecoder.get(), mediaStream,
|
||||
v->mTime - stream->mNextVideoTime));
|
||||
VERBOSE_LOG("writing last video to MediaStream %p for %lldus",
|
||||
mediaStream, v->mTime - stream->mNextVideoTime);
|
||||
// Write last video frame to catch up. mLastVideoImage can be null here
|
||||
// which is fine, it just means there's no video.
|
||||
WriteVideoToMediaStream(stream->mLastVideoImage,
|
||||
|
@ -432,9 +443,8 @@ void MediaDecoderStateMachine::SendStreamData()
|
|||
stream->mNextVideoTime = v->mTime;
|
||||
}
|
||||
if (stream->mNextVideoTime < v->GetEndTime()) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder writing video frame %lldus to MediaStream %p for %lldus",
|
||||
mDecoder.get(), v->mTime, mediaStream,
|
||||
v->GetEndTime() - stream->mNextVideoTime));
|
||||
VERBOSE_LOG("writing video frame %lldus to MediaStream %p for %lldus",
|
||||
v->mTime, mediaStream, v->GetEndTime() - stream->mNextVideoTime);
|
||||
WriteVideoToMediaStream(v->mImage,
|
||||
v->GetEndTime() - stream->mNextVideoTime, v->mDisplay,
|
||||
&output);
|
||||
|
@ -442,8 +452,8 @@ void MediaDecoderStateMachine::SendStreamData()
|
|||
stream->mLastVideoImage = v->mImage;
|
||||
stream->mLastVideoImageDisplaySize = v->mDisplay;
|
||||
} else {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder skipping writing video frame %lldus (end %lldus) to MediaStream",
|
||||
mDecoder.get(), v->mTime, v->GetEndTime()));
|
||||
VERBOSE_LOG("skipping writing video frame %lldus (end %lldus) to MediaStream",
|
||||
v->mTime, v->GetEndTime());
|
||||
}
|
||||
}
|
||||
if (output.GetDuration() > 0) {
|
||||
|
@ -598,7 +608,7 @@ MediaDecoderStateMachine::DecodeVideo()
|
|||
!HasLowUndecodedData())
|
||||
{
|
||||
mSkipToNextKeyFrame = true;
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Skipping video decode to the next keyframe", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Skipping video decode to the next keyframe");
|
||||
}
|
||||
|
||||
// Time the video decode, so that if it's slow, we can increase our low
|
||||
|
@ -625,9 +635,8 @@ MediaDecoderStateMachine::DecodeVideo()
|
|||
std::min(THRESHOLD_FACTOR * DurationToUsecs(decodeTime), AMPLE_AUDIO_USECS);
|
||||
mAmpleAudioThresholdUsecs = std::max(THRESHOLD_FACTOR * mLowAudioThresholdUsecs,
|
||||
mAmpleAudioThresholdUsecs);
|
||||
DECODER_LOG(PR_LOG_DEBUG,
|
||||
("Slow video decode, set mLowAudioThresholdUsecs=%lld mAmpleAudioThresholdUsecs=%lld",
|
||||
mLowAudioThresholdUsecs, mAmpleAudioThresholdUsecs));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Slow video decode, set mLowAudioThresholdUsecs=%lld mAmpleAudioThresholdUsecs=%lld",
|
||||
mLowAudioThresholdUsecs, mAmpleAudioThresholdUsecs);
|
||||
}
|
||||
|
||||
SendStreamData();
|
||||
|
@ -715,9 +724,8 @@ MediaDecoderStateMachine::CheckIfDecodeComplete()
|
|||
DispatchDecodeTasksIfNeeded();
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
DECODER_LOG(PR_LOG_DEBUG,
|
||||
("%p CheckIfDecodeComplete %scompleted", mDecoder.get(),
|
||||
((mState == DECODER_STATE_COMPLETED) ? "" : "NOT ")));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "CheckIfDecodeComplete %scompleted",
|
||||
((mState == DECODER_STATE_COMPLETED) ? "" : "NOT "));
|
||||
}
|
||||
|
||||
bool MediaDecoderStateMachine::IsPlaying()
|
||||
|
@ -753,7 +761,7 @@ static void WriteSilence(AudioStream* aStream, uint32_t aFrames)
|
|||
void MediaDecoderStateMachine::AudioLoop()
|
||||
{
|
||||
NS_ASSERTION(OnAudioThread(), "Should be on audio thread.");
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Begun audio thread/loop", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Begun audio thread/loop");
|
||||
int64_t audioDuration = 0;
|
||||
int64_t audioStartTime = -1;
|
||||
uint32_t channels, rate;
|
||||
|
@ -891,8 +899,7 @@ void MediaDecoderStateMachine::AudioLoop()
|
|||
// hardware so that the next audio chunk begins playback at the correct
|
||||
// time.
|
||||
missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value());
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder playing %d frames of silence",
|
||||
mDecoder.get(), int32_t(missingFrames.value())));
|
||||
VERBOSE_LOG("playing %d frames of silence", int32_t(missingFrames.value()));
|
||||
framesWritten = PlaySilence(static_cast<uint32_t>(missingFrames.value()),
|
||||
channels, playedFrames.value());
|
||||
} else {
|
||||
|
@ -945,7 +952,7 @@ void MediaDecoderStateMachine::AudioLoop()
|
|||
}
|
||||
}
|
||||
}
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Reached audio stream end.", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Reached audio stream end.");
|
||||
{
|
||||
// Must hold lock while shutting down and anulling the audio stream to prevent
|
||||
// state machine thread trying to use it while we're destroying it.
|
||||
|
@ -960,7 +967,7 @@ void MediaDecoderStateMachine::AudioLoop()
|
|||
}
|
||||
}
|
||||
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Audio stream finished playing, audio thread exit", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Audio stream finished playing, audio thread exit");
|
||||
}
|
||||
|
||||
uint32_t MediaDecoderStateMachine::PlaySilence(uint32_t aFrames,
|
||||
|
@ -991,10 +998,8 @@ uint32_t MediaDecoderStateMachine::PlayFromAudioQueue(uint64_t aFrameOffset,
|
|||
}
|
||||
int64_t offset = -1;
|
||||
uint32_t frames = 0;
|
||||
if (!PR_GetEnv("MOZ_QUIET")) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder playing %d frames of data to stream for AudioData at %lld",
|
||||
mDecoder.get(), audio->mFrames, audio->mTime));
|
||||
}
|
||||
VERBOSE_LOG("playing %d frames of data to stream for AudioData at %lld",
|
||||
audio->mFrames, audio->mTime);
|
||||
mAudioStream->Write(audio->mAudioData,
|
||||
audio->mFrames);
|
||||
|
||||
|
@ -1045,7 +1050,7 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
|
|||
|
||||
void MediaDecoderStateMachine::StopPlayback()
|
||||
{
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p StopPlayback()", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "StopPlayback()");
|
||||
|
||||
AssertCurrentThreadInMonitor();
|
||||
|
||||
|
@ -1088,7 +1093,7 @@ int64_t MediaDecoderStateMachine::GetCurrentTimeViaMediaStreamSync()
|
|||
|
||||
void MediaDecoderStateMachine::StartPlayback()
|
||||
{
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p StartPlayback()", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "StartPlayback()");
|
||||
|
||||
NS_ASSERTION(!IsPlaying(), "Shouldn't be playing when StartPlayback() is called");
|
||||
AssertCurrentThreadInMonitor();
|
||||
|
@ -1300,7 +1305,7 @@ void MediaDecoderStateMachine::Shutdown()
|
|||
|
||||
// Change state before issuing shutdown request to threads so those
|
||||
// threads can start exiting cleanly during the Shutdown call.
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state to SHUTDOWN", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state to SHUTDOWN");
|
||||
ScheduleStateMachine();
|
||||
mState = DECODER_STATE_SHUTDOWN;
|
||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
|
@ -1368,7 +1373,7 @@ void MediaDecoderStateMachine::Play()
|
|||
// when the state machine notices the decoder's state change to PLAYING.
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
if (mState == DECODER_STATE_BUFFERING) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state from BUFFERING to DECODING", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state from BUFFERING to DECODING");
|
||||
mState = DECODER_STATE_DECODING;
|
||||
mDecodeStartTime = TimeStamp::Now();
|
||||
}
|
||||
|
@ -1442,7 +1447,7 @@ void MediaDecoderStateMachine::Seek(const SeekTarget& aTarget)
|
|||
mSeekTarget = SeekTarget(seekTime, aTarget.mType);
|
||||
|
||||
mBasePosition = seekTime - mStartTime;
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state to SEEKING (to %ld)", mDecoder.get(), mSeekTarget.mTime));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state to SEEKING (to %ld)", mSeekTarget.mTime);
|
||||
mState = DECODER_STATE_SEEKING;
|
||||
if (mDecoder->GetDecodedStream()) {
|
||||
mDecoder->RecreateDecodedStream(seekTime - mStartTime);
|
||||
|
@ -1464,7 +1469,7 @@ void MediaDecoderStateMachine::StopAudioThread()
|
|||
mStopAudioThread = true;
|
||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
if (mAudioThread) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Shutdown audio thread", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Shutdown audio thread");
|
||||
{
|
||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||
mAudioThread->Shutdown();
|
||||
|
@ -1512,10 +1517,9 @@ MediaDecoderStateMachine::SetReaderIdle()
|
|||
#ifdef PR_LOGGING
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p SetReaderIdle() audioQueue=%lld videoQueue=%lld",
|
||||
mDecoder.get(),
|
||||
GetDecodedAudioDuration(),
|
||||
mReader->VideoQueue().Duration()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "SetReaderIdle() audioQueue=%lld videoQueue=%lld",
|
||||
GetDecodedAudioDuration(),
|
||||
mReader->VideoQueue().Duration());
|
||||
}
|
||||
#endif
|
||||
MOZ_ASSERT(OnDecodeThread());
|
||||
|
@ -1525,7 +1529,7 @@ MediaDecoderStateMachine::SetReaderIdle()
|
|||
void
|
||||
MediaDecoderStateMachine::SetReaderActive()
|
||||
{
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p SetReaderActive()", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "SetReaderActive()");
|
||||
MOZ_ASSERT(OnDecodeThread());
|
||||
mReader->SetActive();
|
||||
}
|
||||
|
@ -1699,7 +1703,7 @@ MediaDecoderStateMachine::StartAudioThread()
|
|||
nullptr,
|
||||
MEDIA_THREAD_STACK_SIZE);
|
||||
if (NS_FAILED(rv)) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state to SHUTDOWN because failed to create audio thread", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_WARNING, "Changed state to SHUTDOWN because failed to create audio thread");
|
||||
mState = DECODER_STATE_SHUTDOWN;
|
||||
return rv;
|
||||
}
|
||||
|
@ -1776,7 +1780,7 @@ MediaDecoderStateMachine::DecodeError()
|
|||
// Change state to shutdown before sending error report to MediaDecoder
|
||||
// and the HTMLMediaElement, so that our pipeline can start exiting
|
||||
// cleanly during the sync dispatch below.
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state to SHUTDOWN", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_WARNING, "Decode error, changed state to SHUTDOWN");
|
||||
ScheduleStateMachine();
|
||||
mState = DECODER_STATE_SHUTDOWN;
|
||||
mDecoder->GetReentrantMonitor().NotifyAll();
|
||||
|
@ -1803,7 +1807,7 @@ MediaDecoderStateMachine::CallDecodeMetadata()
|
|||
return;
|
||||
}
|
||||
if (NS_FAILED(DecodeMetadata())) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("Decode metadata failed, shutting down decoder"));
|
||||
DECODER_LOG(PR_LOG_WARNING, "Decode metadata failed, shutting down decoder");
|
||||
DecodeError();
|
||||
}
|
||||
}
|
||||
|
@ -1812,7 +1816,7 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
|
|||
{
|
||||
AssertCurrentThreadInMonitor();
|
||||
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoding Media Headers", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Decoding Media Headers");
|
||||
if (mState != DECODER_STATE_DECODING_METADATA) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -1857,10 +1861,9 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
|
|||
"Active seekable media should have end time");
|
||||
MOZ_ASSERT(!(mMediaSeekable && mTransportSeekable) ||
|
||||
GetDuration() != -1, "Seekable media should have duration");
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Media goes from %lld to %lld (duration %lld)"
|
||||
" transportSeekable=%d, mediaSeekable=%d",
|
||||
mDecoder.get(), mStartTime, mEndTime, GetDuration(),
|
||||
mTransportSeekable, mMediaSeekable));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Media goes from %lld to %lld (duration %lld) "
|
||||
"transportSeekable=%d, mediaSeekable=%d",
|
||||
mStartTime, mEndTime, GetDuration(), mTransportSeekable, mMediaSeekable);
|
||||
|
||||
if (HasAudio() && !HasVideo()) {
|
||||
// We're playing audio only. We don't need to worry about slow video
|
||||
|
@ -1892,7 +1895,7 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
|
|||
}
|
||||
|
||||
if (mState == DECODER_STATE_DECODING_METADATA) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING_METADATA to DECODING", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state from DECODING_METADATA to DECODING");
|
||||
StartDecoding();
|
||||
}
|
||||
|
||||
|
@ -2025,8 +2028,7 @@ void MediaDecoderStateMachine::DecodeSeek()
|
|||
// Seeked to end of media, move to COMPLETED state. Note we don't do
|
||||
// this if we're playing a live stream, since the end of media will advance
|
||||
// once we download more data!
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lld) to COMPLETED",
|
||||
mDecoder.get(), seekTime));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state from SEEKING (to %lld) to COMPLETED", seekTime);
|
||||
stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStoppedAtEnd);
|
||||
// Explicitly set our state so we don't decode further, and so
|
||||
// we report playback ended to the media element.
|
||||
|
@ -2035,8 +2037,7 @@ void MediaDecoderStateMachine::DecodeSeek()
|
|||
mIsVideoDecoding = false;
|
||||
DispatchDecodeTasksIfNeeded();
|
||||
} else {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state from SEEKING (to %lld) to DECODING",
|
||||
mDecoder.get(), seekTime));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state from SEEKING (to %lld) to DECODING", seekTime);
|
||||
stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStopped);
|
||||
StartDecoding();
|
||||
}
|
||||
|
@ -2049,8 +2050,7 @@ void MediaDecoderStateMachine::DecodeSeek()
|
|||
}
|
||||
|
||||
// Try to decode another frame to detect if we're at the end...
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Seek completed, mCurrentFrameTime=%lld\n",
|
||||
mDecoder.get(), mCurrentFrameTime));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Seek completed, mCurrentFrameTime=%lld", mCurrentFrameTime);
|
||||
|
||||
{
|
||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||
|
@ -2226,19 +2226,14 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
|
|||
!mDecoder->IsDataCachedToEndOfResource() &&
|
||||
!resource->IsSuspended())
|
||||
{
|
||||
DECODER_LOG(PR_LOG_DEBUG,
|
||||
("%p Buffering: wait %ds, timeout in %.3lfs %s",
|
||||
mDecoder.get(),
|
||||
mBufferingWait,
|
||||
mBufferingWait - elapsed.ToSeconds(),
|
||||
(mQuickBuffering ? "(quick exit)" : "")));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Buffering: wait %ds, timeout in %.3lfs %s",
|
||||
mBufferingWait, mBufferingWait - elapsed.ToSeconds(),
|
||||
(mQuickBuffering ? "(quick exit)" : ""));
|
||||
ScheduleStateMachine(USECS_PER_S);
|
||||
return NS_OK;
|
||||
} else {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state from BUFFERING to DECODING", mDecoder.get()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Buffered for %.3lfs",
|
||||
mDecoder.get(),
|
||||
(now - mBufferingStart).ToSeconds()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state from BUFFERING to DECODING");
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
|
||||
StartDecoding();
|
||||
}
|
||||
|
||||
|
@ -2324,10 +2319,7 @@ void MediaDecoderStateMachine::RenderVideoFrame(VideoData* aData,
|
|||
return;
|
||||
}
|
||||
|
||||
if (!PR_GetEnv("MOZ_QUIET")) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder playing video frame %lld",
|
||||
mDecoder.get(), aData->mTime));
|
||||
}
|
||||
VERBOSE_LOG("playing video frame %lld", aData->mTime);
|
||||
|
||||
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
|
||||
if (container) {
|
||||
|
@ -2438,12 +2430,9 @@ void MediaDecoderStateMachine::AdvanceFrame()
|
|||
mVideoFrameEndTime = frame->GetEndTime();
|
||||
currentFrame = frame;
|
||||
#ifdef PR_LOGGING
|
||||
if (!PR_GetEnv("MOZ_QUIET")) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder discarding video frame %lld", mDecoder.get(), frame->mTime));
|
||||
if (droppedFrames++) {
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Decoder discarding video frame %lld (%d so far)",
|
||||
mDecoder.get(), frame->mTime, droppedFrames - 1));
|
||||
}
|
||||
VERBOSE_LOG("discarding video frame %lld", frame->mTime);
|
||||
if (droppedFrames++) {
|
||||
VERBOSE_LOG("discarding video frame %lld (%d so far)", frame->mTime, droppedFrames-1);
|
||||
}
|
||||
#endif
|
||||
mReader->VideoQueue().PopFront();
|
||||
|
@ -2581,7 +2570,7 @@ VideoData* MediaDecoderStateMachine::FindStartTime()
|
|||
// first actual audio frame we have, we'll inject silence during playback
|
||||
// to ensure the audio starts at the correct time.
|
||||
mAudioStartTime = mStartTime;
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Media start time is %lld", mDecoder.get(), mStartTime));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Media start time is %lld", mStartTime);
|
||||
return v;
|
||||
}
|
||||
|
||||
|
@ -2651,14 +2640,13 @@ void MediaDecoderStateMachine::StartBuffering()
|
|||
// the element we're buffering or not.
|
||||
UpdateReadyState();
|
||||
mState = DECODER_STATE_BUFFERING;
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Changed state from DECODING to BUFFERING, decoded for %.3lfs",
|
||||
mDecoder.get(), decodeDuration.ToSeconds()));
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Changed state from DECODING to BUFFERING, decoded for %.3lfs",
|
||||
decodeDuration.ToSeconds());
|
||||
#ifdef PR_LOGGING
|
||||
MediaDecoder::Statistics stats = mDecoder->GetStatistics();
|
||||
DECODER_LOG(PR_LOG_DEBUG, ("%p Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
|
||||
mDecoder.get(),
|
||||
DECODER_LOG(PR_LOG_DEBUG, "Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
|
||||
stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
|
||||
stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)"));
|
||||
stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -2689,18 +2677,16 @@ nsresult MediaDecoderStateMachine::CallRunStateMachine()
|
|||
return res;
|
||||
}
|
||||
|
||||
static void TimeoutExpired(nsITimer *aTimer, void *aClosure) {
|
||||
MediaDecoderStateMachine *machine =
|
||||
static_cast<MediaDecoderStateMachine*>(aClosure);
|
||||
NS_ASSERTION(machine, "Must have been passed state machine");
|
||||
machine->TimeoutExpired();
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::TimeoutExpired()
|
||||
nsresult MediaDecoderStateMachine::TimeoutExpired(int aTimerId)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
NS_ASSERTION(OnStateMachineThread(), "Must be on state machine thread");
|
||||
CallRunStateMachine();
|
||||
mTimer->Cancel();
|
||||
if (mTimerId == aTimerId) {
|
||||
return CallRunStateMachine();
|
||||
} else {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() {
|
||||
|
@ -2709,6 +2695,26 @@ void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() {
|
|||
DispatchVideoDecodeTaskIfNeeded();
|
||||
}
|
||||
|
||||
class TimerEvent : public nsITimerCallback, public nsRunnable {
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
public:
|
||||
TimerEvent(MediaDecoderStateMachine* aStateMachine, int aTimerId)
|
||||
: mStateMachine(aStateMachine), mTimerId(aTimerId) {}
|
||||
|
||||
NS_IMETHOD Run() MOZ_OVERRIDE {
|
||||
return mStateMachine->TimeoutExpired(mTimerId);
|
||||
}
|
||||
|
||||
NS_IMETHOD Notify(nsITimer* aTimer) {
|
||||
return mStateMachine->TimeoutExpired(mTimerId);
|
||||
}
|
||||
private:
|
||||
const nsRefPtr<MediaDecoderStateMachine> mStateMachine;
|
||||
int mTimerId;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS2(TimerEvent, nsITimerCallback, nsIRunnable);
|
||||
|
||||
nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) {
|
||||
AssertCurrentThreadInMonitor();
|
||||
NS_ABORT_IF_FALSE(GetStateMachineThread(),
|
||||
|
@ -2730,15 +2736,32 @@ nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) {
|
|||
if (mRealTime && ms > 40) {
|
||||
ms = 40;
|
||||
}
|
||||
mTimeout = timeout;
|
||||
// Cancel existing timer if any since we are going to schedule a new one.
|
||||
mTimer->Cancel();
|
||||
nsresult rv = mTimer->InitWithFuncCallback(mozilla::TimeoutExpired,
|
||||
this,
|
||||
ms,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
|
||||
// Don't cancel the timer here for this function will be called from
|
||||
// different threads.
|
||||
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
nsRefPtr<TimerEvent> event = new TimerEvent(this, mTimerId+1);
|
||||
|
||||
if (ms == 0) {
|
||||
// Dispatch a runnable to the state machine thread when delay is 0.
|
||||
// It will has less latency than dispatching a runnable to the state
|
||||
// machine thread which will then schedule a zero-delay timer.
|
||||
rv = GetStateMachineThread()->Dispatch(event, NS_DISPATCH_NORMAL);
|
||||
} else if (OnStateMachineThread()) {
|
||||
rv = mTimer->InitWithCallback(event, ms, nsITimer::TYPE_ONE_SHOT);
|
||||
} else {
|
||||
MOZ_ASSERT(false, "non-zero delay timer should be only scheduled in state machine thread");
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mTimeout = timeout;
|
||||
++mTimerId;
|
||||
} else {
|
||||
NS_WARNING("Failed to schedule state machine");
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool MediaDecoderStateMachine::OnDecodeThread() const
|
||||
|
@ -2827,3 +2850,6 @@ void MediaDecoderStateMachine::QueueMetadata(int64_t aPublishTime,
|
|||
|
||||
} // namespace mozilla
|
||||
|
||||
// avoid redefined macro in unified build
|
||||
#undef DECODER_LOG
|
||||
#undef VERBOSE_LOG
|
||||
|
|
|
@ -307,7 +307,7 @@ public:
|
|||
nsresult ScheduleStateMachine(int64_t aUsecs = 0);
|
||||
|
||||
// Timer function to implement ScheduleStateMachine(aUsecs).
|
||||
void TimeoutExpired();
|
||||
nsresult TimeoutExpired(int aGeneration);
|
||||
|
||||
// Set the media fragment end time. aEndTime is in microseconds.
|
||||
void SetFragmentEndTime(int64_t aEndTime);
|
||||
|
@ -937,6 +937,9 @@ private:
|
|||
mozilla::MediaMetadataManager mMetadataManager;
|
||||
|
||||
MediaDecoderOwner::NextFrameStatus mLastFrameStatus;
|
||||
|
||||
// The id of timer tasks, used to ignore tasks that are scheduled previously.
|
||||
int mTimerId;
|
||||
};
|
||||
|
||||
} // namespace mozilla;
|
||||
|
|
|
@ -228,33 +228,6 @@ AudioBuffer::GetThreadSharedChannelsForRate(JSContext* aJSContext)
|
|||
return mSharedChannels;
|
||||
}
|
||||
|
||||
void
|
||||
AudioBuffer::MixToMono(JSContext* aJSContext)
|
||||
{
|
||||
if (mJSChannels.Length() == 1) {
|
||||
// The buffer is already mono
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare the input channels
|
||||
nsAutoTArray<const void*, GUESS_AUDIO_CHANNELS> channels;
|
||||
channels.SetLength(mJSChannels.Length());
|
||||
for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
|
||||
channels[i] = JS_GetFloat32ArrayData(mJSChannels[i]);
|
||||
}
|
||||
|
||||
// Prepare the output channels
|
||||
float* downmixBuffer = new float[mLength];
|
||||
|
||||
// Perform the down-mix
|
||||
AudioChannelsDownMix(channels, &downmixBuffer, 1, mLength);
|
||||
|
||||
// Truncate the shared channels and copy the downmixed data over
|
||||
mJSChannels.SetLength(1);
|
||||
SetRawChannelContents(aJSContext, 0, downmixBuffer);
|
||||
delete[] downmixBuffer;
|
||||
}
|
||||
|
||||
size_t
|
||||
AudioBuffer::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
|
|
|
@ -101,8 +101,6 @@ public:
|
|||
uint32_t aChannel,
|
||||
float* aContents);
|
||||
|
||||
void MixToMono(JSContext* aJSContext);
|
||||
|
||||
protected:
|
||||
bool RestoreJSChannelData(JSContext* aJSContext);
|
||||
void ClearJSChannels();
|
||||
|
|
|
@ -44,22 +44,7 @@ public:
|
|||
|
||||
void Start(double aWhen, double aOffset,
|
||||
const Optional<double>& aDuration, ErrorResult& aRv);
|
||||
void NoteOn(double aWhen, ErrorResult& aRv)
|
||||
{
|
||||
Start(aWhen, 0.0, Optional<double>(), aRv);
|
||||
}
|
||||
void NoteGrainOn(double aWhen, double aOffset,
|
||||
double aDuration, ErrorResult& aRv)
|
||||
{
|
||||
Optional<double> duration;
|
||||
duration.Construct(aDuration);
|
||||
Start(aWhen, aOffset, duration, aRv);
|
||||
}
|
||||
void Stop(double aWhen, ErrorResult& aRv);
|
||||
void NoteOff(double aWhen, ErrorResult& aRv)
|
||||
{
|
||||
Stop(aWhen, aRv);
|
||||
}
|
||||
|
||||
AudioBuffer* GetBuffer(JSContext* aCx) const
|
||||
{
|
||||
|
|
|
@ -204,39 +204,6 @@ AudioContext::CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels,
|
|||
return buffer.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<AudioBuffer>
|
||||
AudioContext::CreateBuffer(JSContext* aJSContext, const ArrayBuffer& aBuffer,
|
||||
bool aMixToMono, ErrorResult& aRv)
|
||||
{
|
||||
// Do not accept this method unless the legacy pref has been set.
|
||||
if (!Preferences::GetBool("media.webaudio.legacy.AudioContext")) {
|
||||
aRv.ThrowNotEnoughArgsError();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Sniff the content of the media.
|
||||
// Failed type sniffing will be handled by SyncDecodeMedia.
|
||||
nsAutoCString contentType;
|
||||
NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, nullptr,
|
||||
aBuffer.Data(), aBuffer.Length(),
|
||||
contentType);
|
||||
|
||||
nsRefPtr<WebAudioDecodeJob> job =
|
||||
new WebAudioDecodeJob(contentType, this, aBuffer);
|
||||
|
||||
if (mDecoder.SyncDecodeMedia(contentType.get(),
|
||||
aBuffer.Data(), aBuffer.Length(), *job) &&
|
||||
job->mOutput) {
|
||||
nsRefPtr<AudioBuffer> buffer = job->mOutput.forget();
|
||||
if (aMixToMono) {
|
||||
buffer->MixToMono(aJSContext);
|
||||
}
|
||||
return buffer.forget();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsValidBufferSize(uint32_t aBufferSize) {
|
||||
|
|
|
@ -126,10 +126,6 @@ public:
|
|||
uint32_t aLength, float aSampleRate,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<AudioBuffer>
|
||||
CreateBuffer(JSContext* aJSContext, const ArrayBuffer& aBuffer,
|
||||
bool aMixToMono, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<MediaStreamAudioDestinationNode>
|
||||
CreateMediaStreamDestination(ErrorResult& aRv);
|
||||
|
||||
|
@ -139,16 +135,6 @@ public:
|
|||
uint32_t aNumberOfOutputChannels,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<ScriptProcessorNode>
|
||||
CreateJavaScriptNode(uint32_t aBufferSize,
|
||||
uint32_t aNumberOfInputChannels,
|
||||
uint32_t aNumberOfOutputChannels,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
return CreateScriptProcessor(aBufferSize, aNumberOfInputChannels,
|
||||
aNumberOfOutputChannels, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<AnalyserNode>
|
||||
CreateAnalyser();
|
||||
|
||||
|
@ -158,12 +144,6 @@ public:
|
|||
already_AddRefed<WaveShaperNode>
|
||||
CreateWaveShaper();
|
||||
|
||||
already_AddRefed<GainNode>
|
||||
CreateGainNode()
|
||||
{
|
||||
return CreateGain();
|
||||
}
|
||||
|
||||
already_AddRefed<MediaElementAudioSourceNode>
|
||||
CreateMediaElementSource(HTMLMediaElement& aMediaElement, ErrorResult& aRv);
|
||||
already_AddRefed<MediaStreamAudioSourceNode>
|
||||
|
@ -172,12 +152,6 @@ public:
|
|||
already_AddRefed<DelayNode>
|
||||
CreateDelay(double aMaxDelayTime, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<DelayNode>
|
||||
CreateDelayNode(double aMaxDelayTime, ErrorResult& aRv)
|
||||
{
|
||||
return CreateDelay(aMaxDelayTime, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<PannerNode>
|
||||
CreatePanner();
|
||||
|
||||
|
|
|
@ -109,10 +109,6 @@ public:
|
|||
AudioParamTimeline::SetTargetAtTime(aTarget, DOMTimeToStreamTime(aStartTime), aTimeConstant, aRv);
|
||||
mCallback(mNode);
|
||||
}
|
||||
void SetTargetValueAtTime(float aTarget, double aStartTime, double aTimeConstant, ErrorResult& aRv)
|
||||
{
|
||||
SetTargetAtTime(aTarget, aStartTime, aTimeConstant, aRv);
|
||||
}
|
||||
void CancelScheduledValues(double aStartTime, ErrorResult& aRv)
|
||||
{
|
||||
if (!WebAudioUtils::IsTimeValid(aStartTime)) {
|
||||
|
|
|
@ -132,8 +132,6 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void RunNextPhase();
|
||||
|
||||
void Decode();
|
||||
void AllocateBuffer();
|
||||
void CallbackTheResult();
|
||||
|
@ -207,30 +205,6 @@ MediaDecodeTask::CreateReader()
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MediaDecodeTask::RunNextPhase()
|
||||
{
|
||||
// This takes care of handling the logic of where to run the next phase.
|
||||
// If we were invoked synchronously, we do not have a thread pool and
|
||||
// everything happens on the main thread. Just invoke Run() in that case.
|
||||
// Otherwise, some things happen on the main thread and others are run
|
||||
// in the thread pool.
|
||||
if (!mThreadPool) {
|
||||
Run();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mPhase) {
|
||||
case PhaseEnum::AllocateBuffer:
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
NS_DispatchToMainThread(this);
|
||||
break;
|
||||
case PhaseEnum::Decode:
|
||||
case PhaseEnum::Done:
|
||||
MOZ_CRASH("Invalid phase Decode");
|
||||
}
|
||||
}
|
||||
|
||||
class AutoResampler {
|
||||
public:
|
||||
AutoResampler()
|
||||
|
@ -259,8 +233,7 @@ private:
|
|||
void
|
||||
MediaDecodeTask::Decode()
|
||||
{
|
||||
MOZ_ASSERT(!mThreadPool == NS_IsMainThread(),
|
||||
"We should be on the main thread only if we don't have a thread pool");
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
mBufferDecoder->BeginDecoding(NS_GetCurrentThread());
|
||||
|
||||
|
@ -395,7 +368,7 @@ MediaDecodeTask::Decode()
|
|||
}
|
||||
|
||||
mPhase = PhaseEnum::AllocateBuffer;
|
||||
RunNextPhase();
|
||||
NS_DispatchToMainThread(this);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -489,29 +462,6 @@ MediaBufferDecoder::AsyncDecodeMedia(const char* aContentType, uint8_t* aBuffer,
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MediaBufferDecoder::SyncDecodeMedia(const char* aContentType, uint8_t* aBuffer,
|
||||
uint32_t aLength,
|
||||
WebAudioDecodeJob& aDecodeJob)
|
||||
{
|
||||
// Do not attempt to decode the media if we were not successful at sniffing
|
||||
// the content type.
|
||||
if (!*aContentType ||
|
||||
strcmp(aContentType, APPLICATION_OCTET_STREAM) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecodeTask> task =
|
||||
new MediaDecodeTask(aContentType, aBuffer, aLength, aDecodeJob, nullptr);
|
||||
if (!task->CreateReader()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
task->Run();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MediaBufferDecoder::EnsureThreadPoolInitialized()
|
||||
{
|
||||
|
@ -536,15 +486,12 @@ WebAudioDecodeJob::WebAudioDecodeJob(const nsACString& aContentType,
|
|||
, mFailureCallback(aFailureCallback)
|
||||
{
|
||||
MOZ_ASSERT(aContext);
|
||||
MOZ_ASSERT(aSuccessCallback);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_COUNT_CTOR(WebAudioDecodeJob);
|
||||
|
||||
mArrayBuffer = aBuffer.Obj();
|
||||
|
||||
MOZ_ASSERT(aSuccessCallback ||
|
||||
(!aSuccessCallback && !aFailureCallback),
|
||||
"If a success callback is not passed, no failure callback should be passed either");
|
||||
|
||||
mozilla::HoldJSObjects(this);
|
||||
}
|
||||
|
||||
|
@ -564,10 +511,8 @@ WebAudioDecodeJob::OnSuccess(ErrorCode aErrorCode)
|
|||
|
||||
// Ignore errors in calling the callback, since there is not much that we can
|
||||
// do about it here.
|
||||
if (mSuccessCallback) {
|
||||
ErrorResult rv;
|
||||
mSuccessCallback->Call(*mOutput, rv);
|
||||
}
|
||||
ErrorResult rv;
|
||||
mSuccessCallback->Call(*mOutput, rv);
|
||||
|
||||
mContext->RemoveFromDecodeQueue(this);
|
||||
}
|
||||
|
|
|
@ -80,9 +80,6 @@ public:
|
|||
void AsyncDecodeMedia(const char* aContentType, uint8_t* aBuffer,
|
||||
uint32_t aLength, WebAudioDecodeJob& aDecodeJob);
|
||||
|
||||
bool SyncDecodeMedia(const char* aContentType, uint8_t* aBuffer,
|
||||
uint32_t aLength, WebAudioDecodeJob& aDecodeJob);
|
||||
|
||||
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
return 0;
|
||||
|
|
|
@ -26,7 +26,6 @@ support-files =
|
|||
[test_AudioBuffer.html]
|
||||
[test_AudioContext.html]
|
||||
[test_AudioListener.html]
|
||||
[test_AudioParam.html]
|
||||
[test_OfflineAudioContext.html]
|
||||
[test_analyserNode.html]
|
||||
[test_audioBufferSourceNode.html]
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test AudioParam</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var context = new AudioContext();
|
||||
var gain = context.createGain().gain;
|
||||
|
||||
ok("value" in gain, "The value attr must exist");
|
||||
gain.value = 0.5;
|
||||
ok("defaultValue" in gain, "The defaultValue attr must exist");
|
||||
(function() {
|
||||
"use strict"; // in order to get the readOnly setter to throw
|
||||
expectTypeError(function() {
|
||||
gain.defaultValue = 0.5;
|
||||
});
|
||||
})();
|
||||
|
||||
gain.setValueAtTime(1, 0.25);
|
||||
gain.linearRampToValueAtTime(0.75, 0.5);
|
||||
gain.exponentialRampToValueAtTime(0.1, 0.75);
|
||||
gain.setTargetAtTime(0.2, 1, 0.5);
|
||||
gain.setTargetValueAtTime(0.3, 1.25, 0.5);
|
||||
gain.cancelScheduledValues(1.5);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -23,9 +23,6 @@ var gTest = {
|
|||
|
||||
var delay = context.createDelay();
|
||||
|
||||
var delay2 = context.createDelayNode();
|
||||
isnot(delay, delay2, "createDelayNode should create a different delay node");
|
||||
|
||||
source.buffer = buffer;
|
||||
|
||||
source.connect(delay);
|
||||
|
@ -40,13 +37,6 @@ var gTest = {
|
|||
is(delay.channelCountMode, "max", "Correct channelCountMode for the delay node");
|
||||
is(delay.channelInterpretation, "speakers", "Correct channelCountInterpretation for the delay node");
|
||||
|
||||
var delay2 = context.createDelay(2);
|
||||
is(delay2.delayTime.value, 0, "Correct initial value");
|
||||
is(delay2.delayTime.defaultValue, 0, "Correct default value");
|
||||
delay2.delayTime.value = 0.5;
|
||||
is(delay2.delayTime.value, 0.5, "Correct initial value");
|
||||
is(delay2.delayTime.defaultValue, 0, "Correct default value");
|
||||
|
||||
expectException(function() {
|
||||
context.createDelay(0);
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
|
|
|
@ -23,9 +23,6 @@ var gTest = {
|
|||
|
||||
var gain = context.createGain();
|
||||
|
||||
var gain2 = context.createGainNode();
|
||||
isnot(gain, gain2, "createGainNode should create a different gain node");
|
||||
|
||||
source.buffer = buffer;
|
||||
|
||||
source.connect(gain);
|
||||
|
|
|
@ -302,8 +302,6 @@ function runResampling(test, response, callback) {
|
|||
var cx = new OfflineAudioContext(1, 1, sampleRate);
|
||||
cx.decodeAudioData(response, function onSuccess(asyncResult) {
|
||||
is(asyncResult.sampleRate, sampleRate, "Correct sample rate");
|
||||
syncResult = cx.createBuffer(response, false);
|
||||
compareBuffers(syncResult, asyncResult);
|
||||
|
||||
checkResampledBuffer(asyncResult, test, callback);
|
||||
}, function onFailure() {
|
||||
|
@ -320,8 +318,6 @@ function runTest(test, response, callback) {
|
|||
ok(expectCallback, "Success callback should fire asynchronously");
|
||||
ok(test.valid, "Did expect success for test " + test.url);
|
||||
|
||||
syncResult = cx.createBuffer(response, false);
|
||||
compareBuffers(syncResult, asyncResult);
|
||||
checkAudioBuffer(asyncResult, test);
|
||||
|
||||
test.expectedBuffer = asyncResult;
|
||||
|
|
|
@ -20,7 +20,7 @@ addLoadEvent(function() {
|
|||
var context = new AudioContext();
|
||||
var buffer = null;
|
||||
|
||||
var sourceSP = context.createJavaScriptNode(2048);
|
||||
var sourceSP = context.createScriptProcessor(2048);
|
||||
sourceSP.addEventListener("audioprocess", function(e) {
|
||||
// generate the audio
|
||||
for (var i = 0; i < 2048; ++i) {
|
||||
|
|
|
@ -28,54 +28,6 @@ addLoadEvent(function() {
|
|||
is(destination.channelInterpretation, "speakers", "Correct channelCountInterpretation for the destination node");
|
||||
ok(destination instanceof EventTarget, "AudioNodes must be EventTargets");
|
||||
|
||||
testWith(context, buffer, destination, function(source) {
|
||||
source.start(0);
|
||||
}, function(source) {
|
||||
source.stop();
|
||||
}, function() {
|
||||
testWith(context, buffer, destination, function(source) {
|
||||
source.start(0, 1);
|
||||
}, function(source) {
|
||||
expectTypeError(function() {
|
||||
source.noteOff();
|
||||
});
|
||||
source.noteOff(0);
|
||||
}, function() {
|
||||
testWith(context, buffer, destination, function(source) {
|
||||
source.start(0, 1, 0.5);
|
||||
}, function(source) {
|
||||
source.stop(0);
|
||||
}, function() {
|
||||
testWith(context, buffer, destination, function(source) {
|
||||
source.noteOn(0);
|
||||
}, function(source) {
|
||||
source.noteOff(0);
|
||||
}, function() {
|
||||
testWith(context, buffer, destination, function(source) {
|
||||
source.noteGrainOn(0, 1, 0.5);
|
||||
}, function(source) {
|
||||
source.stop();
|
||||
}, function() {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function testWith(context, buffer, destination, start, stop, callback)
|
||||
{
|
||||
var source = createNode(context, buffer, destination);
|
||||
start(source);
|
||||
SimpleTest.executeSoon(function() {
|
||||
stop(source);
|
||||
callback();
|
||||
source.disconnect();
|
||||
});
|
||||
}
|
||||
|
||||
function createNode(context, buffer, destination) {
|
||||
var source = context.createBufferSource();
|
||||
is(source.context, context, "Source node has proper context");
|
||||
is(source.numberOfInputs, 0, "Source node has 0 inputs");
|
||||
|
@ -102,8 +54,15 @@ function createNode(context, buffer, destination) {
|
|||
is(destination.numberOfInputs, 1, "Destination node has 0 inputs");
|
||||
is(destination.numberOfOutputs, 0, "Destination node has 0 outputs");
|
||||
|
||||
return source;
|
||||
}
|
||||
source.start(0);
|
||||
SimpleTest.executeSoon(function() {
|
||||
source.stop(0);
|
||||
source.disconnect();
|
||||
|
||||
SpecialPowers.clearUserPref("media.webaudio.enabled");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
|
|
@ -31,7 +31,8 @@ void EbmlComposer::GenerateHeader()
|
|||
Ebml_StartSubElement(&ebml, &segEbmlLoc, Segment);
|
||||
{
|
||||
Ebml_StartSubElement(&ebml, &ebmlLocseg, SeekHead);
|
||||
// Todo: We don't know the exact sizes of encoded data and ignore this section.
|
||||
// Todo: We don't know the exact sizes of encoded data and
|
||||
// ignore this section.
|
||||
Ebml_EndSubElement(&ebml, &ebmlLocseg);
|
||||
writeSegmentInformation(&ebml, &ebmlLoc, TIME_CODE_SCALE, 0);
|
||||
{
|
||||
|
@ -54,31 +55,52 @@ void EbmlComposer::GenerateHeader()
|
|||
Ebml_EndSubElement(&ebml, &trackLoc);
|
||||
}
|
||||
}
|
||||
// The Recording length is unknow and ignore write the whole Segment element size
|
||||
// The Recording length is unknown and
|
||||
// ignore write the whole Segment element size
|
||||
}
|
||||
MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + mCodecPrivateData.Length(),
|
||||
"write more data > EBML_BUFFER_SIZE");
|
||||
mClusterBuffs.AppendElement();
|
||||
mClusterBuffs.LastElement().SetLength(ebml.offset);
|
||||
memcpy(mClusterBuffs.LastElement().Elements(), ebml.buf, ebml.offset);
|
||||
auto block = mClusterBuffs.AppendElement();
|
||||
block->SetLength(ebml.offset);
|
||||
memcpy(block->Elements(), ebml.buf, ebml.offset);
|
||||
mFlushState |= FLUSH_METADATA;
|
||||
}
|
||||
|
||||
void EbmlComposer::FinishMetadata()
|
||||
{
|
||||
if (mFlushState & FLUSH_METADATA) {
|
||||
// We don't remove the first element of mClusterBuffs because the
|
||||
// |mClusterHeaderIndex| may have value.
|
||||
mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[0]);
|
||||
mFlushState &= ~FLUSH_METADATA;
|
||||
}
|
||||
}
|
||||
|
||||
void EbmlComposer::FinishCluster()
|
||||
{
|
||||
MOZ_ASSERT(mClusterLengthLoc > 0 );
|
||||
MOZ_ASSERT(mClusterHeaderIndex > 0);
|
||||
for (uint32_t i = 0; i < mClusterBuffs.Length(); i ++ ) {
|
||||
mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[i]);
|
||||
FinishMetadata();
|
||||
if (!(mFlushState & FLUSH_CLUSTER)) {
|
||||
// No completed cluster available.
|
||||
return;
|
||||
}
|
||||
mClusterBuffs.Clear();
|
||||
|
||||
MOZ_ASSERT(mClusterLengthLoc > 0);
|
||||
EbmlGlobal ebml;
|
||||
EbmlLoc ebmlLoc;
|
||||
ebmlLoc.offset = mClusterLengthLoc;
|
||||
ebml.offset = mClusterCanFlushBuffs[mClusterHeaderIndex].Length();
|
||||
ebml.buf = mClusterCanFlushBuffs[mClusterHeaderIndex].Elements();
|
||||
ebml.offset = mClusterBuffs[mClusterHeaderIndex].Length();
|
||||
ebml.buf = mClusterBuffs[mClusterHeaderIndex].Elements();
|
||||
Ebml_EndSubElement(&ebml, &ebmlLoc);
|
||||
// Move the mClusterBuffs data from mClusterHeaderIndex that we can skip
|
||||
// the metadata and the rest P-frames after ContainerWriter::FLUSH_NEEDED.
|
||||
for (uint32_t i = mClusterHeaderIndex; i < mClusterBuffs.Length(); i++) {
|
||||
mClusterCanFlushBuffs.AppendElement()->SwapElements(mClusterBuffs[i]);
|
||||
}
|
||||
|
||||
mClusterHeaderIndex = 0;
|
||||
mClusterLengthLoc = 0;
|
||||
mClusterBuffs.Clear();
|
||||
mFlushState &= ~FLUSH_CLUSTER;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -87,27 +109,29 @@ EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame)
|
|||
EbmlGlobal ebml;
|
||||
ebml.offset = 0;
|
||||
|
||||
if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME && mClusterHeaderIndex > 0) {
|
||||
if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
|
||||
FinishCluster();
|
||||
}
|
||||
|
||||
mClusterBuffs.AppendElement();
|
||||
mClusterBuffs.LastElement().SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE);
|
||||
ebml.buf = mClusterBuffs.LastElement().Elements();
|
||||
auto block = mClusterBuffs.AppendElement();
|
||||
block->SetLength(aFrame->GetFrameData().Length() + DEFAULT_HEADER_SIZE);
|
||||
ebml.buf = block->Elements();
|
||||
|
||||
if (aFrame->GetFrameType() == EncodedFrame::FrameType::VP8_I_FRAME) {
|
||||
EbmlLoc ebmlLoc;
|
||||
Ebml_StartSubElement(&ebml, &ebmlLoc, Cluster);
|
||||
mClusterHeaderIndex = mClusterBuffs.Length() - 1; // current cluster header array index
|
||||
MOZ_ASSERT(mClusterBuffs.Length() > 0);
|
||||
// current cluster header array index
|
||||
mClusterHeaderIndex = mClusterBuffs.Length() - 1;
|
||||
mClusterLengthLoc = ebmlLoc.offset;
|
||||
if (aFrame->GetFrameType() != EncodedFrame::FrameType::VORBIS_AUDIO_FRAME) {
|
||||
mClusterTimecode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC;
|
||||
}
|
||||
mClusterTimecode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC;
|
||||
Ebml_SerializeUnsigned(&ebml, Timecode, mClusterTimecode);
|
||||
mFlushState |= FLUSH_CLUSTER;
|
||||
}
|
||||
|
||||
if (aFrame->GetFrameType() != EncodedFrame::FrameType::VORBIS_AUDIO_FRAME) {
|
||||
short timeCode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC - mClusterTimecode;
|
||||
short timeCode = aFrame->GetTimeStamp() / PR_USEC_PER_MSEC
|
||||
- mClusterTimecode;
|
||||
writeSimpleBlock(&ebml, 0x1, timeCode, aFrame->GetFrameType() ==
|
||||
EncodedFrame::FrameType::VP8_I_FRAME,
|
||||
0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
|
||||
|
@ -117,9 +141,10 @@ EbmlComposer::WriteSimpleBlock(EncodedFrame* aFrame)
|
|||
0, 0, (unsigned char*)aFrame->GetFrameData().Elements(),
|
||||
aFrame->GetFrameData().Length());
|
||||
}
|
||||
MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE + aFrame->GetFrameData().Length(),
|
||||
MOZ_ASSERT(ebml.offset <= DEFAULT_HEADER_SIZE +
|
||||
aFrame->GetFrameData().Length(),
|
||||
"write more data > EBML_BUFFER_SIZE");
|
||||
mClusterBuffs.LastElement().SetLength(ebml.offset);
|
||||
block->SetLength(ebml.offset);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -155,18 +180,25 @@ void
|
|||
EbmlComposer::ExtractBuffer(nsTArray<nsTArray<uint8_t> >* aDestBufs,
|
||||
uint32_t aFlag)
|
||||
{
|
||||
if ((aFlag & ContainerWriter::FLUSH_NEEDED) && mClusterHeaderIndex > 0) {
|
||||
if ((aFlag & ContainerWriter::FLUSH_NEEDED) ||
|
||||
(aFlag & ContainerWriter::GET_HEADER))
|
||||
{
|
||||
FinishMetadata();
|
||||
}
|
||||
if (aFlag & ContainerWriter::FLUSH_NEEDED)
|
||||
{
|
||||
FinishCluster();
|
||||
}
|
||||
// aDestBufs may have some element
|
||||
for (uint32_t i = 0; i < mClusterCanFlushBuffs.Length(); i ++ ) {
|
||||
for (uint32_t i = 0; i < mClusterCanFlushBuffs.Length(); i++) {
|
||||
aDestBufs->AppendElement()->SwapElements(mClusterCanFlushBuffs[i]);
|
||||
}
|
||||
mClusterCanFlushBuffs.Clear();
|
||||
}
|
||||
|
||||
EbmlComposer::EbmlComposer()
|
||||
: mClusterHeaderIndex(0)
|
||||
: mFlushState(FLUSH_NONE)
|
||||
, mClusterHeaderIndex(0)
|
||||
, mClusterLengthLoc(0)
|
||||
, mClusterTimecode(0)
|
||||
, mWidth(0)
|
||||
|
|
|
@ -46,13 +46,23 @@ public:
|
|||
void ExtractBuffer(nsTArray<nsTArray<uint8_t> >* aDestBufs,
|
||||
uint32_t aFlag = 0);
|
||||
private:
|
||||
// Move the metadata data to mClusterCanFlushBuffs.
|
||||
void FinishMetadata();
|
||||
// Close current cluster and move data to mClusterCanFlushBuffs.
|
||||
void FinishCluster();
|
||||
// The temporary storage for cluster data.
|
||||
nsTArray<nsTArray<uint8_t> > mClusterBuffs;
|
||||
// The storage which contain valid cluster data.
|
||||
nsTArray<nsTArray<uint8_t> > mClusterCanFlushBuffs;
|
||||
// Indicate the header index in mClusterBuffs.
|
||||
|
||||
// Indicate the data types in mClusterBuffs.
|
||||
enum {
|
||||
FLUSH_NONE = 0,
|
||||
FLUSH_METADATA = 1 << 0,
|
||||
FLUSH_CLUSTER = 1 << 1
|
||||
};
|
||||
uint32_t mFlushState;
|
||||
// Indicate the cluster header index in mClusterBuffs.
|
||||
uint32_t mClusterHeaderIndex;
|
||||
// The cluster length position.
|
||||
uint64_t mClusterLengthLoc;
|
||||
|
|
|
@ -482,7 +482,7 @@ public:
|
|||
if (sameOrigin) {
|
||||
init.mMessage = mErrorMsg;
|
||||
init.mLineno = mLineNumber;
|
||||
init.mColumn = mColumn;
|
||||
init.mColno = mColumn;
|
||||
init.mError = mError;
|
||||
} else {
|
||||
NS_WARNING("Not same origin error!");
|
||||
|
|
|
@ -142,7 +142,7 @@ JSEventHandler::HandleEvent(nsIDOMEvent* aEvent)
|
|||
lineNumber.Value() = scriptEvent->Lineno();
|
||||
|
||||
columnNumber.Construct();
|
||||
columnNumber.Value() = scriptEvent->Column();
|
||||
columnNumber.Value() = scriptEvent->Colno();
|
||||
|
||||
ThreadsafeAutoJSContext cx;
|
||||
error.Construct(cx);
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
[ "Callback message", msg, "Error: hello" ],
|
||||
[ "Event error-object", errorEvent.error, thrown],
|
||||
[ "Callback error-object", error, thrown ],
|
||||
[ "Event column", errorEvent.column, 0 ], // Sadly not correct right now
|
||||
[ "Event column", errorEvent.colno, 0 ], // Sadly not correct right now
|
||||
[ "Callback column", column, 0 ]
|
||||
]);
|
||||
</script>
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
#include "ArchiveZipFile.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIPlatformCharset.h"
|
||||
#include "nsNativeCharsetUtils.h"
|
||||
#include "nsCExternalHandlerService.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
|
|
@ -169,14 +169,13 @@ nsDOMIdentity.prototype = {
|
|||
let message = this.DOMIdentityMessage(aOptions);
|
||||
|
||||
// We permit calling of request() outside of a user input handler only when
|
||||
// we are handling the (deprecated) get() or getVerifiedEmail() calls,
|
||||
// which make use of an RP context marked as _internal, or when a certified
|
||||
// app is calling.
|
||||
//
|
||||
// XXX Bug 982460 - grant the same privilege to packaged apps
|
||||
// a certified or privileged app is calling, or when we are handling the
|
||||
// (deprecated) get() or getVerifiedEmail() calls, which make use of an RP
|
||||
// context marked as _internal.
|
||||
|
||||
if (!aOptions._internal &&
|
||||
this._appStatus !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
this._appStatus !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED &&
|
||||
this._appStatus !== Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
|
||||
|
||||
// If the caller is not special in one of those ways, see if the user has
|
||||
// preffed on 'syntheticEventsOk' (useful for testing); otherwise, if
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
<html>
|
||||
<!--
|
||||
Certified and privileged apps can call mozId outside an event handler
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=971379
|
||||
Certified apps: https://bugzilla.mozilla.org/show_bug.cgi?id=971379
|
||||
Privileged apps: https://bugzilla.mozilla.org/show_bug.cgi?id=982460
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
|
|
@ -94,16 +94,13 @@ let apps = [
|
|||
},
|
||||
},
|
||||
{
|
||||
title: "a privileged app, which may not use synthetic events (until bug 982460 lands)",
|
||||
title: "a privileged app, which may use synthetic events",
|
||||
manifest: "https://example.com/manifest_priv.webapp",
|
||||
origin: "https://example.com",
|
||||
uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_syntheticEvents.html",
|
||||
wantIssuer: "firefox-accounts",
|
||||
expected: {
|
||||
success: false,
|
||||
errors: [
|
||||
"ERROR_REQUEST_WHILE_NOT_HANDLING_USER_INPUT",
|
||||
],
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
@ -28,20 +28,3 @@ interface AudioBufferSourceNode : AudioNode {
|
|||
|
||||
attribute EventHandler onended;
|
||||
};
|
||||
|
||||
/*
|
||||
* The origin of this IDL file is
|
||||
* https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#AlternateNames
|
||||
*/
|
||||
partial interface AudioBufferSourceNode {
|
||||
// Same as start()
|
||||
[Throws,Pref="media.webaudio.legacy.AudioBufferSourceNode"]
|
||||
void noteOn(double when);
|
||||
[Throws,Pref="media.webaudio.legacy.AudioBufferSourceNode"]
|
||||
void noteGrainOn(double when, double grainOffset, double grainDuration);
|
||||
|
||||
[Throws,Pref="media.webaudio.legacy.AudioBufferSourceNode"]
|
||||
// Same as stop()
|
||||
void noteOff(double when);
|
||||
};
|
||||
|
||||
|
|
|
@ -74,29 +74,6 @@ interface AudioContext : EventTarget {
|
|||
|
||||
};
|
||||
|
||||
/*
|
||||
* The origin of this IDL file is
|
||||
* https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#AlternateNames
|
||||
*/
|
||||
partial interface AudioContext {
|
||||
[NewObject, Throws]
|
||||
AudioBuffer? createBuffer(ArrayBuffer buffer, boolean mixToMono);
|
||||
|
||||
// Same as createGain()
|
||||
[NewObject,Pref="media.webaudio.legacy.AudioContext"]
|
||||
GainNode createGainNode();
|
||||
|
||||
// Same as createDelay()
|
||||
[NewObject, Throws, Pref="media.webaudio.legacy.AudioContext"]
|
||||
DelayNode createDelayNode(optional double maxDelayTime = 1);
|
||||
|
||||
// Same as createScriptProcessor()
|
||||
[NewObject, Throws, Pref="media.webaudio.legacy.AudioContext"]
|
||||
ScriptProcessorNode createJavaScriptNode(optional unsigned long bufferSize = 0,
|
||||
optional unsigned long numberOfInputChannels = 2,
|
||||
optional unsigned long numberOfOutputChannels = 2);
|
||||
};
|
||||
|
||||
// Mozilla extensions
|
||||
partial interface AudioContext {
|
||||
// Read AudioChannel.webidl for more information about this attribute.
|
||||
|
|
|
@ -37,14 +37,3 @@ interface AudioParam {
|
|||
void cancelScheduledValues(double startTime);
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* The origin of this IDL file is
|
||||
* https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#AlternateNames
|
||||
*/
|
||||
partial interface AudioParam {
|
||||
// Same as setTargetAtTime()
|
||||
[Throws,Pref="media.webaudio.legacy.AudioParam"]
|
||||
void setTargetValueAtTime(float target, double startTime, double timeConstant);
|
||||
};
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ interface ErrorEvent : Event
|
|||
readonly attribute DOMString message;
|
||||
readonly attribute DOMString filename;
|
||||
readonly attribute unsigned long lineno;
|
||||
readonly attribute unsigned long column;
|
||||
readonly attribute unsigned long colno;
|
||||
readonly attribute any error;
|
||||
};
|
||||
|
||||
|
@ -18,6 +18,6 @@ dictionary ErrorEventInit : EventInit
|
|||
DOMString message = "";
|
||||
DOMString filename = "";
|
||||
unsigned long lineno = 0;
|
||||
unsigned long column = 0;
|
||||
unsigned long colno = 0;
|
||||
any error = null;
|
||||
};
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include "nsIDOMChromeWindow.h"
|
||||
#include "nsIEffectiveTLDService.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIPlatformCharset.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
|
|
|
@ -3147,7 +3147,7 @@ WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
|
|||
errorInit.mMessage = aMessage;
|
||||
errorInit.mFilename = aFilename;
|
||||
errorInit.mLineno = aLineNumber;
|
||||
errorInit.mColumn = aColumnNumber;
|
||||
errorInit.mColno = aColumnNumber;
|
||||
|
||||
nsRefPtr<ErrorEvent> errorEvent =
|
||||
ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
is(event.message, "Error: " + sentMessage, "Got correct error");
|
||||
is(event.filename, errorFilename, "Got correct filename");
|
||||
is(event.lineno, errorLine, "Got correct lineno");
|
||||
is(event.column, errorColumn, "Got correct column");
|
||||
is(event.colno, errorColumn, "Got correct column");
|
||||
ok(receivedMessage !== undefined, "Got message already");
|
||||
ok(receivedError === undefined, "Haven't gotten error yet");
|
||||
receivedError = event.message;
|
||||
|
|
|
@ -32,8 +32,9 @@ enum AllowedTouchBehavior {
|
|||
NONE = 0,
|
||||
VERTICAL_PAN = 1 << 0,
|
||||
HORIZONTAL_PAN = 1 << 1,
|
||||
ZOOM = 1 << 2,
|
||||
UNKNOWN = 1 << 3
|
||||
PINCH_ZOOM = 1 << 2,
|
||||
DOUBLE_TAP_ZOOM = 1 << 3,
|
||||
UNKNOWN = 1 << 4
|
||||
};
|
||||
|
||||
class Layer;
|
||||
|
|
|
@ -233,7 +233,8 @@ typedef GeckoContentController::APZStateChange APZStateChange;
|
|||
*/
|
||||
static const uint32_t DefaultTouchBehavior = AllowedTouchBehavior::VERTICAL_PAN |
|
||||
AllowedTouchBehavior::HORIZONTAL_PAN |
|
||||
AllowedTouchBehavior::ZOOM;
|
||||
AllowedTouchBehavior::PINCH_ZOOM |
|
||||
AllowedTouchBehavior::DOUBLE_TAP_ZOOM;
|
||||
|
||||
/**
|
||||
* Angle from axis within which we stay axis-locked
|
||||
|
@ -781,7 +782,7 @@ nsEventStatus AsyncPanZoomController::OnTouchCancel(const MultiTouchInput& aEven
|
|||
nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
|
||||
APZC_LOG("%p got a scale-begin in state %d\n", this, mState);
|
||||
|
||||
if (!TouchActionAllowZoom()) {
|
||||
if (!TouchActionAllowPinchZoom()) {
|
||||
return nsEventStatus_eIgnore;
|
||||
}
|
||||
|
||||
|
@ -972,7 +973,7 @@ nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEven
|
|||
APZC_LOG("%p got a single-tap-up in state %d\n", this, mState);
|
||||
// If mZoomConstraints.mAllowDoubleTapZoom is true we wait for a call to OnSingleTapConfirmed before
|
||||
// sending event to content
|
||||
if (!mZoomConstraints.mAllowDoubleTapZoom) {
|
||||
if (!(mZoomConstraints.mAllowDoubleTapZoom && TouchActionAllowDoubleTapZoom())) {
|
||||
return GenerateSingleTap(aEvent.mPoint, aEvent.modifiers);
|
||||
}
|
||||
return nsEventStatus_eIgnore;
|
||||
|
@ -987,14 +988,13 @@ nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent)
|
|||
APZC_LOG("%p got a double-tap in state %d\n", this, mState);
|
||||
nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
|
||||
if (controller) {
|
||||
if (mZoomConstraints.mAllowDoubleTapZoom) {
|
||||
if (mZoomConstraints.mAllowDoubleTapZoom && TouchActionAllowDoubleTapZoom()) {
|
||||
int32_t modifiers = WidgetModifiersToDOMModifiers(aEvent.modifiers);
|
||||
CSSPoint geckoScreenPoint;
|
||||
if (ConvertToGecko(aEvent.mPoint, &geckoScreenPoint)) {
|
||||
controller->HandleDoubleTap(geckoScreenPoint, modifiers, GetGuid());
|
||||
}
|
||||
}
|
||||
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
return nsEventStatus_eIgnore;
|
||||
|
@ -1961,19 +1961,29 @@ void AsyncPanZoomController::CheckContentResponse() {
|
|||
}
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::TouchActionAllowZoom() {
|
||||
bool AsyncPanZoomController::TouchActionAllowPinchZoom() {
|
||||
if (!mTouchActionPropertyEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pointer events specification implies all touch points to allow zoom
|
||||
// to perform it.
|
||||
for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) {
|
||||
if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::ZOOM)) {
|
||||
if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::PINCH_ZOOM)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AsyncPanZoomController::TouchActionAllowDoubleTapZoom() {
|
||||
if (!mTouchActionPropertyEnabled) {
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < mTouchBlockState.mAllowedTouchBehaviors.Length(); i++) {
|
||||
if (!(mTouchBlockState.mAllowedTouchBehaviors[i] & AllowedTouchBehavior::DOUBLE_TAP_ZOOM)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -595,9 +595,14 @@ private:
|
|||
};
|
||||
|
||||
/*
|
||||
* Returns whether current touch behavior values allow zooming.
|
||||
* Returns whether current touch behavior values allow pinch-zooming.
|
||||
*/
|
||||
bool TouchActionAllowZoom();
|
||||
bool TouchActionAllowPinchZoom();
|
||||
|
||||
/*
|
||||
* Returns whether current touch behavior values allow double-tap-zooming.
|
||||
*/
|
||||
bool TouchActionAllowDoubleTapZoom();
|
||||
|
||||
/*
|
||||
* Returns allowed touch behavior from the mAllowedTouchBehavior array.
|
||||
|
|
|
@ -86,6 +86,32 @@ ClientTiledThebesLayer::BeginPaint()
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// Subframes on Fennec are not async scrollable because they have no displayport.
|
||||
// However, the code in RenderLayer() picks up a displayport from the nearest
|
||||
// scrollable ancestor container layer anyway, which is incorrect for Fennec. This
|
||||
// behaviour results in the subframe getting clipped improperly and perma-blank areas
|
||||
// while scrolling the subframe. To work around this, we detect if this layer is
|
||||
// the primary scrollable layer and disable the tiling behaviour if it is not.
|
||||
bool isPrimaryScrollableThebesLayer = false;
|
||||
if (Layer* scrollable = ClientManager()->GetPrimaryScrollableLayer()) {
|
||||
if (GetParent() == scrollable) {
|
||||
for (Layer* child = scrollable->GetFirstChild(); child; child = child->GetNextSibling()) {
|
||||
if (child->GetType() == Layer::TYPE_THEBES) {
|
||||
if (child == this) {
|
||||
// |this| is the first thebes layer child of the GetPrimaryScrollableLayer()
|
||||
isPrimaryScrollableThebesLayer = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isPrimaryScrollableThebesLayer) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Get the metrics of the nearest scrollable layer and the nearest layer
|
||||
// with a displayport.
|
||||
ContainerLayer* displayPortParent = nullptr;
|
||||
|
|
|
@ -152,6 +152,9 @@ public:
|
|||
|
||||
virtual bool Lock() {
|
||||
MOZ_ASSERT(!mLocked);
|
||||
if (!mTextureHost) {
|
||||
return false;
|
||||
}
|
||||
if (!mTextureHost->Lock()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -415,7 +415,7 @@ TEST_F(AsyncPanZoomControllerTester, PinchWithTouchActionNone) {
|
|||
|
||||
nsTArray<uint32_t> values;
|
||||
values.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
|
||||
values.AppendElement(mozilla::layers::AllowedTouchBehavior::ZOOM);
|
||||
values.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
|
||||
apzc->SetTouchActionEnabled(true);
|
||||
|
||||
apzc->SetAllowedTouchBehavior(values);
|
||||
|
@ -906,7 +906,7 @@ TEST_F(AsyncPanZoomControllerTester, LongPress) {
|
|||
TEST_F(AsyncPanZoomControllerTester, LongPressWithTouchAction) {
|
||||
DoLongPressTest(true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
|
||||
| mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
|
||||
| mozilla::layers::AllowedTouchBehavior::ZOOM);
|
||||
| mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
|
||||
}
|
||||
|
||||
TEST_F(AsyncPanZoomControllerTester, LongPressPreventDefault) {
|
||||
|
@ -916,7 +916,7 @@ TEST_F(AsyncPanZoomControllerTester, LongPressPreventDefault) {
|
|||
TEST_F(AsyncPanZoomControllerTester, LongPressPreventDefaultWithTouchAction) {
|
||||
DoLongPressPreventDefaultTest(true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
|
||||
| mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
|
||||
| mozilla::layers::AllowedTouchBehavior::ZOOM);
|
||||
| mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
|
||||
}
|
||||
|
||||
// Layer tree for HitTesting1
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/* 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/. */
|
||||
|
||||
// This file contains the structures described in Microsoft's document
|
||||
// "The MATH table and OpenType Features for Math Processing" (not yet public).
|
||||
//
|
||||
// Arrays of varying size are indicated in comments. Typically, gfxMathTable
|
||||
// will read the header of the structure first, verify that there is enough
|
||||
// space for the specified arrays and then use a pointer to browse these arrays.
|
||||
|
||||
#ifndef MATH_TABLE_STRUCTURE_H
|
||||
#define MATH_TABLE_STRUCTURE_H
|
||||
|
||||
#include "gfxFontUtils.h"
|
||||
|
||||
typedef mozilla::AutoSwap_PRUint16 Count16;
|
||||
typedef mozilla::AutoSwap_PRUint16 GlyphID;
|
||||
typedef mozilla::AutoSwap_PRUint16 Offset;
|
||||
|
||||
struct MathValueRecord {
|
||||
mozilla::AutoSwap_PRInt16 mValue;
|
||||
Offset mDeviceTable;
|
||||
};
|
||||
|
||||
struct RangeRecord {
|
||||
GlyphID mStart;
|
||||
GlyphID mEnd;
|
||||
mozilla::AutoSwap_PRUint16 mStartCoverageIndex;
|
||||
};
|
||||
|
||||
struct Coverage {
|
||||
mozilla::AutoSwap_PRUint16 mFormat;
|
||||
};
|
||||
|
||||
struct CoverageFormat1 {
|
||||
mozilla::AutoSwap_PRUint16 mFormat;
|
||||
Count16 mGlyphCount;
|
||||
// GlyphID mGlyphArray[mGlyphCount]
|
||||
};
|
||||
|
||||
struct CoverageFormat2 {
|
||||
mozilla::AutoSwap_PRUint16 mFormat;
|
||||
Count16 mRangeCount;
|
||||
// RangeRecord mRangeArray[mRangeCount];
|
||||
};
|
||||
|
||||
struct MATHTableHeader {
|
||||
mozilla::AutoSwap_PRUint32 mVersion;
|
||||
Offset mMathConstants;
|
||||
Offset mMathGlyphInfo;
|
||||
Offset mMathVariants;
|
||||
};
|
||||
|
||||
struct MathConstants {
|
||||
mozilla::AutoSwap_PRInt16 mInt16[gfxFontEntry::ScriptScriptPercentScaleDown -
|
||||
gfxFontEntry::ScriptPercentScaleDown + 1];
|
||||
mozilla::AutoSwap_PRUint16 mUint16[gfxFontEntry::DisplayOperatorMinHeight -
|
||||
gfxFontEntry::
|
||||
DelimitedSubFormulaMinHeight + 1];
|
||||
MathValueRecord mMathValues[gfxFontEntry::RadicalKernAfterDegree -
|
||||
gfxFontEntry::MathLeading + 1];
|
||||
mozilla::AutoSwap_PRUint16 mRadicalDegreeBottomRaisePercent;
|
||||
};
|
||||
|
||||
struct MathGlyphInfo {
|
||||
Offset mMathItalicsCorrectionInfo;
|
||||
Offset mMathTopAccentAttachment;
|
||||
Offset mExtendedShapeCoverage;
|
||||
Offset mMathKernInfo;
|
||||
};
|
||||
|
||||
struct MathItalicsCorrectionInfo {
|
||||
Offset mCoverage;
|
||||
Count16 mItalicsCorrectionCount;
|
||||
// MathValueRecord mItalicsCorrection[mItalicsCorrectionCount]
|
||||
};
|
||||
|
||||
struct MathVariants {
|
||||
mozilla::AutoSwap_PRUint16 mMinConnectorOverlap;
|
||||
Offset mVertGlyphCoverage;
|
||||
Offset mHorizGlyphCoverage;
|
||||
Count16 mVertGlyphCount;
|
||||
Count16 mHorizGlyphCount;
|
||||
// Offset mVertGlyphConstruction[mVertGlyphCount];
|
||||
// Offset mHorizGlyphConstruction[mHorizGlyphCount];
|
||||
};
|
||||
|
||||
struct MathGlyphVariantRecord {
|
||||
GlyphID mVariantGlyph;
|
||||
mozilla::AutoSwap_PRUint16 mAdvanceMeasurement;
|
||||
};
|
||||
|
||||
struct MathGlyphConstruction {
|
||||
Offset mGlyphAssembly;
|
||||
Count16 mVariantCount;
|
||||
// MathGlyphVariantRecord mMathGlyphVariantRecord[mVariantCount]
|
||||
};
|
||||
|
||||
struct GlyphPartRecord {
|
||||
GlyphID mGlyph;
|
||||
mozilla::AutoSwap_PRUint16 mStartConnectorLength;
|
||||
mozilla::AutoSwap_PRUint16 mEndConnectorLength;
|
||||
mozilla::AutoSwap_PRUint16 mFullAdvance;
|
||||
mozilla::AutoSwap_PRUint16 mPartFlags;
|
||||
};
|
||||
|
||||
// PartFlags enumeration currently uses only one bit:
|
||||
// 0x0001 If set, the part can be skipped or repeated.
|
||||
// 0xFFFE Reserved.
|
||||
enum {
|
||||
PART_FLAG_EXTENDER = 0x01
|
||||
};
|
||||
|
||||
struct GlyphAssembly {
|
||||
MathValueRecord mItalicsCorrection;
|
||||
Count16 mPartCount;
|
||||
// GlyphPartRecord mPartRecords[mPartCount]
|
||||
};
|
||||
|
||||
#endif
|
|
@ -117,7 +117,9 @@ gfxFT2FontBase::GetMetrics()
|
|||
new(&mMetrics) gfxFont::Metrics(); // zero initialize
|
||||
mSpaceGlyph = 0;
|
||||
} else {
|
||||
gfxFT2LockedFace(this).GetMetrics(&mMetrics, &mSpaceGlyph);
|
||||
gfxFT2LockedFace face(this);
|
||||
mFUnitsConvFactor = face.XScale();
|
||||
face.GetMetrics(&mMetrics, &mSpaceGlyph);
|
||||
}
|
||||
|
||||
SanitizeMetrics(&mMetrics, false);
|
||||
|
|
|
@ -426,9 +426,6 @@ gfxFT2Font::ShapeText(gfxContext *aContext,
|
|||
|
||||
if (!ok && gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) {
|
||||
if (!mHarfBuzzShaper) {
|
||||
gfxFT2LockedFace face(this);
|
||||
mFUnitsConvFactor = face.XScale();
|
||||
|
||||
mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
|
||||
}
|
||||
ok = mHarfBuzzShaper->ShapeText(aContext, aText,
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "gfxTypes.h"
|
||||
#include "gfxContext.h"
|
||||
#include "gfxFontMissingGlyphs.h"
|
||||
#include "gfxHarfBuzzShaper.h"
|
||||
#include "gfxUserFontSet.h"
|
||||
#include "gfxPlatformFontList.h"
|
||||
#include "gfxScriptItemizer.h"
|
||||
|
@ -38,6 +39,7 @@
|
|||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "gfxSVGGlyphs.h"
|
||||
#include "gfxMathTable.h"
|
||||
#include "gfx2DGlue.h"
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
|
@ -112,6 +114,7 @@ gfxFontEntry::gfxFontEntry() :
|
|||
mIgnoreGDEF(false),
|
||||
mIgnoreGSUB(false),
|
||||
mSVGInitialized(false),
|
||||
mMathInitialized(false),
|
||||
mHasSpaceFeaturesInitialized(false),
|
||||
mHasSpaceFeatures(false),
|
||||
mHasSpaceFeaturesKerning(false),
|
||||
|
@ -141,6 +144,7 @@ gfxFontEntry::gfxFontEntry(const nsAString& aName, bool aIsStandardFace) :
|
|||
mIgnoreGDEF(false),
|
||||
mIgnoreGSUB(false),
|
||||
mSVGInitialized(false),
|
||||
mMathInitialized(false),
|
||||
mHasSpaceFeaturesInitialized(false),
|
||||
mHasSpaceFeatures(false),
|
||||
mHasSpaceFeaturesKerning(false),
|
||||
|
@ -389,6 +393,78 @@ gfxFontEntry::NotifyGlyphsChanged()
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
gfxFontEntry::TryGetMathTable(gfxFont* aFont)
|
||||
{
|
||||
if (!mMathInitialized) {
|
||||
mMathInitialized = true;
|
||||
|
||||
// If UnitsPerEm is not known/valid, we can't use MATH table
|
||||
if (UnitsPerEm() == kInvalidUPEM) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We don't use AutoTable here because we'll pass ownership of this
|
||||
// blob to the gfxMathTable, once we've confirmed the table exists
|
||||
hb_blob_t *mathTable = GetFontTable(TRUETYPE_TAG('M','A','T','H'));
|
||||
if (!mathTable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// gfxMathTable will hb_blob_destroy() the table when it is finished
|
||||
// with it.
|
||||
mMathTable = new gfxMathTable(mathTable);
|
||||
if (!mMathTable->HasValidHeaders()) {
|
||||
mMathTable = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !!mMathTable;
|
||||
}
|
||||
|
||||
gfxFloat
|
||||
gfxFontEntry::GetMathConstant(gfxFontEntry::MathConstant aConstant)
|
||||
{
|
||||
NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
|
||||
gfxFloat value = mMathTable->GetMathConstant(aConstant);
|
||||
if (aConstant == gfxFontEntry::ScriptPercentScaleDown ||
|
||||
aConstant == gfxFontEntry::ScriptScriptPercentScaleDown ||
|
||||
aConstant == gfxFontEntry::RadicalDegreeBottomRaisePercent) {
|
||||
return value / 100.0;
|
||||
}
|
||||
return value / mUnitsPerEm;
|
||||
}
|
||||
|
||||
bool
|
||||
gfxFontEntry::GetMathItalicsCorrection(uint32_t aGlyphID,
|
||||
gfxFloat* aItalicCorrection)
|
||||
{
|
||||
NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
|
||||
int16_t italicCorrection;
|
||||
if (!mMathTable->GetMathItalicsCorrection(aGlyphID, &italicCorrection)) {
|
||||
return false;
|
||||
}
|
||||
*aItalicCorrection = gfxFloat(italicCorrection) / mUnitsPerEm;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
gfxFontEntry::GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
|
||||
uint16_t aSize)
|
||||
{
|
||||
NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
|
||||
return mMathTable->GetMathVariantsSize(aGlyphID, aVertical, aSize);
|
||||
}
|
||||
|
||||
bool
|
||||
gfxFontEntry::GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
|
||||
uint32_t aGlyphs[4])
|
||||
{
|
||||
NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
|
||||
return mMathTable->GetMathVariantsParts(aGlyphID, aVertical, aGlyphs);
|
||||
}
|
||||
|
||||
/**
|
||||
* FontTableBlobData
|
||||
*
|
||||
|
@ -1952,6 +2028,28 @@ gfxFont::~gfxFont()
|
|||
}
|
||||
}
|
||||
|
||||
gfxFloat
|
||||
gfxFont::GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID)
|
||||
{
|
||||
if (ProvidesGlyphWidths()) {
|
||||
return GetGlyphWidth(aCtx, aGID) / 65536.0;
|
||||
}
|
||||
if (mFUnitsConvFactor == 0.0f) {
|
||||
GetMetrics();
|
||||
}
|
||||
NS_ASSERTION(mFUnitsConvFactor > 0.0f,
|
||||
"missing font unit conversion factor");
|
||||
if (!mHarfBuzzShaper) {
|
||||
mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
|
||||
}
|
||||
gfxHarfBuzzShaper* shaper =
|
||||
static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
|
||||
if (!shaper->Initialize() || !SetupCairoFont(aCtx)) {
|
||||
return 0;
|
||||
}
|
||||
return shaper->GetGlyphHAdvance(aCtx, aGID) / 65536.0;
|
||||
}
|
||||
|
||||
/*static*/
|
||||
PLDHashOperator
|
||||
gfxFont::AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData)
|
||||
|
|
|
@ -43,11 +43,14 @@ class gfxTextRun;
|
|||
class gfxFont;
|
||||
class gfxFontFamily;
|
||||
class gfxFontGroup;
|
||||
class gfxGraphiteShaper;
|
||||
class gfxHarfBuzzShaper;
|
||||
class gfxUserFontSet;
|
||||
class gfxUserFontData;
|
||||
class gfxShapedText;
|
||||
class gfxShapedWord;
|
||||
class gfxSVGGlyphs;
|
||||
class gfxMathTable;
|
||||
class gfxTextContextPaint;
|
||||
class FontInfoData;
|
||||
|
||||
|
@ -311,6 +314,79 @@ public:
|
|||
// (e.g. animated SVG glyphs)
|
||||
void NotifyGlyphsChanged();
|
||||
|
||||
enum MathConstant {
|
||||
// The order of the constants must match the order of the fields
|
||||
// defined in the MATH table.
|
||||
ScriptPercentScaleDown,
|
||||
ScriptScriptPercentScaleDown,
|
||||
DelimitedSubFormulaMinHeight,
|
||||
DisplayOperatorMinHeight,
|
||||
MathLeading,
|
||||
AxisHeight,
|
||||
AccentBaseHeight,
|
||||
FlattenedAccentBaseHeight,
|
||||
SubscriptShiftDown,
|
||||
SubscriptTopMax,
|
||||
SubscriptBaselineDropMin,
|
||||
SuperscriptShiftUp,
|
||||
SuperscriptShiftUpCramped,
|
||||
SuperscriptBottomMin,
|
||||
SuperscriptBaselineDropMax,
|
||||
SubSuperscriptGapMin,
|
||||
SuperscriptBottomMaxWithSubscript,
|
||||
SpaceAfterScript,
|
||||
UpperLimitGapMin,
|
||||
UpperLimitBaselineRiseMin,
|
||||
LowerLimitGapMin,
|
||||
LowerLimitBaselineDropMin,
|
||||
StackTopShiftUp,
|
||||
StackTopDisplayStyleShiftUp,
|
||||
StackBottomShiftDown,
|
||||
StackBottomDisplayStyleShiftDown,
|
||||
StackGapMin,
|
||||
StackDisplayStyleGapMin,
|
||||
StretchStackTopShiftUp,
|
||||
StretchStackBottomShiftDown,
|
||||
StretchStackGapAboveMin,
|
||||
StretchStackGapBelowMin,
|
||||
FractionNumeratorShiftUp,
|
||||
FractionNumeratorDisplayStyleShiftUp,
|
||||
FractionDenominatorShiftDown,
|
||||
FractionDenominatorDisplayStyleShiftDown,
|
||||
FractionNumeratorGapMin,
|
||||
FractionNumDisplayStyleGapMin,
|
||||
FractionRuleThickness,
|
||||
FractionDenominatorGapMin,
|
||||
FractionDenomDisplayStyleGapMin,
|
||||
SkewedFractionHorizontalGap,
|
||||
SkewedFractionVerticalGap,
|
||||
OverbarVerticalGap,
|
||||
OverbarRuleThickness,
|
||||
OverbarExtraAscender,
|
||||
UnderbarVerticalGap,
|
||||
UnderbarRuleThickness,
|
||||
UnderbarExtraDescender,
|
||||
RadicalVerticalGap,
|
||||
RadicalDisplayStyleVerticalGap,
|
||||
RadicalRuleThickness,
|
||||
RadicalExtraAscender,
|
||||
RadicalKernBeforeDegree,
|
||||
RadicalKernAfterDegree,
|
||||
RadicalDegreeBottomRaisePercent
|
||||
};
|
||||
|
||||
// Call TryGetMathTable to try to load the Open Type MATH table. The other
|
||||
// functions forward the call to the gfxMathTable class. The GetMath...()
|
||||
// functions MUST NOT be called unless TryGetMathTable() has returned true.
|
||||
bool TryGetMathTable(gfxFont* aFont);
|
||||
gfxFloat GetMathConstant(MathConstant aConstant);
|
||||
bool GetMathItalicsCorrection(uint32_t aGlyphID,
|
||||
gfxFloat* aItalicCorrection);
|
||||
uint32_t GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
|
||||
uint16_t aSize);
|
||||
bool GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
|
||||
uint32_t aGlyphs[4]);
|
||||
|
||||
virtual bool MatchesGenericFamily(const nsACString& aGeneric) const {
|
||||
return true;
|
||||
}
|
||||
|
@ -433,6 +509,7 @@ public:
|
|||
bool mIgnoreGDEF : 1;
|
||||
bool mIgnoreGSUB : 1;
|
||||
bool mSVGInitialized : 1;
|
||||
bool mMathInitialized : 1;
|
||||
bool mHasSpaceFeaturesInitialized : 1;
|
||||
bool mHasSpaceFeatures : 1;
|
||||
bool mHasSpaceFeaturesKerning : 1;
|
||||
|
@ -458,6 +535,7 @@ public:
|
|||
nsAutoPtr<gfxSVGGlyphs> mSVGGlyphs;
|
||||
// list of gfxFonts that are using SVG glyphs
|
||||
nsTArray<gfxFont*> mFontsUsingSVGGlyphs;
|
||||
nsAutoPtr<gfxMathTable> mMathTable;
|
||||
nsTArray<gfxFontFeature> mFeatureSettings;
|
||||
uint32_t mLanguageOverride;
|
||||
|
||||
|
@ -1381,6 +1459,10 @@ protected:
|
|||
|
||||
/* a SPECIFIC single font family */
|
||||
class gfxFont {
|
||||
|
||||
friend class gfxHarfBuzzShaper;
|
||||
friend class gfxGraphiteShaper;
|
||||
|
||||
public:
|
||||
nsrefcnt AddRef(void) {
|
||||
NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
|
||||
|
@ -1508,19 +1590,8 @@ public:
|
|||
virtual uint32_t GetGlyph(uint32_t unicode, uint32_t variation_selector) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// subclasses may provide (possibly hinted) glyph widths (in font units);
|
||||
// if they do not override this, harfbuzz will use unhinted widths
|
||||
// derived from the font tables
|
||||
virtual bool ProvidesGlyphWidths() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The return value is interpreted as a horizontal advance in 16.16 fixed
|
||||
// point format.
|
||||
virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID) {
|
||||
return -1;
|
||||
}
|
||||
// Return the horizontal advance of a glyph.
|
||||
gfxFloat GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID);
|
||||
|
||||
// Return Azure GlyphRenderingOptions for drawing this font.
|
||||
virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions>
|
||||
|
@ -1816,6 +1887,19 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
// subclasses may provide (possibly hinted) glyph widths (in font units);
|
||||
// if they do not override this, harfbuzz will use unhinted widths
|
||||
// derived from the font tables
|
||||
virtual bool ProvidesGlyphWidths() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The return value is interpreted as a horizontal advance in 16.16 fixed
|
||||
// point format.
|
||||
virtual int32_t GetGlyphWidth(gfxContext *aCtx, uint16_t aGID) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
void AddGlyphChangeObserver(GlyphChangeObserver *aObserver);
|
||||
void RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver);
|
||||
|
||||
|
|
|
@ -188,10 +188,6 @@ hb_position_t
|
|||
gfxHarfBuzzShaper::GetGlyphHAdvance(gfxContext *aContext,
|
||||
hb_codepoint_t glyph) const
|
||||
{
|
||||
if (mUseFontGlyphWidths) {
|
||||
return mFont->GetGlyphWidth(aContext, glyph);
|
||||
}
|
||||
|
||||
// font did not implement GetHintedGlyphWidth, so get an unhinted value
|
||||
// directly from the font tables
|
||||
|
||||
|
@ -211,13 +207,19 @@ gfxHarfBuzzShaper::GetGlyphHAdvance(gfxContext *aContext,
|
|||
uint16_t(hmtx->metrics[glyph].advanceWidth));
|
||||
}
|
||||
|
||||
static hb_position_t
|
||||
HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
|
||||
hb_codepoint_t glyph, void *user_data)
|
||||
/* static */
|
||||
hb_position_t
|
||||
gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
|
||||
hb_codepoint_t glyph, void *user_data)
|
||||
{
|
||||
const gfxHarfBuzzShaper::FontCallbackData *fcd =
|
||||
static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
|
||||
return fcd->mShaper->GetGlyphHAdvance(fcd->mContext, glyph);
|
||||
gfxFont *gfxfont = fcd->mShaper->GetFont();
|
||||
if (gfxfont->ProvidesGlyphWidths()) {
|
||||
return gfxfont->GetGlyphWidth(fcd->mContext, glyph);
|
||||
} else {
|
||||
return fcd->mShaper->GetGlyphHAdvance(fcd->mContext, glyph);
|
||||
}
|
||||
}
|
||||
|
||||
static hb_bool_t
|
||||
|
@ -817,6 +819,121 @@ static hb_unicode_funcs_t * sHBUnicodeFuncs = nullptr;
|
|||
static const hb_script_t sMathScript =
|
||||
hb_ot_tag_to_script(HB_TAG('m','a','t','h'));
|
||||
|
||||
bool
|
||||
gfxHarfBuzzShaper::Initialize()
|
||||
{
|
||||
if (mInitialized) {
|
||||
return mHBFont != nullptr;
|
||||
}
|
||||
mInitialized = true;
|
||||
mCallbackData.mShaper = this;
|
||||
|
||||
mUseFontGlyphWidths = mFont->ProvidesGlyphWidths();
|
||||
|
||||
if (!sHBFontFuncs) {
|
||||
// static function callback pointers, initialized by the first
|
||||
// harfbuzz shaper used
|
||||
sHBFontFuncs = hb_font_funcs_create();
|
||||
hb_font_funcs_set_glyph_func(sHBFontFuncs, HBGetGlyph,
|
||||
nullptr, nullptr);
|
||||
hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs,
|
||||
HBGetGlyphHAdvance,
|
||||
nullptr, nullptr);
|
||||
hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs,
|
||||
HBGetContourPoint,
|
||||
nullptr, nullptr);
|
||||
hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs,
|
||||
HBGetHKerning,
|
||||
nullptr, nullptr);
|
||||
|
||||
sHBUnicodeFuncs =
|
||||
hb_unicode_funcs_create(hb_unicode_funcs_get_empty());
|
||||
hb_unicode_funcs_set_mirroring_func(sHBUnicodeFuncs,
|
||||
HBGetMirroring,
|
||||
nullptr, nullptr);
|
||||
hb_unicode_funcs_set_script_func(sHBUnicodeFuncs, HBGetScript,
|
||||
nullptr, nullptr);
|
||||
hb_unicode_funcs_set_general_category_func(sHBUnicodeFuncs,
|
||||
HBGetGeneralCategory,
|
||||
nullptr, nullptr);
|
||||
hb_unicode_funcs_set_combining_class_func(sHBUnicodeFuncs,
|
||||
HBGetCombiningClass,
|
||||
nullptr, nullptr);
|
||||
hb_unicode_funcs_set_eastasian_width_func(sHBUnicodeFuncs,
|
||||
HBGetEastAsianWidth,
|
||||
nullptr, nullptr);
|
||||
hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs,
|
||||
HBUnicodeCompose,
|
||||
nullptr, nullptr);
|
||||
hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs,
|
||||
HBUnicodeDecompose,
|
||||
nullptr, nullptr);
|
||||
}
|
||||
|
||||
gfxFontEntry *entry = mFont->GetFontEntry();
|
||||
if (!mUseFontGetGlyph) {
|
||||
// get the cmap table and find offset to our subtable
|
||||
mCmapTable = entry->GetFontTable(TRUETYPE_TAG('c','m','a','p'));
|
||||
if (!mCmapTable) {
|
||||
NS_WARNING("failed to load cmap, glyphs will be missing");
|
||||
return false;
|
||||
}
|
||||
uint32_t len;
|
||||
const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &len);
|
||||
bool symbol;
|
||||
mCmapFormat = gfxFontUtils::
|
||||
FindPreferredSubtable(data, len,
|
||||
&mSubtableOffset, &mUVSTableOffset,
|
||||
&symbol);
|
||||
if (mCmapFormat <= 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mUseFontGlyphWidths) {
|
||||
// if font doesn't implement GetGlyphWidth, we will be reading
|
||||
// the hmtx table directly;
|
||||
// read mNumLongMetrics from hhea table without caching its blob,
|
||||
// and preload/cache the hmtx table
|
||||
gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h','h','e','a'));
|
||||
if (hheaTable) {
|
||||
uint32_t len;
|
||||
const HMetricsHeader* hhea =
|
||||
reinterpret_cast<const HMetricsHeader*>
|
||||
(hb_blob_get_data(hheaTable, &len));
|
||||
if (len >= sizeof(HMetricsHeader)) {
|
||||
mNumLongMetrics = hhea->numberOfHMetrics;
|
||||
if (mNumLongMetrics > 0 &&
|
||||
int16_t(hhea->metricDataFormat) == 0) {
|
||||
// no point reading hmtx if number of entries is zero!
|
||||
// in that case, we won't be able to use this font
|
||||
// (this method will return FALSE below if mHmtx is null)
|
||||
mHmtxTable =
|
||||
entry->GetFontTable(TRUETYPE_TAG('h','m','t','x'));
|
||||
if (hb_blob_get_length(mHmtxTable) <
|
||||
mNumLongMetrics * sizeof(HLongMetric)) {
|
||||
// hmtx table is not large enough for the claimed
|
||||
// number of entries: invalid, do not use.
|
||||
hb_blob_destroy(mHmtxTable);
|
||||
mHmtxTable = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!mHmtxTable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
mHBFont = hb_font_create(mHBFace);
|
||||
hb_font_set_funcs(mHBFont, sHBFontFuncs, &mCallbackData, nullptr);
|
||||
hb_font_set_ppem(mHBFont, mFont->GetAdjustedSize(), mFont->GetAdjustedSize());
|
||||
uint32_t scale = FloatToFixed(mFont->GetAdjustedSize()); // 16.16 fixed-point
|
||||
hb_font_set_scale(mHBFont, scale, scale);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
|
||||
const char16_t *aText,
|
||||
|
@ -831,112 +948,8 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
|
|||
}
|
||||
|
||||
mCallbackData.mContext = aContext;
|
||||
gfxFontEntry *entry = mFont->GetFontEntry();
|
||||
|
||||
if (!mInitialized) {
|
||||
mInitialized = true;
|
||||
mCallbackData.mShaper = this;
|
||||
|
||||
mUseFontGlyphWidths = mFont->ProvidesGlyphWidths();
|
||||
|
||||
if (!sHBFontFuncs) {
|
||||
// static function callback pointers, initialized by the first
|
||||
// harfbuzz shaper used
|
||||
sHBFontFuncs = hb_font_funcs_create();
|
||||
hb_font_funcs_set_glyph_func(sHBFontFuncs, HBGetGlyph,
|
||||
nullptr, nullptr);
|
||||
hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs,
|
||||
HBGetGlyphHAdvance,
|
||||
nullptr, nullptr);
|
||||
hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs,
|
||||
HBGetContourPoint,
|
||||
nullptr, nullptr);
|
||||
hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs,
|
||||
HBGetHKerning,
|
||||
nullptr, nullptr);
|
||||
|
||||
sHBUnicodeFuncs =
|
||||
hb_unicode_funcs_create(hb_unicode_funcs_get_empty());
|
||||
hb_unicode_funcs_set_mirroring_func(sHBUnicodeFuncs,
|
||||
HBGetMirroring,
|
||||
nullptr, nullptr);
|
||||
hb_unicode_funcs_set_script_func(sHBUnicodeFuncs, HBGetScript,
|
||||
nullptr, nullptr);
|
||||
hb_unicode_funcs_set_general_category_func(sHBUnicodeFuncs,
|
||||
HBGetGeneralCategory,
|
||||
nullptr, nullptr);
|
||||
hb_unicode_funcs_set_combining_class_func(sHBUnicodeFuncs,
|
||||
HBGetCombiningClass,
|
||||
nullptr, nullptr);
|
||||
hb_unicode_funcs_set_eastasian_width_func(sHBUnicodeFuncs,
|
||||
HBGetEastAsianWidth,
|
||||
nullptr, nullptr);
|
||||
hb_unicode_funcs_set_compose_func(sHBUnicodeFuncs,
|
||||
HBUnicodeCompose,
|
||||
nullptr, nullptr);
|
||||
hb_unicode_funcs_set_decompose_func(sHBUnicodeFuncs,
|
||||
HBUnicodeDecompose,
|
||||
nullptr, nullptr);
|
||||
}
|
||||
|
||||
if (!mUseFontGetGlyph) {
|
||||
// get the cmap table and find offset to our subtable
|
||||
mCmapTable = entry->GetFontTable(TRUETYPE_TAG('c','m','a','p'));
|
||||
if (!mCmapTable) {
|
||||
NS_WARNING("failed to load cmap, glyphs will be missing");
|
||||
return false;
|
||||
}
|
||||
uint32_t len;
|
||||
const uint8_t* data = (const uint8_t*)hb_blob_get_data(mCmapTable, &len);
|
||||
bool symbol;
|
||||
mCmapFormat = gfxFontUtils::
|
||||
FindPreferredSubtable(data, len,
|
||||
&mSubtableOffset, &mUVSTableOffset,
|
||||
&symbol);
|
||||
}
|
||||
|
||||
if (!mUseFontGlyphWidths) {
|
||||
// if font doesn't implement GetGlyphWidth, we will be reading
|
||||
// the hmtx table directly;
|
||||
// read mNumLongMetrics from hhea table without caching its blob,
|
||||
// and preload/cache the hmtx table
|
||||
gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h','h','e','a'));
|
||||
if (hheaTable) {
|
||||
uint32_t len;
|
||||
const HMetricsHeader* hhea =
|
||||
reinterpret_cast<const HMetricsHeader*>
|
||||
(hb_blob_get_data(hheaTable, &len));
|
||||
if (len >= sizeof(HMetricsHeader)) {
|
||||
mNumLongMetrics = hhea->numberOfHMetrics;
|
||||
if (mNumLongMetrics > 0 &&
|
||||
int16_t(hhea->metricDataFormat) == 0) {
|
||||
// no point reading hmtx if number of entries is zero!
|
||||
// in that case, we won't be able to use this font
|
||||
// (this method will return FALSE below if mHmtx is null)
|
||||
mHmtxTable =
|
||||
entry->GetFontTable(TRUETYPE_TAG('h','m','t','x'));
|
||||
if (hb_blob_get_length(mHmtxTable) <
|
||||
mNumLongMetrics * sizeof(HLongMetric)) {
|
||||
// hmtx table is not large enough for the claimed
|
||||
// number of entries: invalid, do not use.
|
||||
hb_blob_destroy(mHmtxTable);
|
||||
mHmtxTable = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mHBFont = hb_font_create(mHBFace);
|
||||
hb_font_set_funcs(mHBFont, sHBFontFuncs, &mCallbackData, nullptr);
|
||||
hb_font_set_ppem(mHBFont, mFont->GetAdjustedSize(), mFont->GetAdjustedSize());
|
||||
uint32_t scale = FloatToFixed(mFont->GetAdjustedSize()); // 16.16 fixed-point
|
||||
hb_font_set_scale(mHBFont, scale, scale);
|
||||
}
|
||||
|
||||
if ((!mUseFontGetGlyph && mCmapFormat <= 0) ||
|
||||
(!mUseFontGlyphWidths && !mHmtxTable)) {
|
||||
// unable to shape with this font
|
||||
if (!Initialize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -945,6 +958,7 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
|
|||
nsAutoTArray<hb_feature_t,20> features;
|
||||
nsDataHashtable<nsUint32HashKey,uint32_t> mergedFeatures;
|
||||
|
||||
gfxFontEntry *entry = mFont->GetFontEntry();
|
||||
if (MergeFontFeatures(style,
|
||||
entry->mFeatureSettings,
|
||||
aShapedText->DisableLigatures(),
|
||||
|
|
|
@ -24,6 +24,7 @@ public:
|
|||
gfxContext *mContext;
|
||||
};
|
||||
|
||||
bool Initialize();
|
||||
virtual bool ShapeText(gfxContext *aContext,
|
||||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
|
@ -42,6 +43,11 @@ public:
|
|||
hb_position_t GetGlyphHAdvance(gfxContext *aContext,
|
||||
hb_codepoint_t glyph) const;
|
||||
|
||||
// get harfbuzz horizontal advance in 16.16 fixed point format.
|
||||
static hb_position_t
|
||||
HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
|
||||
hb_codepoint_t glyph, void *user_data);
|
||||
|
||||
hb_position_t GetHKerning(uint16_t aFirstGlyph,
|
||||
uint16_t aSecondGlyph) const;
|
||||
|
||||
|
|
|
@ -0,0 +1,459 @@
|
|||
/* 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 "gfxMathTable.h"
|
||||
|
||||
#include "MathTableStructures.h"
|
||||
#include "harfbuzz/hb.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
gfxMathTable::gfxMathTable(hb_blob_t* aMathTable)
|
||||
: mMathTable(aMathTable)
|
||||
, mGlyphConstruction(nullptr)
|
||||
, mGlyphID(-1)
|
||||
, mVertical(false)
|
||||
{
|
||||
}
|
||||
|
||||
gfxMathTable::~gfxMathTable()
|
||||
{
|
||||
hb_blob_destroy(mMathTable);
|
||||
}
|
||||
|
||||
bool
|
||||
gfxMathTable::HasValidHeaders()
|
||||
{
|
||||
const char* mathData = hb_blob_get_data(mMathTable, nullptr);
|
||||
// Verify the MATH table header.
|
||||
if (!ValidStructure(mathData, sizeof(MATHTableHeader))) {
|
||||
return false;
|
||||
}
|
||||
const MATHTableHeader* header = GetMATHTableHeader();
|
||||
if (uint32_t(header->mVersion) != 0x00010000 ||
|
||||
!ValidOffset(mathData, uint16_t(header->mMathConstants)) ||
|
||||
!ValidOffset(mathData, uint16_t(header->mMathGlyphInfo)) ||
|
||||
!ValidOffset(mathData, uint16_t(header->mMathVariants))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify the MathConstants header.
|
||||
const MathConstants* mathconstants = GetMathConstants();
|
||||
const char* start = reinterpret_cast<const char*>(mathconstants);
|
||||
if (!ValidStructure(start, sizeof(MathConstants))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify the MathGlyphInfo header.
|
||||
const MathGlyphInfo* mathglyphinfo = GetMathGlyphInfo();
|
||||
start = reinterpret_cast<const char*>(mathglyphinfo);
|
||||
if (!ValidStructure(start, sizeof(MathGlyphInfo))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify the MathVariants header.
|
||||
const MathVariants* mathvariants = GetMathVariants();
|
||||
start = reinterpret_cast<const char*>(mathvariants);
|
||||
if (!ValidStructure(start, sizeof(MathVariants)) ||
|
||||
!ValidStructure(start,
|
||||
sizeof(MathVariants) + sizeof(Offset) *
|
||||
(uint16_t(mathvariants->mVertGlyphCount) +
|
||||
uint16_t(mathvariants->mHorizGlyphCount))) ||
|
||||
!ValidOffset(start, uint16_t(mathvariants->mVertGlyphCoverage)) ||
|
||||
!ValidOffset(start, uint16_t(mathvariants->mHorizGlyphCoverage))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t
|
||||
gfxMathTable::GetMathConstant(gfxFontEntry::MathConstant aConstant)
|
||||
{
|
||||
const MathConstants* mathconstants = GetMathConstants();
|
||||
|
||||
if (aConstant <= gfxFontEntry::ScriptScriptPercentScaleDown) {
|
||||
return int16_t(mathconstants->mInt16[aConstant]);
|
||||
}
|
||||
|
||||
if (aConstant <= gfxFontEntry::DisplayOperatorMinHeight) {
|
||||
return
|
||||
uint16_t(mathconstants->
|
||||
mUint16[aConstant - gfxFontEntry::DelimitedSubFormulaMinHeight]);
|
||||
}
|
||||
|
||||
if (aConstant <= gfxFontEntry::RadicalKernAfterDegree) {
|
||||
return int16_t(mathconstants->
|
||||
mMathValues[aConstant - gfxFontEntry::MathLeading].mValue);
|
||||
}
|
||||
|
||||
return uint16_t(mathconstants->mRadicalDegreeBottomRaisePercent);
|
||||
}
|
||||
|
||||
bool
|
||||
gfxMathTable::GetMathItalicsCorrection(uint32_t aGlyphID,
|
||||
int16_t* aItalicCorrection)
|
||||
{
|
||||
const MathGlyphInfo* mathglyphinfo = GetMathGlyphInfo();
|
||||
|
||||
// Get the offset of the italic correction and verify whether it is valid.
|
||||
const char* start = reinterpret_cast<const char*>(mathglyphinfo);
|
||||
uint16_t offset = mathglyphinfo->mMathItalicsCorrectionInfo;
|
||||
if (offset == 0 || !ValidOffset(start, offset)) {
|
||||
return false;
|
||||
}
|
||||
start += offset;
|
||||
|
||||
// Verify the validity of the MathItalicsCorrectionInfo and retrieve it.
|
||||
if (!ValidStructure(start, sizeof(MathItalicsCorrectionInfo))) {
|
||||
return false;
|
||||
}
|
||||
const MathItalicsCorrectionInfo* italicsCorrectionInfo =
|
||||
reinterpret_cast<const MathItalicsCorrectionInfo*>(start);
|
||||
|
||||
// Get the coverage index for the glyph.
|
||||
offset = italicsCorrectionInfo->mCoverage;
|
||||
const Coverage* coverage =
|
||||
reinterpret_cast<const Coverage*>(start + offset);
|
||||
int32_t i = GetCoverageIndex(coverage, aGlyphID);
|
||||
|
||||
// Get the ItalicsCorrection.
|
||||
uint16_t count = italicsCorrectionInfo->mItalicsCorrectionCount;
|
||||
if (i < 0 || i >= count) {
|
||||
return false;
|
||||
}
|
||||
start = reinterpret_cast<const char*>(italicsCorrectionInfo + 1);
|
||||
if (!ValidStructure(start, count * sizeof(MathValueRecord))) {
|
||||
return false;
|
||||
}
|
||||
const MathValueRecord* mathValueRecordArray =
|
||||
reinterpret_cast<const MathValueRecord*>(start);
|
||||
|
||||
*aItalicCorrection = int16_t(mathValueRecordArray[i].mValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
gfxMathTable::GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
|
||||
uint16_t aSize)
|
||||
{
|
||||
// Select the glyph construction.
|
||||
SelectGlyphConstruction(aGlyphID, aVertical);
|
||||
if (!mGlyphConstruction) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Verify the validity of the array of the MathGlyphVariantRecord's and
|
||||
// whether there is a variant of the requested size.
|
||||
uint16_t count = mGlyphConstruction->mVariantCount;
|
||||
const char* start = reinterpret_cast<const char*>(mGlyphConstruction + 1);
|
||||
if (aSize >= count ||
|
||||
!ValidStructure(start, count * sizeof(MathGlyphVariantRecord))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return the glyph index of the requested size variant.
|
||||
const MathGlyphVariantRecord* recordArray =
|
||||
reinterpret_cast<const MathGlyphVariantRecord*>(start);
|
||||
return uint32_t(recordArray[aSize].mVariantGlyph);
|
||||
}
|
||||
|
||||
bool
|
||||
gfxMathTable::GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
|
||||
uint32_t aGlyphs[4])
|
||||
{
|
||||
// Get the glyph assembly corresponding to that (aGlyphID, aVertical) pair.
|
||||
const GlyphAssembly* glyphAssembly = GetGlyphAssembly(aGlyphID, aVertical);
|
||||
if (!glyphAssembly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify the validity of the array of GlyphPartRecord's and retrieve it.
|
||||
uint16_t count = glyphAssembly->mPartCount;
|
||||
const char* start = reinterpret_cast<const char*>(glyphAssembly + 1);
|
||||
if (!ValidStructure(start, count * sizeof(GlyphPartRecord))) {
|
||||
return false;
|
||||
}
|
||||
const GlyphPartRecord* recordArray =
|
||||
reinterpret_cast<const GlyphPartRecord*>(start);
|
||||
|
||||
// XXXfredw The structure of the Open Type Math table is a bit more general
|
||||
// than the one currently used by the nsMathMLChar code, so we try to fallback
|
||||
// in reasonable way. We use the approach of the copyComponents function in
|
||||
// github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py
|
||||
//
|
||||
// The nsMathMLChar code can use at most 3 non extender pieces (aGlyphs[0],
|
||||
// aGlyphs[1] and aGlyphs[2]) and the extenders between these pieces should
|
||||
// all be the same (aGlyphs[4]). Also, the parts of vertical assembly are
|
||||
// stored from bottom to top in the Open Type MATH table while they are
|
||||
// stored from top to bottom in nsMathMLChar.
|
||||
|
||||
// Count the number of non extender pieces
|
||||
uint16_t nonExtenderCount = 0;
|
||||
for (uint16_t i = 0; i < count; i++) {
|
||||
if (!(uint16_t(recordArray[i].mPartFlags) & PART_FLAG_EXTENDER)) {
|
||||
nonExtenderCount++;
|
||||
}
|
||||
}
|
||||
if (nonExtenderCount > 3) {
|
||||
// Not supported: too many pieces
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now browse the list of pieces
|
||||
|
||||
// 0 = look for a left/bottom glyph
|
||||
// 1 = look for an extender between left/bottom and mid
|
||||
// 2 = look for a middle glyph
|
||||
// 3 = look for an extender between middle and right/top
|
||||
// 4 = look for a right/top glyph
|
||||
// 5 = no more piece expected
|
||||
uint8_t state = 0;
|
||||
|
||||
// First extender char found.
|
||||
uint32_t extenderChar = 0;
|
||||
|
||||
// Clear the aGlyphs table.
|
||||
memset(aGlyphs, 0, sizeof(uint32_t) * 4);
|
||||
|
||||
for (uint16_t i = 0; i < count; i++) {
|
||||
|
||||
bool isExtender = uint16_t(recordArray[i].mPartFlags) & PART_FLAG_EXTENDER;
|
||||
uint32_t glyph = recordArray[i].mGlyph;
|
||||
|
||||
if ((state == 1 || state == 2) && nonExtenderCount < 3) {
|
||||
// do not try to find a middle glyph
|
||||
state += 2;
|
||||
}
|
||||
|
||||
if (isExtender) {
|
||||
if (!extenderChar) {
|
||||
extenderChar = glyph;
|
||||
aGlyphs[3] = extenderChar;
|
||||
} else if (extenderChar != glyph) {
|
||||
// Not supported: different extenders
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state == 0) { // or state == 1
|
||||
// ignore left/bottom piece and multiple successive extenders
|
||||
state = 1;
|
||||
} else if (state == 2) { // or state == 3
|
||||
// ignore middle piece and multiple successive extenders
|
||||
state = 3;
|
||||
} else if (state >= 4) {
|
||||
// Not supported: unexpected extender
|
||||
return false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state == 0) {
|
||||
// copy left/bottom part
|
||||
aGlyphs[mVertical ? 2 : 0] = glyph;
|
||||
state = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state == 1 || state == 2) {
|
||||
// copy middle part
|
||||
aGlyphs[1] = glyph;
|
||||
state = 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state == 3 || state == 4) {
|
||||
// copy right/top part
|
||||
aGlyphs[mVertical ? 0 : 2] = glyph;
|
||||
state = 5;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
gfxMathTable::ValidStructure(const char* aStart, uint16_t aSize)
|
||||
{
|
||||
unsigned int mathDataLength;
|
||||
const char* mathData = hb_blob_get_data(mMathTable, &mathDataLength);
|
||||
return (mathData <= aStart &&
|
||||
aStart + aSize <= mathData + mathDataLength);
|
||||
}
|
||||
|
||||
bool
|
||||
gfxMathTable::ValidOffset(const char* aStart, uint16_t aOffset)
|
||||
{
|
||||
unsigned int mathDataLength;
|
||||
const char* mathData = hb_blob_get_data(mMathTable, &mathDataLength);
|
||||
return (mathData <= aStart + aOffset &&
|
||||
aStart + aOffset < mathData + mathDataLength);
|
||||
}
|
||||
|
||||
const MATHTableHeader*
|
||||
gfxMathTable::GetMATHTableHeader()
|
||||
{
|
||||
const char* mathData = hb_blob_get_data(mMathTable, nullptr);
|
||||
return reinterpret_cast<const MATHTableHeader*>(mathData);
|
||||
}
|
||||
|
||||
const MathConstants*
|
||||
gfxMathTable::GetMathConstants()
|
||||
{
|
||||
const char* mathData = hb_blob_get_data(mMathTable, nullptr);
|
||||
return
|
||||
reinterpret_cast<const MathConstants*>(mathData +
|
||||
uint16_t(GetMATHTableHeader()->
|
||||
mMathConstants));
|
||||
}
|
||||
|
||||
const MathGlyphInfo*
|
||||
gfxMathTable::GetMathGlyphInfo()
|
||||
{
|
||||
const char* mathData = hb_blob_get_data(mMathTable, nullptr);
|
||||
return
|
||||
reinterpret_cast<const MathGlyphInfo*>(mathData +
|
||||
uint16_t(GetMATHTableHeader()->
|
||||
mMathGlyphInfo));
|
||||
}
|
||||
|
||||
const MathVariants*
|
||||
gfxMathTable::GetMathVariants()
|
||||
{
|
||||
const char* mathData = hb_blob_get_data(mMathTable, nullptr);
|
||||
return
|
||||
reinterpret_cast<const MathVariants*>(mathData +
|
||||
uint16_t(GetMATHTableHeader()->
|
||||
mMathVariants));
|
||||
}
|
||||
|
||||
const GlyphAssembly*
|
||||
gfxMathTable::GetGlyphAssembly(uint32_t aGlyphID, bool aVertical)
|
||||
{
|
||||
// Select the glyph construction.
|
||||
SelectGlyphConstruction(aGlyphID, aVertical);
|
||||
if (!mGlyphConstruction) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the offset of the glyph assembly and verify whether it is valid.
|
||||
const char* start = reinterpret_cast<const char*>(mGlyphConstruction);
|
||||
uint16_t offset = mGlyphConstruction->mGlyphAssembly;
|
||||
if (offset == 0 || !ValidOffset(start, offset)) {
|
||||
return nullptr;
|
||||
}
|
||||
start += offset;
|
||||
|
||||
// Verify the validity of the GlyphAssembly and return it.
|
||||
if (!ValidStructure(start, sizeof(GlyphAssembly))) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<const GlyphAssembly*>(start);
|
||||
}
|
||||
|
||||
int32_t
|
||||
gfxMathTable::GetCoverageIndex(const Coverage* aCoverage, uint32_t aGlyph)
|
||||
{
|
||||
if (uint16_t(aCoverage->mFormat) == 1) {
|
||||
// Coverage Format 1: list of individual glyph indices in the glyph set.
|
||||
const CoverageFormat1* table =
|
||||
reinterpret_cast<const CoverageFormat1*>(aCoverage);
|
||||
uint16_t count = table->mGlyphCount;
|
||||
const char* start = reinterpret_cast<const char*>(table + 1);
|
||||
if (ValidStructure(start, count * sizeof(GlyphID))) {
|
||||
const GlyphID* glyphArray =
|
||||
reinterpret_cast<const GlyphID*>(start);
|
||||
uint32_t imin = 0, imax = count;
|
||||
while (imin < imax) {
|
||||
uint32_t imid = (imin + imax) >> 1;
|
||||
uint16_t glyphMid = glyphArray[imid];
|
||||
if (glyphMid == aGlyph) {
|
||||
return imid;
|
||||
}
|
||||
if (glyphMid < aGlyph) {
|
||||
imin = imid + 1;
|
||||
} else {
|
||||
imax = imid;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (uint16_t(aCoverage->mFormat) == 2) {
|
||||
// Coverage Format 2: ranges of consecutive indices.
|
||||
const CoverageFormat2* table =
|
||||
reinterpret_cast<const CoverageFormat2*>(aCoverage);
|
||||
uint16_t count = table->mRangeCount;
|
||||
const char* start = reinterpret_cast<const char*>(table + 1);
|
||||
if (ValidStructure(start, count * sizeof(RangeRecord))) {
|
||||
const RangeRecord* rangeArray =
|
||||
reinterpret_cast<const RangeRecord*>(start);
|
||||
uint32_t imin = 0, imax = count;
|
||||
while (imin < imax) {
|
||||
uint32_t imid = (imin + imax) >> 1;
|
||||
uint16_t rStart = rangeArray[imid].mStart;
|
||||
uint16_t rEnd = rangeArray[imid].mEnd;
|
||||
if (rEnd < aGlyph) {
|
||||
imin = imid + 1;
|
||||
} else if (aGlyph < rStart) {
|
||||
imax = imid;
|
||||
} else {
|
||||
return (uint16_t(rangeArray[imid].mStartCoverageIndex) +
|
||||
aGlyph - rStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
gfxMathTable::SelectGlyphConstruction(uint32_t aGlyphID, bool aVertical)
|
||||
{
|
||||
if (mGlyphID == aGlyphID && mVertical == aVertical) {
|
||||
// The (glyph, direction) pair is already selected: nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
// Update our cached values.
|
||||
mVertical = aVertical;
|
||||
mGlyphID = aGlyphID;
|
||||
mGlyphConstruction = nullptr;
|
||||
|
||||
// Get the coverage index for the new values.
|
||||
const MathVariants* mathvariants = GetMathVariants();
|
||||
const char* start = reinterpret_cast<const char*>(mathvariants);
|
||||
uint16_t offset = (aVertical ?
|
||||
mathvariants->mVertGlyphCoverage :
|
||||
mathvariants->mHorizGlyphCoverage);
|
||||
const Coverage* coverage =
|
||||
reinterpret_cast<const Coverage*>(start + offset);
|
||||
int32_t i = GetCoverageIndex(coverage, aGlyphID);
|
||||
|
||||
// Get the offset to the glyph construction.
|
||||
uint16_t count = (aVertical ?
|
||||
mathvariants->mVertGlyphCount :
|
||||
mathvariants->mHorizGlyphCount);
|
||||
start = reinterpret_cast<const char*>(mathvariants + 1);
|
||||
if (i < 0 || i >= count) {
|
||||
return;
|
||||
}
|
||||
if (!aVertical) {
|
||||
start += uint16_t(mathvariants->mVertGlyphCount) * sizeof(Offset);
|
||||
}
|
||||
if (!ValidStructure(start, count * sizeof(Offset))) {
|
||||
return;
|
||||
}
|
||||
const Offset* offsetArray = reinterpret_cast<const Offset*>(start);
|
||||
offset = uint16_t(offsetArray[i]);
|
||||
|
||||
// Make mGlyphConstruction point to the desired glyph construction.
|
||||
start = reinterpret_cast<const char*>(mathvariants);
|
||||
if (!ValidStructure(start + offset, sizeof(MathGlyphConstruction))) {
|
||||
return;
|
||||
}
|
||||
mGlyphConstruction =
|
||||
reinterpret_cast<const MathGlyphConstruction*>(start + offset);
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/* 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 GFX_MATH_TABLE_H
|
||||
#define GFX_MATH_TABLE_H
|
||||
|
||||
#include "gfxFont.h"
|
||||
|
||||
struct Coverage;
|
||||
struct GlyphAssembly;
|
||||
struct MATHTableHeader;
|
||||
struct MathConstants;
|
||||
struct MathGlyphConstruction;
|
||||
struct MathGlyphInfo;
|
||||
struct MathVariants;
|
||||
|
||||
/**
|
||||
* Used by |gfxFontEntry| to represent the MATH table of an OpenType font.
|
||||
* Each |gfxFontEntry| owns at most one |gfxMathTable| instance.
|
||||
*/
|
||||
class gfxMathTable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param aMathTable The MATH table from the OpenType font
|
||||
*
|
||||
* The gfxMathTable object takes over ownership of the blob references
|
||||
* that are passed in, and will hb_blob_destroy() them when finished;
|
||||
* the caller should -not- destroy this reference.
|
||||
*/
|
||||
gfxMathTable(hb_blob_t* aMathTable);
|
||||
|
||||
/**
|
||||
* Releases our reference to the MATH table and cleans up everything else.
|
||||
*/
|
||||
~gfxMathTable();
|
||||
|
||||
/**
|
||||
* Returns the value of the specified constant from the MATH table.
|
||||
*/
|
||||
int32_t GetMathConstant(gfxFontEntry::MathConstant aConstant);
|
||||
|
||||
/**
|
||||
* If the MATH table contains an italic correction for that glyph, this
|
||||
* function gets the value and returns true. Otherwise it returns false.
|
||||
*/
|
||||
bool
|
||||
GetMathItalicsCorrection(uint32_t aGlyphID, int16_t* aItalicCorrection);
|
||||
|
||||
/**
|
||||
* @param aGlyphID glyph index of the character we want to stretch
|
||||
* @param aVertical direction of the stretching (vertical/horizontal)
|
||||
* @param aSize the desired size variant
|
||||
*
|
||||
* Returns the glyph index of the desired size variant or 0 if there is not
|
||||
* any such size variant.
|
||||
*/
|
||||
uint32_t GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
|
||||
uint16_t aSize);
|
||||
|
||||
/**
|
||||
* @param aGlyphID glyph index of the character we want to stretch
|
||||
* @param aVertical direction of the stretching (vertical/horizontal)
|
||||
* @param aGlyphs pre-allocated buffer of 4 elements where the glyph
|
||||
* indexes (or 0 for absent parts) will be stored. The parts are stored in
|
||||
* the order expected by the nsMathMLChar: Top (or Left), Middle, Bottom
|
||||
* (or Right), Glue.
|
||||
*
|
||||
* Tries to fill-in aGlyphs with the relevant glyph indexes and returns
|
||||
* whether the operation was successful. The function returns false if
|
||||
* there is not any assembly for the character we want to stretch or if
|
||||
* the format is not supported by the nsMathMLChar code.
|
||||
*
|
||||
*/
|
||||
bool GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
|
||||
uint32_t aGlyphs[4]);
|
||||
|
||||
protected:
|
||||
friend class gfxFontEntry;
|
||||
// This allows gfxFontEntry to verify the validity of the main headers
|
||||
// before starting to use the MATH table.
|
||||
bool HasValidHeaders();
|
||||
|
||||
private:
|
||||
// HarfBuzz blob where the MATH table is stored.
|
||||
hb_blob_t* mMathTable;
|
||||
|
||||
// Cached values for the latest (mGlyphID, mVertical) pair that has been
|
||||
// accessed and the corresponding glyph construction. These are verified
|
||||
// by SelectGlyphConstruction and updated if necessary.
|
||||
// mGlyphConstruction will be set to nullptr if no construction is defined
|
||||
// for the glyph. If non-null, its mGlyphAssembly and mVariantCount fields
|
||||
// may be safely read, but no further validation will have been done.
|
||||
const MathGlyphConstruction* mGlyphConstruction;
|
||||
uint32_t mGlyphID;
|
||||
bool mVertical;
|
||||
void SelectGlyphConstruction(uint32_t aGlyphID, bool aVertical);
|
||||
|
||||
// Access to some structures of the MATH table.
|
||||
// These accessors just return a pointer, but do NOT themselves check the
|
||||
// validity of anything. Until we've checked that HasValidHeaders (which
|
||||
// does validate them) returns true, they might return pointers that cannot
|
||||
// even safely be dereferenced. GetGlyphAssembly may return nullptr if the
|
||||
// given glyph has no assembly defined.
|
||||
const MATHTableHeader* GetMATHTableHeader();
|
||||
const MathConstants* GetMathConstants();
|
||||
const MathGlyphInfo* GetMathGlyphInfo();
|
||||
const MathVariants* GetMathVariants();
|
||||
const GlyphAssembly* GetGlyphAssembly(uint32_t aGlyphID, bool aVertical);
|
||||
|
||||
// Verify whether a structure or an offset belongs to the math data and can
|
||||
// be read safely.
|
||||
bool ValidStructure(const char* aStructStart, uint16_t aStructSize);
|
||||
bool ValidOffset(const char* aOffsetStart, uint16_t aOffset);
|
||||
|
||||
// Get the coverage index of a glyph index from an Open Type coverage table
|
||||
// or -1 if the glyph index is not found.
|
||||
int32_t GetCoverageIndex(const Coverage* aCoverage, uint32_t aGlyph);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1624,10 +1624,7 @@ gfxFcFont::ShapeText(gfxContext *aContext,
|
|||
|
||||
if (!ok) {
|
||||
if (!mHarfBuzzShaper) {
|
||||
gfxFT2LockedFace face(this);
|
||||
mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
|
||||
// Used by gfxHarfBuzzShaper, currently only for kerning
|
||||
mFUnitsConvFactor = face.XScale();
|
||||
}
|
||||
ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
|
||||
aScript, aShapedText);
|
||||
|
|
|
@ -804,6 +804,7 @@ gfxWindowsPlatform::GetCommonFallbackFonts(const uint32_t aCh,
|
|||
aFontList.AppendElement(kFontEbrima);
|
||||
break;
|
||||
case 0x09:
|
||||
aFontList.AppendElement(kFontNirmalaUI);
|
||||
aFontList.AppendElement(kFontUtsaah);
|
||||
aFontList.AppendElement(kFontAparajita);
|
||||
break;
|
||||
|
|
|
@ -26,6 +26,7 @@ EXPORTS += [
|
|||
'gfxGradientCache.h',
|
||||
'gfxImageSurface.h',
|
||||
'gfxLineSegment.h',
|
||||
'gfxMathTable.h',
|
||||
'gfxMatrix.h',
|
||||
'gfxPath.h',
|
||||
'gfxPattern.h',
|
||||
|
@ -235,6 +236,7 @@ UNIFIED_SOURCES += [
|
|||
'gfxGraphiteShaper.cpp',
|
||||
'gfxHarfBuzzShaper.cpp',
|
||||
'gfxImageSurface.cpp',
|
||||
'gfxMathTable.cpp',
|
||||
'gfxMatrix.cpp',
|
||||
'gfxPath.cpp',
|
||||
'gfxPattern.cpp',
|
||||
|
|
|
@ -34,6 +34,13 @@ typedef enum {
|
|||
kPlatformCharsetSel_PlainTextInFile = 7
|
||||
} nsPlatformCharsetSel;
|
||||
|
||||
/**
|
||||
* DO NOT ADD NEW USES OF THIS INTERFACE!
|
||||
* Removal is https://bugzilla.mozilla.org/show_bug.cgi?id=943272
|
||||
*
|
||||
* Instead, use UTF-16 APIs on Windows and UTF-8 APIs everywhere else.
|
||||
* Assume plain text files are UTF-8.
|
||||
*/
|
||||
class nsIPlatformCharset : public nsISupports
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -139,6 +139,16 @@ class Nursery
|
|||
return total;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE uintptr_t start() const {
|
||||
JS_ASSERT(runtime_);
|
||||
return ((JS::shadow::Runtime *)runtime_)->gcNurseryStart_;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE uintptr_t heapEnd() const {
|
||||
JS_ASSERT(runtime_);
|
||||
return ((JS::shadow::Runtime *)runtime_)->gcNurseryEnd_;
|
||||
}
|
||||
|
||||
private:
|
||||
/*
|
||||
* The start and end pointers are stored under the runtime so that we can
|
||||
|
@ -190,16 +200,6 @@ class Nursery
|
|||
return reinterpret_cast<NurseryChunkLayout *>(start())[index];
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE uintptr_t start() const {
|
||||
JS_ASSERT(runtime_);
|
||||
return ((JS::shadow::Runtime *)runtime_)->gcNurseryStart_;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE uintptr_t heapEnd() const {
|
||||
JS_ASSERT(runtime_);
|
||||
return ((JS::shadow::Runtime *)runtime_)->gcNurseryEnd_;
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void setCurrentChunk(int chunkno) {
|
||||
JS_ASSERT(chunkno < NumNurseryChunks);
|
||||
JS_ASSERT(chunkno < numActiveChunks_);
|
||||
|
@ -314,10 +314,7 @@ class Nursery
|
|||
#endif
|
||||
|
||||
friend class gc::MinorCollectionTracer;
|
||||
friend class jit::CodeGenerator;
|
||||
friend class jit::MacroAssembler;
|
||||
friend class jit::ICStubCompiler;
|
||||
friend class jit::BaselineCompiler;
|
||||
friend void SetGCZeal(JSRuntime *, uint8_t, uint32_t);
|
||||
};
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "jit/Snapshots.h"
|
||||
#include "vm/TraceLogging.h"
|
||||
|
||||
#include "jit/IonFrameIterator-inl.h"
|
||||
#include "jit/JitFrameIterator-inl.h"
|
||||
#include "vm/Stack-inl.h"
|
||||
|
||||
using namespace js;
|
||||
|
@ -24,15 +24,15 @@ using namespace js::jit;
|
|||
// These constructor are exactly the same except for the type of the iterator
|
||||
// which is given to the SnapshotIterator constructor. Doing so avoid the
|
||||
// creation of virtual functions for the IonIterator but may introduce some
|
||||
// weirdness as IonInlineIterator is using an IonFrameIterator reference.
|
||||
// weirdness as IonInlineIterator is using a JitFrameIterator reference.
|
||||
//
|
||||
// If a function relies on ionScript() or to use OsiIndex(), due to the
|
||||
// lack of virtual, these functions will use the IonFrameIterator reference
|
||||
// lack of virtual, these functions will use the JitFrameIterator reference
|
||||
// contained in the InlineFrameIterator and thus are not able to recover
|
||||
// correctly the data stored in IonBailoutIterator.
|
||||
//
|
||||
// Currently, such cases should not happen because our only use case of the
|
||||
// IonFrameIterator within InlineFrameIterator is to read the frame content, or
|
||||
// JitFrameIterator within InlineFrameIterator is to read the frame content, or
|
||||
// to clone it to find the parent scripted frame. Both use cases are fine and
|
||||
// should not cause any issue since the only potential issue is to read the
|
||||
// bailed out frame.
|
||||
|
@ -63,7 +63,7 @@ IonBailoutIterator::dump() const
|
|||
++frames;
|
||||
}
|
||||
} else {
|
||||
IonFrameIterator::dump();
|
||||
JitFrameIterator::dump();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,8 +151,8 @@ jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
|
|||
}
|
||||
|
||||
IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
|
||||
const IonFrameIterator &frame)
|
||||
: IonFrameIterator(activations),
|
||||
const JitFrameIterator &frame)
|
||||
: JitFrameIterator(activations),
|
||||
machine_(frame.machineState())
|
||||
{
|
||||
returnAddressToFp_ = frame.returnAddressToFp();
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
#include "jstypes.h"
|
||||
|
||||
#include "jit/IonFrameIterator.h"
|
||||
#include "jit/IonFrames.h"
|
||||
#include "jit/JitFrameIterator.h"
|
||||
#include "vm/Stack.h"
|
||||
|
||||
namespace js {
|
||||
|
@ -111,8 +111,8 @@ class InvalidationBailoutStack;
|
|||
|
||||
// This iterator is constructed at a time where there is no exit frame at the
|
||||
// moment. They must be initialized to the first JS frame instead of the exit
|
||||
// frame as usually done with IonFrameIterator.
|
||||
class IonBailoutIterator : public IonFrameIterator
|
||||
// frame as usually done with JitFrameIterator.
|
||||
class IonBailoutIterator : public JitFrameIterator
|
||||
{
|
||||
MachineState machine_;
|
||||
uint32_t snapshotOffset_;
|
||||
|
@ -122,7 +122,7 @@ class IonBailoutIterator : public IonFrameIterator
|
|||
public:
|
||||
IonBailoutIterator(const JitActivationIterator &activations, BailoutStack *sp);
|
||||
IonBailoutIterator(const JitActivationIterator &activations, InvalidationBailoutStack *sp);
|
||||
IonBailoutIterator(const JitActivationIterator &activations, const IonFrameIterator &frame);
|
||||
IonBailoutIterator(const JitActivationIterator &activations, const JitFrameIterator &frame);
|
||||
|
||||
SnapshotOffset snapshotOffset() const {
|
||||
JS_ASSERT(topIonScript_);
|
||||
|
@ -138,7 +138,7 @@ class IonBailoutIterator : public IonFrameIterator
|
|||
IonScript *ionScript() const {
|
||||
if (topIonScript_)
|
||||
return topIonScript_;
|
||||
return IonFrameIterator::ionScript();
|
||||
return JitFrameIterator::ionScript();
|
||||
}
|
||||
|
||||
void dump() const;
|
||||
|
|
|
@ -1538,7 +1538,7 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo *bailoutInfo)
|
|||
RootedScript outerScript(cx, nullptr);
|
||||
|
||||
JS_ASSERT(cx->currentlyRunningInJit());
|
||||
IonFrameIterator iter(cx);
|
||||
JitFrameIterator iter(cx);
|
||||
|
||||
uint32_t frameno = 0;
|
||||
while (frameno < numFrames) {
|
||||
|
|
|
@ -416,7 +416,6 @@ BaselineCompiler::emitEpilogue()
|
|||
#ifdef JSGC_GENERATIONAL
|
||||
// On input:
|
||||
// R2.scratchReg() contains object being written to.
|
||||
// R1.scratchReg() contains slot index being written to.
|
||||
// Otherwise, baseline stack will be synced, so all other registers are usable as scratch.
|
||||
// This calls:
|
||||
// void PostWriteBarrier(JSRuntime *rt, JSObject *obj);
|
||||
|
@ -2101,15 +2100,13 @@ BaselineCompiler::emit_JSOP_SETALIASEDVAR()
|
|||
// Fully sync the stack if post-barrier is needed.
|
||||
// Scope coordinate object is already in R2.scratchReg().
|
||||
frame.syncStack(0);
|
||||
Register temp = R1.scratchReg();
|
||||
|
||||
Nursery &nursery = cx->runtime()->gcNursery;
|
||||
Label skipBarrier;
|
||||
Label isTenured;
|
||||
masm.branchTestObject(Assembler::NotEqual, R0, &skipBarrier);
|
||||
masm.branchPtr(Assembler::Below, objReg, ImmWord(nursery.start()), &isTenured);
|
||||
masm.branchPtr(Assembler::Below, objReg, ImmWord(nursery.heapEnd()), &skipBarrier);
|
||||
masm.branchPtrInNurseryRange(objReg, temp, &skipBarrier);
|
||||
|
||||
masm.bind(&isTenured);
|
||||
masm.call(&postBarrierSlot_);
|
||||
|
||||
masm.bind(&skipBarrier);
|
||||
|
@ -2419,6 +2416,7 @@ BaselineCompiler::emitFormalArgAccess(uint32_t arg, bool get)
|
|||
#ifdef JSGC_GENERATIONAL
|
||||
// Fully sync the stack if post-barrier is needed.
|
||||
frame.syncStack(0);
|
||||
Register temp = R1.scratchReg();
|
||||
|
||||
// Reload the arguments object
|
||||
Register reg = R2.scratchReg();
|
||||
|
@ -2426,11 +2424,8 @@ BaselineCompiler::emitFormalArgAccess(uint32_t arg, bool get)
|
|||
|
||||
Nursery &nursery = cx->runtime()->gcNursery;
|
||||
Label skipBarrier;
|
||||
Label isTenured;
|
||||
masm.branchPtr(Assembler::Below, reg, ImmWord(nursery.start()), &isTenured);
|
||||
masm.branchPtr(Assembler::Below, reg, ImmWord(nursery.heapEnd()), &skipBarrier);
|
||||
masm.branchPtrInNurseryRange(reg, temp, &skipBarrier);
|
||||
|
||||
masm.bind(&isTenured);
|
||||
masm.call(&postBarrierSlot_);
|
||||
|
||||
masm.bind(&skipBarrier);
|
||||
|
|
|
@ -28,7 +28,7 @@ MarkLocals(BaselineFrame *frame, JSTracer *trc, unsigned start, unsigned end)
|
|||
}
|
||||
|
||||
void
|
||||
BaselineFrame::trace(JSTracer *trc, IonFrameIterator &frameIterator)
|
||||
BaselineFrame::trace(JSTracer *trc, JitFrameIterator &frameIterator)
|
||||
{
|
||||
replaceCalleeToken(MarkCalleeToken(trc, calleeToken()));
|
||||
|
||||
|
@ -208,7 +208,7 @@ BaselineFrame::initForOsr(InterpreterFrame *fp, uint32_t numStackValues)
|
|||
// debugger, wants a valid return address, but it's okay to just pick one.
|
||||
// In debug mode there's always at least 1 ICEntry (since there are always
|
||||
// debug prologue/epilogue calls).
|
||||
IonFrameIterator iter(cx);
|
||||
JitFrameIterator iter(cx);
|
||||
JS_ASSERT(iter.returnAddress() == nullptr);
|
||||
BaselineScript *baseline = fp->script()->baselineScript();
|
||||
iter.current()->setReturnAddress(baseline->returnAddressForIC(baseline->icEntry(0)));
|
||||
|
|
|
@ -296,7 +296,7 @@ class BaselineFrame
|
|||
flags_ |= OVER_RECURSED;
|
||||
}
|
||||
|
||||
void trace(JSTracer *trc, IonFrameIterator &frame);
|
||||
void trace(JSTracer *trc, JitFrameIterator &frame);
|
||||
|
||||
bool isFunctionFrame() const {
|
||||
return CalleeTokenIsFunction(calleeToken());
|
||||
|
|
|
@ -738,10 +738,7 @@ ICStubCompiler::emitPostWriteBarrierSlot(MacroAssembler &masm, Register obj, Val
|
|||
Label skipBarrier;
|
||||
masm.branchTestObject(Assembler::NotEqual, val, &skipBarrier);
|
||||
|
||||
Label isTenured;
|
||||
masm.branchPtr(Assembler::Below, obj, ImmWord(nursery.start()), &isTenured);
|
||||
masm.branchPtr(Assembler::Below, obj, ImmWord(nursery.heapEnd()), &skipBarrier);
|
||||
masm.bind(&isTenured);
|
||||
masm.branchPtrInNurseryRange(obj, scratch, &skipBarrier);
|
||||
|
||||
Register valReg = masm.extractObject(val, scratch);
|
||||
masm.branchPtr(Assembler::Below, valReg, ImmWord(nursery.start()), &skipBarrier);
|
||||
|
@ -773,7 +770,7 @@ IsTopFrameConstructing(JSContext *cx)
|
|||
{
|
||||
JS_ASSERT(cx->currentlyRunningInJit());
|
||||
JitActivationIterator activations(cx->runtime());
|
||||
IonFrameIterator iter(activations);
|
||||
JitFrameIterator iter(activations);
|
||||
JS_ASSERT(iter.type() == JitFrame_Exit);
|
||||
|
||||
++iter;
|
||||
|
|
|
@ -931,7 +931,7 @@ jit::ToggleBaselineSPS(JSRuntime *runtime, bool enable)
|
|||
static void
|
||||
MarkActiveBaselineScripts(JSRuntime *rt, const JitActivationIterator &activation)
|
||||
{
|
||||
for (jit::IonFrameIterator iter(activation); !iter.done(); ++iter) {
|
||||
for (jit::JitFrameIterator iter(activation); !iter.done(); ++iter) {
|
||||
switch (iter.type()) {
|
||||
case JitFrame_BaselineJS:
|
||||
iter.script()->baselineScript()->setActive();
|
||||
|
|
|
@ -1012,9 +1012,12 @@ CodeGenerator::visitLambdaArrow(LLambdaArrow *lir)
|
|||
|
||||
emitLambdaInit(output, scopeChain, info);
|
||||
|
||||
// Store the lexical |this| value.
|
||||
// Initialize extended slots. Lexical |this| is stored in the first one.
|
||||
MOZ_ASSERT(info.flags & JSFunction::EXTENDED);
|
||||
masm.storeValue(thisv, Address(output, FunctionExtended::offsetOfArrowThisSlot()));
|
||||
static_assert(FunctionExtended::NUM_EXTENDED_SLOTS == 2, "All slots must be initialized");
|
||||
static_assert(FunctionExtended::ARROW_THIS_SLOT == 0, "|this| must be stored in first slot");
|
||||
masm.storeValue(thisv, Address(output, FunctionExtended::offsetOfExtendedSlot(0)));
|
||||
masm.storeValue(UndefinedValue(), Address(output, FunctionExtended::offsetOfExtendedSlot(1)));
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
return true;
|
||||
|
@ -1828,22 +1831,16 @@ CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO *lir)
|
|||
if (!addOutOfLineCode(ool))
|
||||
return false;
|
||||
|
||||
const Nursery &nursery = GetIonContext()->runtime->gcNursery();
|
||||
Register temp = ToRegister(lir->temp());
|
||||
Register temp = ToTempRegisterOrInvalid(lir->temp());
|
||||
|
||||
if (lir->object()->isConstant()) {
|
||||
const Nursery &nursery = GetIonContext()->runtime->gcNursery();
|
||||
JS_ASSERT(!nursery.isInside(&lir->object()->toConstant()->toObject()));
|
||||
} else {
|
||||
Register objreg = ToRegister(lir->object());
|
||||
masm.movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
|
||||
masm.addPtr(objreg, temp);
|
||||
masm.branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), ool->rejoin());
|
||||
masm.branchPtrInNurseryRange(ToRegister(lir->object()), temp, ool->rejoin());
|
||||
}
|
||||
|
||||
Register valuereg = ToRegister(lir->value());
|
||||
masm.movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
|
||||
masm.addPtr(valuereg, temp);
|
||||
masm.branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), ool->entry());
|
||||
masm.branchPtrInNurseryRange(ToRegister(lir->value()), temp, ool->entry());
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
#endif
|
||||
|
@ -1858,27 +1855,17 @@ CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV *lir)
|
|||
if (!addOutOfLineCode(ool))
|
||||
return false;
|
||||
|
||||
ValueOperand value = ToValue(lir, LPostWriteBarrierV::Input);
|
||||
masm.branchTestObject(Assembler::NotEqual, value, ool->rejoin());
|
||||
|
||||
const Nursery &nursery = GetIonContext()->runtime->gcNursery();
|
||||
Register temp = ToTempRegisterOrInvalid(lir->temp());
|
||||
|
||||
if (lir->object()->isConstant()) {
|
||||
const Nursery &nursery = GetIonContext()->runtime->gcNursery();
|
||||
JS_ASSERT(!nursery.isInside(&lir->object()->toConstant()->toObject()));
|
||||
} else {
|
||||
Register temp = ToRegister(lir->temp());
|
||||
Register objreg = ToRegister(lir->object());
|
||||
masm.movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
|
||||
masm.addPtr(objreg, temp);
|
||||
masm.branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), ool->rejoin());
|
||||
masm.branchPtrInNurseryRange(ToRegister(lir->object()), temp, ool->rejoin());
|
||||
}
|
||||
|
||||
// This section is a little different because we mustn't trash the temp
|
||||
// register before we use its contents.
|
||||
Register temp = ToRegister(lir->temp());
|
||||
masm.unboxObject(value, temp);
|
||||
masm.addPtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
|
||||
masm.branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), ool->entry());
|
||||
ValueOperand value = ToValue(lir, LPostWriteBarrierV::Input);
|
||||
masm.branchValueIsNurseryObject(value, temp, ool->entry());
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
#endif
|
||||
|
|
|
@ -2514,7 +2514,7 @@ InvalidateActivation(FreeOp *fop, uint8_t *ionTop, bool invalidateAll)
|
|||
|
||||
size_t frameno = 1;
|
||||
|
||||
for (IonFrameIterator it(ionTop, SequentialExecution); !it.done(); ++it, ++frameno) {
|
||||
for (JitFrameIterator it(ionTop, SequentialExecution); !it.done(); ++it, ++frameno) {
|
||||
JS_ASSERT_IF(frameno == 1, it.type() == JitFrame_Exit);
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -2880,7 +2880,7 @@ jit::ForbidCompilation(JSContext *cx, JSScript *script, ExecutionMode mode)
|
|||
case SequentialExecution:
|
||||
if (script->hasIonScript()) {
|
||||
// It is only safe to modify script->ion if the script is not currently
|
||||
// running, because IonFrameIterator needs to tell what ionScript to
|
||||
// running, because JitFrameIterator needs to tell what ionScript to
|
||||
// use (either the one on the JSScript, or the one hidden in the
|
||||
// breadcrumbs Invalidation() leaves). Therefore, if invalidation
|
||||
// fails, we cannot disable the script.
|
||||
|
|
|
@ -11,11 +11,11 @@
|
|||
|
||||
#include "jit/IonFrames.h"
|
||||
|
||||
#include "jit/IonFrameIterator.h"
|
||||
#include "jit/JitFrameIterator.h"
|
||||
#include "jit/LIR.h"
|
||||
#include "vm/ForkJoin.h"
|
||||
|
||||
#include "jit/IonFrameIterator-inl.h"
|
||||
#include "jit/JitFrameIterator-inl.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
@ -31,28 +31,28 @@ SafepointIndex::resolve()
|
|||
}
|
||||
|
||||
inline uint8_t *
|
||||
IonFrameIterator::returnAddress() const
|
||||
JitFrameIterator::returnAddress() const
|
||||
{
|
||||
IonCommonFrameLayout *current = (IonCommonFrameLayout *) current_;
|
||||
return current->returnAddress();
|
||||
}
|
||||
|
||||
inline size_t
|
||||
IonFrameIterator::prevFrameLocalSize() const
|
||||
JitFrameIterator::prevFrameLocalSize() const
|
||||
{
|
||||
IonCommonFrameLayout *current = (IonCommonFrameLayout *) current_;
|
||||
return current->prevFrameLocalSize();
|
||||
}
|
||||
|
||||
inline FrameType
|
||||
IonFrameIterator::prevType() const
|
||||
JitFrameIterator::prevType() const
|
||||
{
|
||||
IonCommonFrameLayout *current = (IonCommonFrameLayout *) current_;
|
||||
return current->prevType();
|
||||
}
|
||||
|
||||
inline bool
|
||||
IonFrameIterator::isFakeExitFrame() const
|
||||
JitFrameIterator::isFakeExitFrame() const
|
||||
{
|
||||
bool res = (prevType() == JitFrame_Unwound_Rectifier ||
|
||||
prevType() == JitFrame_Unwound_IonJS ||
|
||||
|
@ -62,7 +62,7 @@ IonFrameIterator::isFakeExitFrame() const
|
|||
}
|
||||
|
||||
inline IonExitFrameLayout *
|
||||
IonFrameIterator::exitFrame() const
|
||||
JitFrameIterator::exitFrame() const
|
||||
{
|
||||
JS_ASSERT(type() == JitFrame_Exit);
|
||||
JS_ASSERT(!isFakeExitFrame());
|
||||
|
@ -72,7 +72,7 @@ IonFrameIterator::exitFrame() const
|
|||
inline BaselineFrame *
|
||||
GetTopBaselineFrame(JSContext *cx)
|
||||
{
|
||||
IonFrameIterator iter(cx);
|
||||
JitFrameIterator iter(cx);
|
||||
JS_ASSERT(iter.type() == JitFrame_Exit);
|
||||
++iter;
|
||||
if (iter.isBaselineStub())
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "vm/ForkJoin.h"
|
||||
#include "vm/Interpreter.h"
|
||||
|
||||
#include "jit/IonFrameIterator-inl.h"
|
||||
#include "jit/JitFrameIterator-inl.h"
|
||||
#include "vm/Probes-inl.h"
|
||||
|
||||
namespace js {
|
||||
|
@ -72,7 +72,7 @@ ReadFrameBooleanSlot(IonJSFrameLayout *fp, int32_t slot)
|
|||
return *(bool *)((char *)fp + OffsetOfFrameSlot(slot));
|
||||
}
|
||||
|
||||
IonFrameIterator::IonFrameIterator(JSContext *cx)
|
||||
JitFrameIterator::JitFrameIterator(JSContext *cx)
|
||||
: current_(cx->mainThread().ionTop),
|
||||
type_(JitFrame_Exit),
|
||||
returnAddressToFp_(nullptr),
|
||||
|
@ -83,7 +83,7 @@ IonFrameIterator::IonFrameIterator(JSContext *cx)
|
|||
{
|
||||
}
|
||||
|
||||
IonFrameIterator::IonFrameIterator(const ActivationIterator &activations)
|
||||
JitFrameIterator::JitFrameIterator(const ActivationIterator &activations)
|
||||
: current_(activations.jitTop()),
|
||||
type_(JitFrame_Exit),
|
||||
returnAddressToFp_(nullptr),
|
||||
|
@ -94,7 +94,7 @@ IonFrameIterator::IonFrameIterator(const ActivationIterator &activations)
|
|||
{
|
||||
}
|
||||
|
||||
IonFrameIterator::IonFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode)
|
||||
JitFrameIterator::JitFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode)
|
||||
: current_((uint8_t *)fp),
|
||||
type_(JitFrame_IonJS),
|
||||
returnAddressToFp_(fp->returnAddress()),
|
||||
|
@ -104,14 +104,14 @@ IonFrameIterator::IonFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode)
|
|||
}
|
||||
|
||||
bool
|
||||
IonFrameIterator::checkInvalidation() const
|
||||
JitFrameIterator::checkInvalidation() const
|
||||
{
|
||||
IonScript *dummy;
|
||||
return checkInvalidation(&dummy);
|
||||
}
|
||||
|
||||
bool
|
||||
IonFrameIterator::checkInvalidation(IonScript **ionScriptOut) const
|
||||
JitFrameIterator::checkInvalidation(IonScript **ionScriptOut) const
|
||||
{
|
||||
uint8_t *returnAddr = returnAddressToFp();
|
||||
JSScript *script = this->script();
|
||||
|
@ -137,13 +137,13 @@ IonFrameIterator::checkInvalidation(IonScript **ionScriptOut) const
|
|||
}
|
||||
|
||||
CalleeToken
|
||||
IonFrameIterator::calleeToken() const
|
||||
JitFrameIterator::calleeToken() const
|
||||
{
|
||||
return ((IonJSFrameLayout *) current_)->calleeToken();
|
||||
}
|
||||
|
||||
JSFunction *
|
||||
IonFrameIterator::callee() const
|
||||
JitFrameIterator::callee() const
|
||||
{
|
||||
JS_ASSERT(isScripted());
|
||||
JS_ASSERT(isFunctionFrame());
|
||||
|
@ -151,7 +151,7 @@ IonFrameIterator::callee() const
|
|||
}
|
||||
|
||||
JSFunction *
|
||||
IonFrameIterator::maybeCallee() const
|
||||
JitFrameIterator::maybeCallee() const
|
||||
{
|
||||
if (isScripted() && (isFunctionFrame()))
|
||||
return callee();
|
||||
|
@ -159,7 +159,7 @@ IonFrameIterator::maybeCallee() const
|
|||
}
|
||||
|
||||
bool
|
||||
IonFrameIterator::isNative() const
|
||||
JitFrameIterator::isNative() const
|
||||
{
|
||||
if (type_ != JitFrame_Exit || isFakeExitFrame())
|
||||
return false;
|
||||
|
@ -167,7 +167,7 @@ IonFrameIterator::isNative() const
|
|||
}
|
||||
|
||||
bool
|
||||
IonFrameIterator::isOOLNative() const
|
||||
JitFrameIterator::isOOLNative() const
|
||||
{
|
||||
if (type_ != JitFrame_Exit)
|
||||
return false;
|
||||
|
@ -175,7 +175,7 @@ IonFrameIterator::isOOLNative() const
|
|||
}
|
||||
|
||||
bool
|
||||
IonFrameIterator::isOOLPropertyOp() const
|
||||
JitFrameIterator::isOOLPropertyOp() const
|
||||
{
|
||||
if (type_ != JitFrame_Exit)
|
||||
return false;
|
||||
|
@ -183,7 +183,7 @@ IonFrameIterator::isOOLPropertyOp() const
|
|||
}
|
||||
|
||||
bool
|
||||
IonFrameIterator::isOOLProxy() const
|
||||
JitFrameIterator::isOOLProxy() const
|
||||
{
|
||||
if (type_ != JitFrame_Exit)
|
||||
return false;
|
||||
|
@ -191,7 +191,7 @@ IonFrameIterator::isOOLProxy() const
|
|||
}
|
||||
|
||||
bool
|
||||
IonFrameIterator::isDOMExit() const
|
||||
JitFrameIterator::isDOMExit() const
|
||||
{
|
||||
if (type_ != JitFrame_Exit)
|
||||
return false;
|
||||
|
@ -199,13 +199,13 @@ IonFrameIterator::isDOMExit() const
|
|||
}
|
||||
|
||||
bool
|
||||
IonFrameIterator::isFunctionFrame() const
|
||||
JitFrameIterator::isFunctionFrame() const
|
||||
{
|
||||
return CalleeTokenIsFunction(calleeToken());
|
||||
}
|
||||
|
||||
JSScript *
|
||||
IonFrameIterator::script() const
|
||||
JitFrameIterator::script() const
|
||||
{
|
||||
JS_ASSERT(isScripted());
|
||||
if (isBaselineJS())
|
||||
|
@ -216,7 +216,7 @@ IonFrameIterator::script() const
|
|||
}
|
||||
|
||||
void
|
||||
IonFrameIterator::baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes) const
|
||||
JitFrameIterator::baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes) const
|
||||
{
|
||||
JS_ASSERT(isBaselineJS());
|
||||
JSScript *script = this->script();
|
||||
|
@ -246,7 +246,7 @@ IonFrameIterator::baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes)
|
|||
}
|
||||
|
||||
Value *
|
||||
IonFrameIterator::actualArgs() const
|
||||
JitFrameIterator::actualArgs() const
|
||||
{
|
||||
return jsFrame()->argv() + 1;
|
||||
}
|
||||
|
@ -275,7 +275,7 @@ SizeOfFramePrefix(FrameType type)
|
|||
}
|
||||
|
||||
uint8_t *
|
||||
IonFrameIterator::prevFp() const
|
||||
JitFrameIterator::prevFp() const
|
||||
{
|
||||
size_t currentSize = SizeOfFramePrefix(type_);
|
||||
// This quick fix must be removed as soon as bug 717297 land. This is
|
||||
|
@ -290,8 +290,8 @@ IonFrameIterator::prevFp() const
|
|||
return current_ + currentSize;
|
||||
}
|
||||
|
||||
IonFrameIterator &
|
||||
IonFrameIterator::operator++()
|
||||
JitFrameIterator &
|
||||
JitFrameIterator::operator++()
|
||||
{
|
||||
JS_ASSERT(type_ != JitFrame_Entry);
|
||||
|
||||
|
@ -319,7 +319,7 @@ IonFrameIterator::operator++()
|
|||
}
|
||||
|
||||
uintptr_t *
|
||||
IonFrameIterator::spillBase() const
|
||||
JitFrameIterator::spillBase() const
|
||||
{
|
||||
// Get the base address to where safepoint registers are spilled.
|
||||
// Out-of-line calls do not unwind the extra padding space used to
|
||||
|
@ -329,7 +329,7 @@ IonFrameIterator::spillBase() const
|
|||
}
|
||||
|
||||
MachineState
|
||||
IonFrameIterator::machineState() const
|
||||
JitFrameIterator::machineState() const
|
||||
{
|
||||
SafepointReader reader(ionScript(), safepoint());
|
||||
uintptr_t *spill = spillBase();
|
||||
|
@ -446,7 +446,7 @@ HandleExceptionIon(JSContext *cx, const InlineFrameIterator &frame, ResumeFromEx
|
|||
}
|
||||
|
||||
static void
|
||||
HandleExceptionBaseline(JSContext *cx, const IonFrameIterator &frame, ResumeFromException *rfe,
|
||||
HandleExceptionBaseline(JSContext *cx, const JitFrameIterator &frame, ResumeFromException *rfe,
|
||||
bool *calledDebugEpilogue)
|
||||
{
|
||||
JS_ASSERT(frame.isBaselineJS());
|
||||
|
@ -583,7 +583,7 @@ HandleException(ResumeFromException *rfe)
|
|||
if (cx->runtime()->hasIonReturnOverride())
|
||||
cx->runtime()->takeIonReturnOverride();
|
||||
|
||||
IonFrameIterator iter(cx);
|
||||
JitFrameIterator iter(cx);
|
||||
while (!iter.isEntry()) {
|
||||
bool overrecursed = false;
|
||||
if (iter.isIonJS()) {
|
||||
|
@ -693,7 +693,7 @@ void
|
|||
HandleParallelFailure(ResumeFromException *rfe)
|
||||
{
|
||||
ForkJoinContext *cx = ForkJoinContext::current();
|
||||
IonFrameIterator iter(cx->perThreadData->ionTop, ParallelExecution);
|
||||
JitFrameIterator iter(cx->perThreadData->ionTop, ParallelExecution);
|
||||
|
||||
parallel::Spew(parallel::SpewBailouts, "Bailing from VM reentry");
|
||||
|
||||
|
@ -774,7 +774,7 @@ MarkCalleeToken(JSTracer *trc, CalleeToken token)
|
|||
|
||||
#ifdef JS_NUNBOX32
|
||||
static inline uintptr_t
|
||||
ReadAllocation(const IonFrameIterator &frame, const LAllocation *a)
|
||||
ReadAllocation(const JitFrameIterator &frame, const LAllocation *a)
|
||||
{
|
||||
if (a->isGeneralReg()) {
|
||||
Register reg = a->toGeneralReg()->reg();
|
||||
|
@ -791,7 +791,7 @@ ReadAllocation(const IonFrameIterator &frame, const LAllocation *a)
|
|||
#endif
|
||||
|
||||
static void
|
||||
MarkActualArguments(JSTracer *trc, const IonFrameIterator &frame)
|
||||
MarkActualArguments(JSTracer *trc, const JitFrameIterator &frame)
|
||||
{
|
||||
IonJSFrameLayout *layout = frame.jsFrame();
|
||||
JS_ASSERT(CalleeTokenIsFunction(layout->calleeToken()));
|
||||
|
@ -806,7 +806,7 @@ MarkActualArguments(JSTracer *trc, const IonFrameIterator &frame)
|
|||
|
||||
#ifdef JS_NUNBOX32
|
||||
static inline void
|
||||
WriteAllocation(const IonFrameIterator &frame, const LAllocation *a, uintptr_t value)
|
||||
WriteAllocation(const JitFrameIterator &frame, const LAllocation *a, uintptr_t value)
|
||||
{
|
||||
if (a->isGeneralReg()) {
|
||||
Register reg = a->toGeneralReg()->reg();
|
||||
|
@ -825,7 +825,7 @@ WriteAllocation(const IonFrameIterator &frame, const LAllocation *a, uintptr_t v
|
|||
#endif
|
||||
|
||||
static void
|
||||
MarkIonJSFrame(JSTracer *trc, const IonFrameIterator &frame)
|
||||
MarkIonJSFrame(JSTracer *trc, const JitFrameIterator &frame)
|
||||
{
|
||||
IonJSFrameLayout *layout = (IonJSFrameLayout *)frame.fp();
|
||||
|
||||
|
@ -895,7 +895,7 @@ MarkIonJSFrame(JSTracer *trc, const IonFrameIterator &frame)
|
|||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
static void
|
||||
UpdateIonJSFrameForMinorGC(JSTracer *trc, const IonFrameIterator &frame)
|
||||
UpdateIonJSFrameForMinorGC(JSTracer *trc, const JitFrameIterator &frame)
|
||||
{
|
||||
// Minor GCs may move slots/elements allocated in the nursery. Update
|
||||
// any slots/elements pointers stored in this frame.
|
||||
|
@ -941,7 +941,7 @@ UpdateIonJSFrameForMinorGC(JSTracer *trc, const IonFrameIterator &frame)
|
|||
#endif
|
||||
|
||||
static void
|
||||
MarkBaselineStubFrame(JSTracer *trc, const IonFrameIterator &frame)
|
||||
MarkBaselineStubFrame(JSTracer *trc, const JitFrameIterator &frame)
|
||||
{
|
||||
// Mark the ICStub pointer stored in the stub frame. This is necessary
|
||||
// so that we don't destroy the stub code after unlinking the stub.
|
||||
|
@ -958,7 +958,7 @@ MarkBaselineStubFrame(JSTracer *trc, const IonFrameIterator &frame)
|
|||
void
|
||||
JitActivationIterator::jitStackRange(uintptr_t *&min, uintptr_t *&end)
|
||||
{
|
||||
IonFrameIterator frames(jitTop(), SequentialExecution);
|
||||
JitFrameIterator frames(jitTop(), SequentialExecution);
|
||||
|
||||
if (frames.isFakeExitFrame()) {
|
||||
min = reinterpret_cast<uintptr_t *>(frames.fp());
|
||||
|
@ -994,7 +994,7 @@ JitActivationIterator::jitStackRange(uintptr_t *&min, uintptr_t *&end)
|
|||
}
|
||||
|
||||
static void
|
||||
MarkJitExitFrame(JSTracer *trc, const IonFrameIterator &frame)
|
||||
MarkJitExitFrame(JSTracer *trc, const JitFrameIterator &frame)
|
||||
{
|
||||
// Ignore fake exit frames created by EnsureExitFrame.
|
||||
if (frame.isFakeExitFrame())
|
||||
|
@ -1134,7 +1134,7 @@ MarkJitExitFrame(JSTracer *trc, const IonFrameIterator &frame)
|
|||
}
|
||||
|
||||
static void
|
||||
MarkRectifierFrame(JSTracer *trc, const IonFrameIterator &frame)
|
||||
MarkRectifierFrame(JSTracer *trc, const JitFrameIterator &frame)
|
||||
{
|
||||
// Mark thisv.
|
||||
//
|
||||
|
@ -1157,7 +1157,7 @@ MarkJitActivation(JSTracer *trc, const JitActivationIterator &activations)
|
|||
}
|
||||
#endif
|
||||
|
||||
for (IonFrameIterator frames(activations); !frames.done(); ++frames) {
|
||||
for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
|
||||
switch (frames.type()) {
|
||||
case JitFrame_Exit:
|
||||
MarkJitExitFrame(trc, frames);
|
||||
|
@ -1197,7 +1197,7 @@ UpdateJitActivationsForMinorGC(JSRuntime *rt, JSTracer *trc)
|
|||
{
|
||||
JS_ASSERT(trc->runtime()->isHeapMinorCollecting());
|
||||
for (JitActivationIterator activations(rt); !activations.done(); ++activations) {
|
||||
for (IonFrameIterator frames(activations); !frames.done(); ++frames) {
|
||||
for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
|
||||
if (frames.type() == JitFrame_IonJS)
|
||||
UpdateIonJSFrameForMinorGC(trc, frames);
|
||||
}
|
||||
|
@ -1220,7 +1220,7 @@ GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes)
|
|||
JSRuntime *rt = cx->runtime();
|
||||
|
||||
// Recover the return address.
|
||||
IonFrameIterator it(rt->mainThread.ionTop, SequentialExecution);
|
||||
JitFrameIterator it(rt->mainThread.ionTop, SequentialExecution);
|
||||
|
||||
// If the previous frame is a rectifier frame (maybe unwound),
|
||||
// skip past it.
|
||||
|
@ -1306,7 +1306,7 @@ SnapshotIterator::SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshot
|
|||
JS_ASSERT(snapshotOffset < ionScript->snapshotsListSize());
|
||||
}
|
||||
|
||||
SnapshotIterator::SnapshotIterator(const IonFrameIterator &iter)
|
||||
SnapshotIterator::SnapshotIterator(const JitFrameIterator &iter)
|
||||
: snapshot_(iter.ionScript()->snapshots(),
|
||||
iter.osiIndex()->snapshotOffset(),
|
||||
iter.ionScript()->snapshotsRVATableSize(),
|
||||
|
@ -1536,7 +1536,7 @@ SnapshotIterator::nextFrame()
|
|||
}
|
||||
|
||||
IonScript *
|
||||
IonFrameIterator::ionScript() const
|
||||
JitFrameIterator::ionScript() const
|
||||
{
|
||||
JS_ASSERT(type() == JitFrame_IonJS);
|
||||
|
||||
|
@ -1553,7 +1553,7 @@ IonFrameIterator::ionScript() const
|
|||
}
|
||||
|
||||
const SafepointIndex *
|
||||
IonFrameIterator::safepoint() const
|
||||
JitFrameIterator::safepoint() const
|
||||
{
|
||||
if (!cachedSafepointIndex_)
|
||||
cachedSafepointIndex_ = ionScript()->getSafepointIndex(returnAddressToFp());
|
||||
|
@ -1561,7 +1561,7 @@ IonFrameIterator::safepoint() const
|
|||
}
|
||||
|
||||
const OsiIndex *
|
||||
IonFrameIterator::osiIndex() const
|
||||
JitFrameIterator::osiIndex() const
|
||||
{
|
||||
SafepointReader reader(ionScript(), safepoint());
|
||||
return ionScript()->getOsiIndex(reader.osiReturnPointOffset());
|
||||
|
@ -1569,7 +1569,7 @@ IonFrameIterator::osiIndex() const
|
|||
|
||||
template <AllowGC allowGC>
|
||||
void
|
||||
InlineFrameIteratorMaybeGC<allowGC>::resetOn(const IonFrameIterator *iter)
|
||||
InlineFrameIteratorMaybeGC<allowGC>::resetOn(const JitFrameIterator *iter)
|
||||
{
|
||||
frame_ = iter;
|
||||
framesRead_ = 0;
|
||||
|
@ -1580,8 +1580,8 @@ InlineFrameIteratorMaybeGC<allowGC>::resetOn(const IonFrameIterator *iter)
|
|||
findNextFrame();
|
||||
}
|
||||
}
|
||||
template void InlineFrameIteratorMaybeGC<NoGC>::resetOn(const IonFrameIterator *iter);
|
||||
template void InlineFrameIteratorMaybeGC<CanGC>::resetOn(const IonFrameIterator *iter);
|
||||
template void InlineFrameIteratorMaybeGC<NoGC>::resetOn(const JitFrameIterator *iter);
|
||||
template void InlineFrameIteratorMaybeGC<CanGC>::resetOn(const JitFrameIterator *iter);
|
||||
|
||||
template <AllowGC allowGC>
|
||||
void
|
||||
|
@ -1719,9 +1719,9 @@ template bool InlineFrameIteratorMaybeGC<NoGC>::isConstructing() const;
|
|||
template bool InlineFrameIteratorMaybeGC<CanGC>::isConstructing() const;
|
||||
|
||||
bool
|
||||
IonFrameIterator::isConstructing() const
|
||||
JitFrameIterator::isConstructing() const
|
||||
{
|
||||
IonFrameIterator parent(*this);
|
||||
JitFrameIterator parent(*this);
|
||||
|
||||
// Skip the current frame and look at the caller's.
|
||||
do {
|
||||
|
@ -1760,7 +1760,7 @@ IonFrameIterator::isConstructing() const
|
|||
}
|
||||
|
||||
unsigned
|
||||
IonFrameIterator::numActualArgs() const
|
||||
JitFrameIterator::numActualArgs() const
|
||||
{
|
||||
if (isScripted())
|
||||
return jsFrame()->numActualArgs();
|
||||
|
@ -1791,7 +1791,7 @@ struct DumpOp {
|
|||
};
|
||||
|
||||
void
|
||||
IonFrameIterator::dumpBaseline() const
|
||||
JitFrameIterator::dumpBaseline() const
|
||||
{
|
||||
JS_ASSERT(isBaselineJS());
|
||||
|
||||
|
@ -1898,7 +1898,7 @@ template void InlineFrameIteratorMaybeGC<NoGC>::dump() const;
|
|||
template void InlineFrameIteratorMaybeGC<CanGC>::dump() const;
|
||||
|
||||
void
|
||||
IonFrameIterator::dump() const
|
||||
JitFrameIterator::dump() const
|
||||
{
|
||||
switch (type_) {
|
||||
case JitFrame_Entry:
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "jscntxt.h"
|
||||
#include "jsfun.h"
|
||||
|
||||
#include "jit/IonFrameIterator.h"
|
||||
#include "jit/JitFrameIterator.h"
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
@ -282,7 +282,7 @@ MakeFrameDescriptor(uint32_t frameSize, FrameType type)
|
|||
inline JSScript *
|
||||
GetTopIonJSScript(uint8_t *ionTop, void **returnAddrOut, ExecutionMode mode)
|
||||
{
|
||||
IonFrameIterator iter(ionTop, mode);
|
||||
JitFrameIterator iter(ionTop, mode);
|
||||
JS_ASSERT(iter.type() == JitFrame_Exit);
|
||||
++iter;
|
||||
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
* 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 jit_IonFrameIterator_inl_h
|
||||
#define jit_IonFrameIterator_inl_h
|
||||
#ifndef jit_JitFrameIterator_inl_h
|
||||
#define jit_JitFrameIterator_inl_h
|
||||
|
||||
#ifdef JS_ION
|
||||
|
||||
#include "jit/IonFrameIterator.h"
|
||||
#include "jit/JitFrameIterator.h"
|
||||
|
||||
#include "jit/Bailouts.h"
|
||||
#include "jit/BaselineFrame.h"
|
||||
|
@ -34,7 +34,7 @@ InlineFrameIteratorMaybeGC<allowGC>::InlineFrameIteratorMaybeGC(
|
|||
}
|
||||
|
||||
inline BaselineFrame *
|
||||
IonFrameIterator::baselineFrame() const
|
||||
JitFrameIterator::baselineFrame() const
|
||||
{
|
||||
JS_ASSERT(isBaselineJS());
|
||||
return (BaselineFrame *)(fp() - BaselineFrame::FramePointerOffset - BaselineFrame::Size());
|
||||
|
@ -45,4 +45,4 @@ IonFrameIterator::baselineFrame() const
|
|||
|
||||
#endif // JS_ION
|
||||
|
||||
#endif /* jit_IonFrameIterator_inl_h */
|
||||
#endif /* jit_JitFrameIterator_inl_h */
|
|
@ -4,8 +4,8 @@
|
|||
* 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 jit_IonFrameIterator_h
|
||||
#define jit_IonFrameIterator_h
|
||||
#ifndef jit_JitFrameIterator_h
|
||||
#define jit_JitFrameIterator_h
|
||||
|
||||
#ifdef JS_ION
|
||||
|
||||
|
@ -81,7 +81,7 @@ class BaselineFrame;
|
|||
|
||||
class JitActivation;
|
||||
|
||||
class IonFrameIterator
|
||||
class JitFrameIterator
|
||||
{
|
||||
protected:
|
||||
uint8_t *current_;
|
||||
|
@ -97,7 +97,7 @@ class IonFrameIterator
|
|||
void dumpBaseline() const;
|
||||
|
||||
public:
|
||||
explicit IonFrameIterator(uint8_t *top, ExecutionMode mode)
|
||||
explicit JitFrameIterator(uint8_t *top, ExecutionMode mode)
|
||||
: current_(top),
|
||||
type_(JitFrame_Exit),
|
||||
returnAddressToFp_(nullptr),
|
||||
|
@ -107,9 +107,9 @@ class IonFrameIterator
|
|||
mode_(mode)
|
||||
{ }
|
||||
|
||||
explicit IonFrameIterator(JSContext *cx);
|
||||
explicit IonFrameIterator(const ActivationIterator &activations);
|
||||
explicit IonFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode);
|
||||
explicit JitFrameIterator(JSContext *cx);
|
||||
explicit JitFrameIterator(const ActivationIterator &activations);
|
||||
explicit JitFrameIterator(IonJSFrameLayout *fp, ExecutionMode mode);
|
||||
|
||||
// Current frame information.
|
||||
FrameType type() const {
|
||||
|
@ -195,7 +195,7 @@ class IonFrameIterator
|
|||
inline bool done() const {
|
||||
return type_ == JitFrame_Entry;
|
||||
}
|
||||
IonFrameIterator &operator++();
|
||||
JitFrameIterator &operator++();
|
||||
|
||||
// Returns the IonScript associated with this JS frame.
|
||||
IonScript *ionScript() const;
|
||||
|
@ -351,7 +351,7 @@ class SnapshotIterator
|
|||
|
||||
SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshotOffset,
|
||||
IonJSFrameLayout *fp, const MachineState &machine);
|
||||
SnapshotIterator(const IonFrameIterator &iter);
|
||||
SnapshotIterator(const JitFrameIterator &iter);
|
||||
SnapshotIterator(const IonBailoutIterator &iter);
|
||||
SnapshotIterator();
|
||||
|
||||
|
@ -423,7 +423,7 @@ class SnapshotIterator
|
|||
template <AllowGC allowGC=CanGC>
|
||||
class InlineFrameIteratorMaybeGC
|
||||
{
|
||||
const IonFrameIterator *frame_;
|
||||
const JitFrameIterator *frame_;
|
||||
SnapshotIterator start_;
|
||||
SnapshotIterator si_;
|
||||
uint32_t framesRead_;
|
||||
|
@ -447,14 +447,14 @@ class InlineFrameIteratorMaybeGC
|
|||
void findNextFrame();
|
||||
|
||||
public:
|
||||
InlineFrameIteratorMaybeGC(JSContext *cx, const IonFrameIterator *iter)
|
||||
InlineFrameIteratorMaybeGC(JSContext *cx, const JitFrameIterator *iter)
|
||||
: callee_(cx),
|
||||
script_(cx)
|
||||
{
|
||||
resetOn(iter);
|
||||
}
|
||||
|
||||
InlineFrameIteratorMaybeGC(JSRuntime *rt, const IonFrameIterator *iter)
|
||||
InlineFrameIteratorMaybeGC(JSRuntime *rt, const JitFrameIterator *iter)
|
||||
: callee_(rt),
|
||||
script_(rt)
|
||||
{
|
||||
|
@ -614,9 +614,9 @@ class InlineFrameIteratorMaybeGC
|
|||
|
||||
void dump() const;
|
||||
|
||||
void resetOn(const IonFrameIterator *iter);
|
||||
void resetOn(const JitFrameIterator *iter);
|
||||
|
||||
const IonFrameIterator &frame() const {
|
||||
const JitFrameIterator &frame() const {
|
||||
return *frame_;
|
||||
}
|
||||
|
||||
|
@ -638,4 +638,4 @@ typedef InlineFrameIteratorMaybeGC<NoGC> InlineFrameIteratorNoGC;
|
|||
|
||||
#endif // JS_ION
|
||||
|
||||
#endif /* jit_IonFrameIterator_h */
|
||||
#endif /* jit_JitFrameIterator_h */
|
|
@ -2331,15 +2331,16 @@ LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier *ins)
|
|||
#ifdef JSGC_GENERATIONAL
|
||||
switch (ins->value()->type()) {
|
||||
case MIRType_Object: {
|
||||
LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp();
|
||||
LPostWriteBarrierO *lir =
|
||||
new(alloc()) LPostWriteBarrierO(useRegisterOrConstant(ins->object()),
|
||||
useRegister(ins->value()),
|
||||
temp());
|
||||
useRegister(ins->value()), tmp);
|
||||
return add(lir, ins) && assignSafepoint(lir, ins);
|
||||
}
|
||||
case MIRType_Value: {
|
||||
LDefinition tmp = needTempForPostBarrier() ? temp() : LDefinition::BogusTemp();
|
||||
LPostWriteBarrierV *lir =
|
||||
new(alloc()) LPostWriteBarrierV(useRegisterOrConstant(ins->object()), temp());
|
||||
new(alloc()) LPostWriteBarrierV(useRegisterOrConstant(ins->object()), tmp);
|
||||
if (!useBox(lir, LPostWriteBarrierV::Input, ins->value()))
|
||||
return false;
|
||||
return add(lir, ins) && assignSafepoint(lir, ins);
|
||||
|
|
|
@ -1018,7 +1018,7 @@ Recompile(JSContext *cx)
|
|||
{
|
||||
JS_ASSERT(cx->currentlyRunningInJit());
|
||||
JitActivationIterator activations(cx->runtime());
|
||||
IonFrameIterator iter(activations);
|
||||
JitFrameIterator iter(activations);
|
||||
|
||||
JS_ASSERT(iter.type() == JitFrame_Exit);
|
||||
++iter;
|
||||
|
|
|
@ -70,7 +70,7 @@ static_assert((sizeof(BailoutStack) % 8) == 0, "BailoutStack should be 8-byte al
|
|||
|
||||
IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
|
||||
BailoutStack *bailout)
|
||||
: IonFrameIterator(activations),
|
||||
: JitFrameIterator(activations),
|
||||
machine_(bailout->machine())
|
||||
{
|
||||
uint8_t *sp = bailout->parentStackPointer();
|
||||
|
@ -105,7 +105,7 @@ IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
|
|||
|
||||
IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
|
||||
InvalidationBailoutStack *bailout)
|
||||
: IonFrameIterator(activations),
|
||||
: JitFrameIterator(activations),
|
||||
machine_(bailout->machine())
|
||||
{
|
||||
returnAddressToFp_ = bailout->osiPointReturnAddress();
|
||||
|
|
|
@ -35,6 +35,8 @@ class LIRGeneratorARM : public LIRGeneratorShared
|
|||
return LDefinition::BogusTemp();
|
||||
}
|
||||
|
||||
bool needTempForPostBarrier() { return false; }
|
||||
|
||||
// x64 has a scratch register, so no need for another temp for dispatch
|
||||
// ICs.
|
||||
LDefinition tempForDispatchCache(MIRType outputType = MIRType_None) {
|
||||
|
|
|
@ -4334,3 +4334,29 @@ MacroAssemblerARMCompat::jumpWithPatch(RepatchLabel *label, Condition cond)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::branchPtrInNurseryRange(Register ptr, Register temp, Label *label)
|
||||
{
|
||||
JS_ASSERT(ptr != temp);
|
||||
JS_ASSERT(ptr != ScratchRegister);
|
||||
|
||||
const Nursery &nursery = GetIonContext()->runtime->gcNursery();
|
||||
movePtr(ImmWord(-ptrdiff_t(nursery.start())), ScratchRegister);
|
||||
addPtr(ptr, ScratchRegister);
|
||||
branchPtr(Assembler::Below, ScratchRegister, Imm32(Nursery::NurserySize), label);
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerARMCompat::branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label)
|
||||
{
|
||||
Label done;
|
||||
|
||||
branchTestObject(Assembler::NotEqual, value, &done);
|
||||
branchPtrInNurseryRange(value.payloadReg(), temp, label);
|
||||
|
||||
bind(&done);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1559,6 +1559,11 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||
void moveFloat32(FloatRegister src, FloatRegister dest) {
|
||||
as_vmov(VFPRegister(src).singleOverlay(), VFPRegister(dest).singleOverlay());
|
||||
}
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
void branchPtrInNurseryRange(Register ptr, Register temp, Label *label);
|
||||
void branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label);
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef MacroAssemblerARMCompat MacroAssemblerSpecific;
|
||||
|
|
|
@ -3245,3 +3245,21 @@ MacroAssemblerMIPSCompat::toggledCall(JitCode *target, bool enabled)
|
|||
MOZ_ASSERT(nextOffset().getOffset() - offset.offset() == ToggledCallSize());
|
||||
return offset;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerMIPSCompat::branchPtrInNurseryRange(Register ptr, Register temp, Label *label)
|
||||
{
|
||||
JS_ASSERT(temp != InvalidReg);
|
||||
const Nursery &nursery = GetIonContext()->runtime->gcNursery();
|
||||
|
||||
// ptr and temp may be the same register, in which case we mustn't trash it
|
||||
// before we use its contents.
|
||||
if (ptr == temp) {
|
||||
addPtr(ImmWord(-ptrdiff_t(nursery.start())), ptr);
|
||||
branchPtr(Assembler::Below, ptr, Imm32(Nursery::NurserySize), label);
|
||||
} else {
|
||||
movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
|
||||
addPtr(ptr, temp);
|
||||
branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), label);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1142,6 +1142,8 @@ public:
|
|||
void moveFloat32(FloatRegister src, FloatRegister dest) {
|
||||
as_movs(dest, src);
|
||||
}
|
||||
|
||||
void branchPtrInNurseryRange(Register ptr, Register temp, Label *label);
|
||||
};
|
||||
|
||||
typedef MacroAssemblerMIPSCompat MacroAssemblerSpecific;
|
||||
|
|
|
@ -1558,10 +1558,12 @@ CodeGeneratorX86Shared::visitFloor(LFloor *lir)
|
|||
FloatRegister scratch = ScratchFloatReg;
|
||||
Register output = ToRegister(lir->output());
|
||||
|
||||
Label bailout;
|
||||
|
||||
if (AssemblerX86Shared::HasSSE41()) {
|
||||
// Bail on negative-zero.
|
||||
Assembler::Condition bailCond = masm.testNegativeZero(input, output);
|
||||
if (!bailoutIf(bailCond, lir->snapshot()))
|
||||
masm.branchNegativeZero(input, output, &bailout);
|
||||
if (!bailoutFrom(&bailout, lir->snapshot()))
|
||||
return false;
|
||||
|
||||
// Round toward -Infinity.
|
||||
|
@ -1579,8 +1581,8 @@ CodeGeneratorX86Shared::visitFloor(LFloor *lir)
|
|||
masm.branchDouble(Assembler::DoubleLessThan, input, scratch, &negative);
|
||||
|
||||
// Bail on negative-zero.
|
||||
Assembler::Condition bailCond = masm.testNegativeZero(input, output);
|
||||
if (!bailoutIf(bailCond, lir->snapshot()))
|
||||
masm.branchNegativeZero(input, output, &bailout);
|
||||
if (!bailoutFrom(&bailout, lir->snapshot()))
|
||||
return false;
|
||||
|
||||
// Input is non-negative, so truncation correctly rounds.
|
||||
|
@ -1625,10 +1627,12 @@ CodeGeneratorX86Shared::visitFloorF(LFloorF *lir)
|
|||
FloatRegister scratch = ScratchFloatReg;
|
||||
Register output = ToRegister(lir->output());
|
||||
|
||||
Label bailout;
|
||||
|
||||
if (AssemblerX86Shared::HasSSE41()) {
|
||||
// Bail on negative-zero.
|
||||
Assembler::Condition bailCond = masm.testNegativeZeroFloat32(input, output);
|
||||
if (!bailoutIf(bailCond, lir->snapshot()))
|
||||
masm.branchNegativeZeroFloat32(input, output, &bailout);
|
||||
if (!bailoutFrom(&bailout, lir->snapshot()))
|
||||
return false;
|
||||
|
||||
// Round toward -Infinity.
|
||||
|
@ -1646,8 +1650,8 @@ CodeGeneratorX86Shared::visitFloorF(LFloorF *lir)
|
|||
masm.branchFloat(Assembler::DoubleLessThan, input, scratch, &negative);
|
||||
|
||||
// Bail on negative-zero.
|
||||
Assembler::Condition bailCond = masm.testNegativeZeroFloat32(input, output);
|
||||
if (!bailoutIf(bailCond, lir->snapshot()))
|
||||
masm.branchNegativeZeroFloat32(input, output, &bailout);
|
||||
if (!bailoutFrom(&bailout, lir->snapshot()))
|
||||
return false;
|
||||
|
||||
// Input is non-negative, so truncation correctly rounds.
|
||||
|
@ -1693,7 +1697,7 @@ CodeGeneratorX86Shared::visitRound(LRound *lir)
|
|||
FloatRegister scratch = ScratchFloatReg;
|
||||
Register output = ToRegister(lir->output());
|
||||
|
||||
Label negative, end;
|
||||
Label negative, end, bailout;
|
||||
|
||||
// Load 0.5 in the temp register.
|
||||
masm.loadConstantDouble(0.5, temp);
|
||||
|
@ -1703,8 +1707,8 @@ CodeGeneratorX86Shared::visitRound(LRound *lir)
|
|||
masm.branchDouble(Assembler::DoubleLessThan, input, scratch, &negative);
|
||||
|
||||
// Bail on negative-zero.
|
||||
Assembler::Condition bailCond = masm.testNegativeZero(input, output);
|
||||
if (!bailoutIf(bailCond, lir->snapshot()))
|
||||
masm.branchNegativeZero(input, output, &bailout);
|
||||
if (!bailoutFrom(&bailout, lir->snapshot()))
|
||||
return false;
|
||||
|
||||
// Input is non-negative. Add 0.5 and truncate, rounding down. Note that we
|
||||
|
@ -1781,7 +1785,7 @@ CodeGeneratorX86Shared::visitRoundF(LRoundF *lir)
|
|||
FloatRegister scratch = ScratchFloatReg;
|
||||
Register output = ToRegister(lir->output());
|
||||
|
||||
Label negative, end;
|
||||
Label negative, end, bailout;
|
||||
|
||||
// Load 0.5 in the temp register.
|
||||
masm.loadConstantFloat32(0.5f, temp);
|
||||
|
@ -1791,8 +1795,8 @@ CodeGeneratorX86Shared::visitRoundF(LRoundF *lir)
|
|||
masm.branchFloat(Assembler::DoubleLessThan, input, scratch, &negative);
|
||||
|
||||
// Bail on negative-zero.
|
||||
Assembler::Condition bailCond = masm.testNegativeZeroFloat32(input, output);
|
||||
if (!bailoutIf(bailCond, lir->snapshot()))
|
||||
masm.branchNegativeZeroFloat32(input, output, &bailout);
|
||||
if (!bailoutFrom(&bailout, lir->snapshot()))
|
||||
return false;
|
||||
|
||||
// Input is non-negative. Add 0.5 and truncate, rounding down. Note that we
|
||||
|
|
|
@ -154,3 +154,45 @@ MacroAssemblerX86Shared::buildOOLFakeExitFrame(void *fakeReturnAddr)
|
|||
Push(ImmPtr(fakeReturnAddr));
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerX86Shared::branchNegativeZero(const FloatRegister ®,
|
||||
const Register &scratch,
|
||||
Label *label)
|
||||
{
|
||||
// Determines whether the low double contained in the XMM register reg
|
||||
// is equal to -0.0.
|
||||
|
||||
#if defined(JS_CODEGEN_X86)
|
||||
Label nonZero;
|
||||
|
||||
// Compare to zero. Lets through {0, -0}.
|
||||
xorpd(ScratchFloatReg, ScratchFloatReg);
|
||||
|
||||
// If reg is non-zero, jump to nonZero.
|
||||
branchDouble(DoubleNotEqual, reg, ScratchFloatReg, &nonZero);
|
||||
|
||||
// Input register is either zero or negative zero. Retrieve sign of input.
|
||||
movmskpd(reg, scratch);
|
||||
|
||||
// If reg is 1 or 3, input is negative zero.
|
||||
// If reg is 0 or 2, input is a normal zero.
|
||||
branchTest32(NonZero, scratch, Imm32(1), label);
|
||||
|
||||
bind(&nonZero);
|
||||
#elif defined(JS_CODEGEN_X64)
|
||||
movq(reg, scratch);
|
||||
cmpq(scratch, Imm32(1));
|
||||
j(Overflow, label);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
MacroAssemblerX86Shared::branchNegativeZeroFloat32(const FloatRegister ®,
|
||||
const Register &scratch,
|
||||
Label *label)
|
||||
{
|
||||
movd(reg, scratch);
|
||||
cmpl(scratch, Imm32(1));
|
||||
j(Overflow, label);
|
||||
}
|
||||
|
|
|
@ -90,6 +90,9 @@ class MacroAssemblerX86Shared : public Assembler
|
|||
j(ConditionFromDoubleCondition(cond), label);
|
||||
}
|
||||
|
||||
void branchNegativeZero(const FloatRegister ®, const Register &scratch, Label *label);
|
||||
void branchNegativeZeroFloat32(const FloatRegister ®, const Register &scratch, Label *label);
|
||||
|
||||
void move32(const Imm32 &imm, const Register &dest) {
|
||||
// Use the ImmWord version of mov to register, which has special
|
||||
// optimizations. Casting to uint32_t here ensures that the value
|
||||
|
@ -519,31 +522,16 @@ class MacroAssemblerX86Shared : public Assembler
|
|||
void convertDoubleToInt32(FloatRegister src, Register dest, Label *fail,
|
||||
bool negativeZeroCheck = true)
|
||||
{
|
||||
// Check for -0.0
|
||||
if (negativeZeroCheck)
|
||||
branchNegativeZero(src, dest, fail);
|
||||
|
||||
cvttsd2si(src, dest);
|
||||
cvtsi2sd(dest, ScratchFloatReg);
|
||||
ucomisd(src, ScratchFloatReg);
|
||||
j(Assembler::Parity, fail);
|
||||
j(Assembler::NotEqual, fail);
|
||||
|
||||
// Check for -0
|
||||
if (negativeZeroCheck) {
|
||||
Label notZero;
|
||||
testl(dest, dest);
|
||||
j(Assembler::NonZero, ¬Zero);
|
||||
|
||||
if (Assembler::HasSSE41()) {
|
||||
ptest(src, src);
|
||||
j(Assembler::NonZero, fail);
|
||||
} else {
|
||||
// bit 0 = sign of low double
|
||||
// bit 1 = sign of high double
|
||||
movmskpd(src, dest);
|
||||
andl(Imm32(1), dest);
|
||||
j(Assembler::NonZero, fail);
|
||||
}
|
||||
|
||||
bind(¬Zero);
|
||||
}
|
||||
}
|
||||
|
||||
// Checks whether a float32 is representable as a 32-bit integer. If so, the
|
||||
|
@ -552,30 +540,15 @@ class MacroAssemblerX86Shared : public Assembler
|
|||
void convertFloat32ToInt32(FloatRegister src, Register dest, Label *fail,
|
||||
bool negativeZeroCheck = true)
|
||||
{
|
||||
// Check for -0.0
|
||||
if (negativeZeroCheck)
|
||||
branchNegativeZeroFloat32(src, dest, fail);
|
||||
|
||||
cvttss2si(src, dest);
|
||||
convertInt32ToFloat32(dest, ScratchFloatReg);
|
||||
ucomiss(src, ScratchFloatReg);
|
||||
j(Assembler::Parity, fail);
|
||||
j(Assembler::NotEqual, fail);
|
||||
|
||||
// Check for -0
|
||||
if (negativeZeroCheck) {
|
||||
Label notZero;
|
||||
branchTest32(Assembler::NonZero, dest, dest, ¬Zero);
|
||||
|
||||
if (Assembler::HasSSE41()) {
|
||||
ptest(src, src);
|
||||
j(Assembler::NonZero, fail);
|
||||
} else {
|
||||
// bit 0 = sign of low float
|
||||
// bits 1 to 3 = signs of higher floats
|
||||
movmskps(src, dest);
|
||||
andl(Imm32(1), dest);
|
||||
j(Assembler::NonZero, fail);
|
||||
}
|
||||
|
||||
bind(¬Zero);
|
||||
}
|
||||
}
|
||||
|
||||
void clampIntToUint8(Register reg) {
|
||||
|
|
|
@ -47,7 +47,7 @@ class BailoutStack
|
|||
|
||||
IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
|
||||
BailoutStack *bailout)
|
||||
: IonFrameIterator(activations),
|
||||
: JitFrameIterator(activations),
|
||||
machine_(bailout->machineState())
|
||||
{
|
||||
uint8_t *sp = bailout->parentStackPointer();
|
||||
|
@ -62,7 +62,7 @@ IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
|
|||
|
||||
IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
|
||||
InvalidationBailoutStack *bailout)
|
||||
: IonFrameIterator(activations),
|
||||
: JitFrameIterator(activations),
|
||||
machine_(bailout->machine())
|
||||
{
|
||||
returnAddressToFp_ = bailout->osiPointReturnAddress();
|
||||
|
|
|
@ -35,6 +35,8 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared
|
|||
|
||||
LDefinition tempToUnbox();
|
||||
|
||||
bool needTempForPostBarrier() { return false; }
|
||||
|
||||
// x64 has a scratch register, so no need for another temp for dispatch
|
||||
// ICs.
|
||||
LDefinition tempForDispatchCache(MIRType outputType = MIRType_None) {
|
||||
|
|
|
@ -367,18 +367,30 @@ MacroAssemblerX64::handleFailureWithHandlerTail()
|
|||
jmp(Operand(rsp, offsetof(ResumeFromException, target)));
|
||||
}
|
||||
|
||||
Assembler::Condition
|
||||
MacroAssemblerX64::testNegativeZero(const FloatRegister ®, const Register &scratch)
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
|
||||
void
|
||||
MacroAssemblerX64::branchPtrInNurseryRange(Register ptr, Register temp, Label *label)
|
||||
{
|
||||
movq(reg, scratch);
|
||||
cmpq(scratch, Imm32(1));
|
||||
return Overflow;
|
||||
JS_ASSERT(ptr != temp);
|
||||
JS_ASSERT(ptr != ScratchReg);
|
||||
|
||||
const Nursery &nursery = GetIonContext()->runtime->gcNursery();
|
||||
movePtr(ImmWord(-ptrdiff_t(nursery.start())), ScratchReg);
|
||||
addPtr(ptr, ScratchReg);
|
||||
branchPtr(Assembler::Below, ScratchReg, Imm32(Nursery::NurserySize), label);
|
||||
}
|
||||
|
||||
Assembler::Condition
|
||||
MacroAssemblerX64::testNegativeZeroFloat32(const FloatRegister ®, const Register &scratch)
|
||||
void
|
||||
MacroAssemblerX64::branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label)
|
||||
{
|
||||
movd(reg, scratch);
|
||||
cmpl(scratch, Imm32(1));
|
||||
return Overflow;
|
||||
// 'Value' representing the start of the nursery tagged as a JSObject
|
||||
const Nursery &nursery = GetIonContext()->runtime->gcNursery();
|
||||
Value start = ObjectValue(*reinterpret_cast<JSObject *>(nursery.start()));
|
||||
|
||||
movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), ScratchReg);
|
||||
addPtr(value.valueReg(), ScratchReg);
|
||||
branchPtr(Assembler::Below, ScratchReg, Imm32(Nursery::NurserySize), label);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -507,9 +507,6 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
|||
emitSet(cond, dest);
|
||||
}
|
||||
|
||||
Condition testNegativeZero(const FloatRegister ®, const Register &scratch);
|
||||
Condition testNegativeZeroFloat32(const FloatRegister ®, const Register &scratch);
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Common interface.
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
@ -1325,6 +1322,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
|||
storeValue(JSVAL_TYPE_INT32, ScratchReg, Dest);
|
||||
}
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
void branchPtrInNurseryRange(Register ptr, Register temp, Label *label);
|
||||
void branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label);
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef MacroAssemblerX64 MacroAssemblerSpecific;
|
||||
|
|
|
@ -67,7 +67,7 @@ class BailoutStack
|
|||
|
||||
IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
|
||||
BailoutStack *bailout)
|
||||
: IonFrameIterator(activations),
|
||||
: JitFrameIterator(activations),
|
||||
machine_(bailout->machine())
|
||||
{
|
||||
uint8_t *sp = bailout->parentStackPointer();
|
||||
|
@ -102,7 +102,7 @@ IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
|
|||
|
||||
IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations,
|
||||
InvalidationBailoutStack *bailout)
|
||||
: IonFrameIterator(activations),
|
||||
: JitFrameIterator(activations),
|
||||
machine_(bailout->machine())
|
||||
{
|
||||
returnAddressToFp_ = bailout->osiPointReturnAddress();
|
||||
|
|
|
@ -38,6 +38,8 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared
|
|||
return LDefinition::BogusTemp();
|
||||
}
|
||||
|
||||
bool needTempForPostBarrier() { return true; }
|
||||
|
||||
LDefinition tempForDispatchCache(MIRType outputType = MIRType_None);
|
||||
|
||||
void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex);
|
||||
|
|
|
@ -387,43 +387,29 @@ MacroAssemblerX86::branchTestValue(Condition cond, const ValueOperand &value, co
|
|||
}
|
||||
}
|
||||
|
||||
Assembler::Condition
|
||||
MacroAssemblerX86::testNegativeZero(const FloatRegister ®, const Register &scratch)
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
|
||||
void
|
||||
MacroAssemblerX86::branchPtrInNurseryRange(Register ptr, Register temp, Label *label)
|
||||
{
|
||||
// Determines whether the single double contained in the XMM register reg
|
||||
// is equal to double-precision -0.
|
||||
JS_ASSERT(ptr != temp);
|
||||
JS_ASSERT(temp != InvalidReg); // A temp register is required for x86.
|
||||
|
||||
Label nonZero;
|
||||
|
||||
// Compare to zero. Lets through {0, -0}.
|
||||
xorpd(ScratchFloatReg, ScratchFloatReg);
|
||||
|
||||
// If reg is non-zero, jump to nonZero.
|
||||
// Sets ZF=0 and PF=0.
|
||||
branchDouble(DoubleNotEqual, reg, ScratchFloatReg, &nonZero);
|
||||
|
||||
// Input register is either zero or negative zero. Retrieve sign of input.
|
||||
movmskpd(reg, scratch);
|
||||
|
||||
// If reg is 1 or 3, input is negative zero.
|
||||
// If reg is 0 or 2, input is a normal zero.
|
||||
// So the following test will set PF=1 for negative zero.
|
||||
orl(Imm32(2), scratch);
|
||||
|
||||
bind(&nonZero);
|
||||
|
||||
// Here we need to be able to test if the input is a negative zero.
|
||||
// - branchDouble joins here for non-zero values in which case it sets
|
||||
// ZF=0 and PF=0. In that case the test should fail.
|
||||
// - orl sets PF=1 on negative zero and PF=0 otherwise
|
||||
// => So testing PF=1 will return if input is negative zero or not.
|
||||
return Parity;
|
||||
const Nursery &nursery = GetIonContext()->runtime->gcNursery();
|
||||
movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp);
|
||||
addPtr(ptr, temp);
|
||||
branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), label);
|
||||
}
|
||||
|
||||
Assembler::Condition
|
||||
MacroAssemblerX86::testNegativeZeroFloat32(const FloatRegister ®, const Register &scratch)
|
||||
void
|
||||
MacroAssemblerX86::branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label)
|
||||
{
|
||||
movd(reg, scratch);
|
||||
cmpl(scratch, Imm32(1));
|
||||
return Overflow;
|
||||
Label done;
|
||||
|
||||
branchTestObject(Assembler::NotEqual, value, &done);
|
||||
branchPtrInNurseryRange(value.payloadReg(), temp, label);
|
||||
|
||||
bind(&done);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -529,9 +529,6 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
|||
emitSet(cond, dest);
|
||||
}
|
||||
|
||||
Condition testNegativeZero(const FloatRegister ®, const Register &scratch);
|
||||
Condition testNegativeZeroFloat32(const FloatRegister ®, const Register &scratch);
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Common interface.
|
||||
/////////////////////////////////////////////////////////////////
|
||||
|
@ -1114,6 +1111,11 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
|||
void linkParallelExitFrame(const Register &pt) {
|
||||
movl(StackPointer, Operand(pt, offsetof(PerThreadData, ionTop)));
|
||||
}
|
||||
|
||||
#ifdef JSGC_GENERATIONAL
|
||||
void branchPtrInNurseryRange(Register ptr, Register temp, Label *label);
|
||||
void branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label);
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef MacroAssemblerX86 MacroAssemblerSpecific;
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
#include "gc/Marking.h"
|
||||
#ifdef JS_ION
|
||||
#include "jit/Ion.h"
|
||||
#include "jit/IonFrameIterator.h"
|
||||
#include "jit/JitFrameIterator.h"
|
||||
#endif
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/Shape.h"
|
||||
|
|
|
@ -544,8 +544,15 @@ class FunctionExtended : public JSFunction
|
|||
public:
|
||||
static const unsigned NUM_EXTENDED_SLOTS = 2;
|
||||
|
||||
/* Arrow functions store their lexical |this| in the first extended slot. */
|
||||
static const unsigned ARROW_THIS_SLOT = 0;
|
||||
|
||||
static inline size_t offsetOfExtendedSlot(unsigned which) {
|
||||
MOZ_ASSERT(which < NUM_EXTENDED_SLOTS);
|
||||
return offsetof(FunctionExtended, extendedSlots) + which * sizeof(HeapValue);
|
||||
}
|
||||
static inline size_t offsetOfArrowThisSlot() {
|
||||
return offsetof(FunctionExtended, extendedSlots) + 0 * sizeof(HeapValue);
|
||||
return offsetOfExtendedSlot(ARROW_THIS_SLOT);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -338,11 +338,11 @@ FrameIter::unaliasedForEachActual(JSContext *cx, Op op)
|
|||
return;
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
if (data_.ionFrames_.isIonJS()) {
|
||||
if (data_.jitFrames_.isIonJS()) {
|
||||
ionInlineFrames_.unaliasedForEachActual(cx, op, jit::ReadFrame_Actuals);
|
||||
} else {
|
||||
JS_ASSERT(data_.ionFrames_.isBaselineJS());
|
||||
data_.ionFrames_.unaliasedForEachActual(op, jit::ReadFrame_Actuals);
|
||||
JS_ASSERT(data_.jitFrames_.isBaselineJS());
|
||||
data_.jitFrames_.unaliasedForEachActual(op, jit::ReadFrame_Actuals);
|
||||
}
|
||||
return;
|
||||
#else
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#endif
|
||||
#include "vm/Opcodes.h"
|
||||
|
||||
#include "jit/IonFrameIterator-inl.h"
|
||||
#include "jit/JitFrameIterator-inl.h"
|
||||
#include "vm/Interpreter-inl.h"
|
||||
#include "vm/Probes-inl.h"
|
||||
#include "vm/ScopeObject-inl.h"
|
||||
|
@ -568,15 +568,15 @@ FrameIter::settleOnActivation()
|
|||
|
||||
#ifdef JS_ION
|
||||
if (activation->isJit()) {
|
||||
data_.ionFrames_ = jit::IonFrameIterator(data_.activations_);
|
||||
data_.jitFrames_ = jit::JitFrameIterator(data_.activations_);
|
||||
|
||||
// Stop at the first scripted frame.
|
||||
while (!data_.ionFrames_.isScripted() && !data_.ionFrames_.done())
|
||||
++data_.ionFrames_;
|
||||
while (!data_.jitFrames_.isScripted() && !data_.jitFrames_.done())
|
||||
++data_.jitFrames_;
|
||||
|
||||
// It's possible to have an JitActivation with no scripted frames,
|
||||
// for instance if we hit an over-recursion during bailout.
|
||||
if (data_.ionFrames_.done()) {
|
||||
if (data_.jitFrames_.done()) {
|
||||
++data_.activations_;
|
||||
continue;
|
||||
}
|
||||
|
@ -632,7 +632,7 @@ FrameIter::Data::Data(JSContext *cx, SavedOption savedOption, ContextOption cont
|
|||
interpFrames_(nullptr),
|
||||
activations_(cx->runtime())
|
||||
#ifdef JS_ION
|
||||
, ionFrames_((uint8_t *)nullptr, SequentialExecution)
|
||||
, jitFrames_((uint8_t *)nullptr, SequentialExecution)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
@ -647,7 +647,7 @@ FrameIter::Data::Data(const FrameIter::Data &other)
|
|||
interpFrames_(other.interpFrames_),
|
||||
activations_(other.activations_)
|
||||
#ifdef JS_ION
|
||||
, ionFrames_(other.ionFrames_)
|
||||
, jitFrames_(other.jitFrames_)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
@ -655,7 +655,7 @@ FrameIter::Data::Data(const FrameIter::Data &other)
|
|||
FrameIter::FrameIter(JSContext *cx, SavedOption savedOption)
|
||||
: data_(cx, savedOption, CURRENT_CONTEXT, nullptr)
|
||||
#ifdef JS_ION
|
||||
, ionInlineFrames_(cx, (js::jit::IonFrameIterator*) nullptr)
|
||||
, ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
|
||||
#endif
|
||||
{
|
||||
settleOnActivation();
|
||||
|
@ -665,7 +665,7 @@ FrameIter::FrameIter(JSContext *cx, ContextOption contextOption,
|
|||
SavedOption savedOption, JSPrincipals *principals)
|
||||
: data_(cx, savedOption, contextOption, principals)
|
||||
#ifdef JS_ION
|
||||
, ionInlineFrames_(cx, (js::jit::IonFrameIterator*) nullptr)
|
||||
, ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
|
||||
#endif
|
||||
{
|
||||
settleOnActivation();
|
||||
|
@ -675,7 +675,7 @@ FrameIter::FrameIter(const FrameIter &other)
|
|||
: data_(other.data_)
|
||||
#ifdef JS_ION
|
||||
, ionInlineFrames_(other.data_.cx_,
|
||||
data_.ionFrames_.isScripted() ? &other.ionInlineFrames_ : nullptr)
|
||||
data_.jitFrames_.isScripted() ? &other.ionInlineFrames_ : nullptr)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
@ -683,7 +683,7 @@ FrameIter::FrameIter(const FrameIter &other)
|
|||
FrameIter::FrameIter(const Data &data)
|
||||
: data_(data)
|
||||
#ifdef JS_ION
|
||||
, ionInlineFrames_(data.cx_, data_.ionFrames_.isIonJS() ? &data_.ionFrames_ : nullptr)
|
||||
, ionInlineFrames_(data.cx_, data_.jitFrames_.isIonJS() ? &data_.jitFrames_ : nullptr)
|
||||
#endif
|
||||
{
|
||||
JS_ASSERT(data.cx_);
|
||||
|
@ -693,12 +693,12 @@ FrameIter::FrameIter(const Data &data)
|
|||
void
|
||||
FrameIter::nextJitFrame()
|
||||
{
|
||||
if (data_.ionFrames_.isIonJS()) {
|
||||
ionInlineFrames_.resetOn(&data_.ionFrames_);
|
||||
if (data_.jitFrames_.isIonJS()) {
|
||||
ionInlineFrames_.resetOn(&data_.jitFrames_);
|
||||
data_.pc_ = ionInlineFrames_.pc();
|
||||
} else {
|
||||
JS_ASSERT(data_.ionFrames_.isBaselineJS());
|
||||
data_.ionFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
|
||||
JS_ASSERT(data_.jitFrames_.isBaselineJS());
|
||||
data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -707,17 +707,17 @@ FrameIter::popJitFrame()
|
|||
{
|
||||
JS_ASSERT(data_.state_ == JIT);
|
||||
|
||||
if (data_.ionFrames_.isIonJS() && ionInlineFrames_.more()) {
|
||||
if (data_.jitFrames_.isIonJS() && ionInlineFrames_.more()) {
|
||||
++ionInlineFrames_;
|
||||
data_.pc_ = ionInlineFrames_.pc();
|
||||
return;
|
||||
}
|
||||
|
||||
++data_.ionFrames_;
|
||||
while (!data_.ionFrames_.done() && !data_.ionFrames_.isScripted())
|
||||
++data_.ionFrames_;
|
||||
++data_.jitFrames_;
|
||||
while (!data_.jitFrames_.done() && !data_.jitFrames_.isScripted())
|
||||
++data_.jitFrames_;
|
||||
|
||||
if (!data_.ionFrames_.done()) {
|
||||
if (!data_.jitFrames_.done()) {
|
||||
nextJitFrame();
|
||||
return;
|
||||
}
|
||||
|
@ -791,7 +791,7 @@ FrameIter::copyData() const
|
|||
* not copied.
|
||||
*/
|
||||
JS_ASSERT(data_.state_ != ASMJS);
|
||||
JS_ASSERT(data_.ionFrames_.type() != jit::JitFrame_IonJS);
|
||||
JS_ASSERT(data_.jitFrames_.type() != jit::JitFrame_IonJS);
|
||||
#endif
|
||||
return data_.cx_->new_<Data>(data_);
|
||||
}
|
||||
|
@ -829,9 +829,9 @@ FrameIter::isFunctionFrame() const
|
|||
return interpFrame()->isFunctionFrame();
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
JS_ASSERT(data_.ionFrames_.isScripted());
|
||||
if (data_.ionFrames_.isBaselineJS())
|
||||
return data_.ionFrames_.isFunctionFrame();
|
||||
JS_ASSERT(data_.jitFrames_.isScripted());
|
||||
if (data_.jitFrames_.isBaselineJS())
|
||||
return data_.jitFrames_.isFunctionFrame();
|
||||
return ionInlineFrames_.isFunctionFrame();
|
||||
#else
|
||||
break;
|
||||
|
@ -852,8 +852,8 @@ FrameIter::isGlobalFrame() const
|
|||
return interpFrame()->isGlobalFrame();
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
if (data_.ionFrames_.isBaselineJS())
|
||||
return data_.ionFrames_.baselineFrame()->isGlobalFrame();
|
||||
if (data_.jitFrames_.isBaselineJS())
|
||||
return data_.jitFrames_.baselineFrame()->isGlobalFrame();
|
||||
JS_ASSERT(!script()->isForEval());
|
||||
return !script()->functionNonDelazifying();
|
||||
#else
|
||||
|
@ -875,8 +875,8 @@ FrameIter::isEvalFrame() const
|
|||
return interpFrame()->isEvalFrame();
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
if (data_.ionFrames_.isBaselineJS())
|
||||
return data_.ionFrames_.baselineFrame()->isEvalFrame();
|
||||
if (data_.jitFrames_.isBaselineJS())
|
||||
return data_.jitFrames_.baselineFrame()->isEvalFrame();
|
||||
JS_ASSERT(!script()->isForEval());
|
||||
return false;
|
||||
#else
|
||||
|
@ -1040,10 +1040,10 @@ FrameIter::isConstructing() const
|
|||
break;
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
if (data_.ionFrames_.isIonJS())
|
||||
if (data_.jitFrames_.isIonJS())
|
||||
return ionInlineFrames_.isConstructing();
|
||||
JS_ASSERT(data_.ionFrames_.isBaselineJS());
|
||||
return data_.ionFrames_.isConstructing();
|
||||
JS_ASSERT(data_.jitFrames_.isBaselineJS());
|
||||
return data_.jitFrames_.isConstructing();
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
|
@ -1062,8 +1062,8 @@ FrameIter::abstractFramePtr() const
|
|||
break;
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
if (data_.ionFrames_.isBaselineJS())
|
||||
return data_.ionFrames_.baselineFrame();
|
||||
if (data_.jitFrames_.isBaselineJS())
|
||||
return data_.jitFrames_.baselineFrame();
|
||||
#endif
|
||||
break;
|
||||
case INTERP:
|
||||
|
@ -1096,8 +1096,8 @@ FrameIter::updatePcQuadratic()
|
|||
}
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
if (data_.ionFrames_.isBaselineJS()) {
|
||||
jit::BaselineFrame *frame = data_.ionFrames_.baselineFrame();
|
||||
if (data_.jitFrames_.isBaselineJS()) {
|
||||
jit::BaselineFrame *frame = data_.jitFrames_.baselineFrame();
|
||||
jit::JitActivation *activation = data_.activations_.activation()->asJit();
|
||||
|
||||
// ActivationIterator::ionTop_ may be invalid, so create a new
|
||||
|
@ -1107,13 +1107,13 @@ FrameIter::updatePcQuadratic()
|
|||
++data_.activations_;
|
||||
|
||||
// Look for the current frame.
|
||||
data_.ionFrames_ = jit::IonFrameIterator(data_.activations_);
|
||||
while (!data_.ionFrames_.isBaselineJS() || data_.ionFrames_.baselineFrame() != frame)
|
||||
++data_.ionFrames_;
|
||||
data_.jitFrames_ = jit::JitFrameIterator(data_.activations_);
|
||||
while (!data_.jitFrames_.isBaselineJS() || data_.jitFrames_.baselineFrame() != frame)
|
||||
++data_.jitFrames_;
|
||||
|
||||
// Update the pc.
|
||||
JS_ASSERT(data_.ionFrames_.baselineFrame() == frame);
|
||||
data_.ionFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
|
||||
JS_ASSERT(data_.jitFrames_.baselineFrame() == frame);
|
||||
data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
@ -1134,9 +1134,9 @@ FrameIter::callee() const
|
|||
return &interpFrame()->callee();
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
if (data_.ionFrames_.isBaselineJS())
|
||||
return data_.ionFrames_.callee();
|
||||
JS_ASSERT(data_.ionFrames_.isIonJS());
|
||||
if (data_.jitFrames_.isBaselineJS())
|
||||
return data_.jitFrames_.callee();
|
||||
JS_ASSERT(data_.jitFrames_.isIonJS());
|
||||
return ionInlineFrames_.callee();
|
||||
#else
|
||||
break;
|
||||
|
@ -1177,11 +1177,11 @@ FrameIter::numActualArgs() const
|
|||
return interpFrame()->numActualArgs();
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
if (data_.ionFrames_.isIonJS())
|
||||
if (data_.jitFrames_.isIonJS())
|
||||
return ionInlineFrames_.numActualArgs();
|
||||
|
||||
JS_ASSERT(data_.ionFrames_.isBaselineJS());
|
||||
return data_.ionFrames_.numActualArgs();
|
||||
JS_ASSERT(data_.jitFrames_.isBaselineJS());
|
||||
return data_.jitFrames_.numActualArgs();
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
|
@ -1206,8 +1206,8 @@ FrameIter::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing) const
|
|||
return interpFrame()->unaliasedActual(i, checkAliasing);
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
JS_ASSERT(data_.ionFrames_.isBaselineJS());
|
||||
return data_.ionFrames_.baselineFrame()->unaliasedActual(i, checkAliasing);
|
||||
JS_ASSERT(data_.jitFrames_.isBaselineJS());
|
||||
return data_.jitFrames_.baselineFrame()->unaliasedActual(i, checkAliasing);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
|
@ -1224,9 +1224,9 @@ FrameIter::scopeChain() const
|
|||
break;
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
if (data_.ionFrames_.isIonJS())
|
||||
if (data_.jitFrames_.isIonJS())
|
||||
return ionInlineFrames_.scopeChain();
|
||||
return data_.ionFrames_.baselineFrame()->scopeChain();
|
||||
return data_.jitFrames_.baselineFrame()->scopeChain();
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
|
@ -1258,8 +1258,8 @@ FrameIter::hasArgsObj() const
|
|||
return interpFrame()->hasArgsObj();
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
JS_ASSERT(data_.ionFrames_.isBaselineJS());
|
||||
return data_.ionFrames_.baselineFrame()->hasArgsObj();
|
||||
JS_ASSERT(data_.jitFrames_.isBaselineJS());
|
||||
return data_.jitFrames_.baselineFrame()->hasArgsObj();
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
|
@ -1278,8 +1278,8 @@ FrameIter::argsObj() const
|
|||
break;
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
JS_ASSERT(data_.ionFrames_.isBaselineJS());
|
||||
return data_.ionFrames_.baselineFrame()->argsObj();
|
||||
JS_ASSERT(data_.jitFrames_.isBaselineJS());
|
||||
return data_.jitFrames_.baselineFrame()->argsObj();
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
|
@ -1309,9 +1309,9 @@ FrameIter::thisv() const
|
|||
break;
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
if (data_.ionFrames_.isIonJS())
|
||||
if (data_.jitFrames_.isIonJS())
|
||||
return ObjectValue(*ionInlineFrames_.thisObject());
|
||||
return data_.ionFrames_.baselineFrame()->thisValue();
|
||||
return data_.jitFrames_.baselineFrame()->thisValue();
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
|
@ -1330,8 +1330,8 @@ FrameIter::returnValue() const
|
|||
break;
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
if (data_.ionFrames_.isBaselineJS())
|
||||
return data_.ionFrames_.baselineFrame()->returnValue();
|
||||
if (data_.jitFrames_.isBaselineJS())
|
||||
return data_.jitFrames_.baselineFrame()->returnValue();
|
||||
#endif
|
||||
break;
|
||||
case INTERP:
|
||||
|
@ -1349,8 +1349,8 @@ FrameIter::setReturnValue(const Value &v)
|
|||
break;
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
if (data_.ionFrames_.isBaselineJS()) {
|
||||
data_.ionFrames_.baselineFrame()->setReturnValue(v);
|
||||
if (data_.jitFrames_.isBaselineJS()) {
|
||||
data_.jitFrames_.baselineFrame()->setReturnValue(v);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
@ -1371,12 +1371,12 @@ FrameIter::numFrameSlots() const
|
|||
break;
|
||||
case JIT: {
|
||||
#ifdef JS_ION
|
||||
if (data_.ionFrames_.isIonJS()) {
|
||||
if (data_.jitFrames_.isIonJS()) {
|
||||
return ionInlineFrames_.snapshotIterator().numAllocations() -
|
||||
ionInlineFrames_.script()->nfixed();
|
||||
}
|
||||
jit::BaselineFrame *frame = data_.ionFrames_.baselineFrame();
|
||||
return frame->numValueSlots() - data_.ionFrames_.script()->nfixed();
|
||||
jit::BaselineFrame *frame = data_.jitFrames_.baselineFrame();
|
||||
return frame->numValueSlots() - data_.jitFrames_.script()->nfixed();
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
|
@ -1397,14 +1397,14 @@ FrameIter::frameSlotValue(size_t index) const
|
|||
break;
|
||||
case JIT:
|
||||
#ifdef JS_ION
|
||||
if (data_.ionFrames_.isIonJS()) {
|
||||
if (data_.jitFrames_.isIonJS()) {
|
||||
jit::SnapshotIterator si(ionInlineFrames_.snapshotIterator());
|
||||
index += ionInlineFrames_.script()->nfixed();
|
||||
return si.maybeReadAllocByIndex(index);
|
||||
}
|
||||
|
||||
index += data_.ionFrames_.script()->nfixed();
|
||||
return *data_.ionFrames_.baselineFrame()->valueSlot(index);
|
||||
index += data_.jitFrames_.script()->nfixed();
|
||||
return *data_.jitFrames_.baselineFrame()->valueSlot(index);
|
||||
#else
|
||||
break;
|
||||
#endif
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "jsfun.h"
|
||||
#include "jsscript.h"
|
||||
|
||||
#include "jit/IonFrameIterator.h"
|
||||
#include "jit/JitFrameIterator.h"
|
||||
#ifdef CHECK_OSIPOINT_REGISTERS
|
||||
#include "jit/Registers.h" // for RegisterDump
|
||||
#endif
|
||||
|
@ -1515,7 +1515,7 @@ class FrameIter
|
|||
ActivationIterator activations_;
|
||||
|
||||
#ifdef JS_ION
|
||||
jit::IonFrameIterator ionFrames_;
|
||||
jit::JitFrameIterator jitFrames_;
|
||||
#endif
|
||||
|
||||
Data(JSContext *cx, SavedOption savedOption, ContextOption contextOption,
|
||||
|
@ -1758,9 +1758,9 @@ FrameIter::script() const
|
|||
return interpFrame()->script();
|
||||
#ifdef JS_ION
|
||||
JS_ASSERT(data_.state_ == JIT);
|
||||
if (data_.ionFrames_.isIonJS())
|
||||
if (data_.jitFrames_.isIonJS())
|
||||
return ionInlineFrames_.script();
|
||||
return data_.ionFrames_.script();
|
||||
return data_.jitFrames_.script();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
|
@ -1770,7 +1770,7 @@ inline bool
|
|||
FrameIter::isIon() const
|
||||
{
|
||||
#ifdef JS_ION
|
||||
return isJit() && data_.ionFrames_.isIonJS();
|
||||
return isJit() && data_.jitFrames_.isIonJS();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
|
@ -1780,7 +1780,7 @@ inline bool
|
|||
FrameIter::isBaseline() const
|
||||
{
|
||||
#ifdef JS_ION
|
||||
return isJit() && data_.ionFrames_.isBaselineJS();
|
||||
return isJit() && data_.jitFrames_.isBaselineJS();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
|
|
|
@ -5098,6 +5098,36 @@ nsRect nsDisplayTransform::TransformRectOut(const nsRect &aUntransformedBounds,
|
|||
factor);
|
||||
}
|
||||
|
||||
bool nsDisplayTransform::UntransformRect(const nsRect &aTransformedBounds,
|
||||
const nsRect &aChildBounds,
|
||||
const nsIFrame* aFrame,
|
||||
const nsPoint &aOrigin,
|
||||
nsRect *aOutRect)
|
||||
{
|
||||
NS_PRECONDITION(aFrame, "Can't take the transform based on a null frame!");
|
||||
|
||||
float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
|
||||
gfx3DMatrix transform = GetResultingTransformMatrix(aFrame, aOrigin, factor, nullptr);
|
||||
if (transform.IsSingular()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gfxRect result(NSAppUnitsToFloatPixels(aTransformedBounds.x, factor),
|
||||
NSAppUnitsToFloatPixels(aTransformedBounds.y, factor),
|
||||
NSAppUnitsToFloatPixels(aTransformedBounds.width, factor),
|
||||
NSAppUnitsToFloatPixels(aTransformedBounds.height, factor));
|
||||
|
||||
gfxRect childGfxBounds(NSAppUnitsToFloatPixels(aChildBounds.x, factor),
|
||||
NSAppUnitsToFloatPixels(aChildBounds.y, factor),
|
||||
NSAppUnitsToFloatPixels(aChildBounds.width, factor),
|
||||
NSAppUnitsToFloatPixels(aChildBounds.height, factor));
|
||||
|
||||
result = transform.UntransformBounds(result, childGfxBounds);
|
||||
*aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool nsDisplayTransform::UntransformVisibleRect(nsDisplayListBuilder* aBuilder,
|
||||
nsRect *aOutRect)
|
||||
{
|
||||
|
|
|
@ -3258,6 +3258,12 @@ public:
|
|||
/* UntransformRect is like TransformRect, except that it inverts the
|
||||
* transform.
|
||||
*/
|
||||
static bool UntransformRect(const nsRect &aTransformedBounds,
|
||||
const nsRect &aChildBounds,
|
||||
const nsIFrame* aFrame,
|
||||
const nsPoint &aOrigin,
|
||||
nsRect *aOutRect);
|
||||
|
||||
bool UntransformVisibleRect(nsDisplayListBuilder* aBuilder,
|
||||
nsRect* aOutRect);
|
||||
|
||||
|
|
|
@ -181,11 +181,6 @@ StickyScrollContainer::ComputeStickyLimits(nsIFrame* aFrame, nsRect* aStick,
|
|||
}
|
||||
|
||||
nsIFrame* scrolledFrame = mScrollFrame->GetScrolledFrame();
|
||||
// FIXME (Bug 920688): cbFrame isn't quite right if we're dealing
|
||||
// with a block-in-inline split whose first part is a block. We
|
||||
// probably want the first in flow of the containing block of the
|
||||
// first inline part. (Or maybe those block-in-inline split pieces
|
||||
// are never a containing block, and we're ok?)
|
||||
nsIFrame* cbFrame = aFrame->GetContainingBlock();
|
||||
NS_ASSERTION(cbFrame == scrolledFrame ||
|
||||
nsLayoutUtils::IsProperAncestorFrame(scrolledFrame, cbFrame),
|
||||
|
|
|
@ -1929,22 +1929,16 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
|
|||
if (overflow.IsEmpty() && !Preserves3DChildren()) {
|
||||
return;
|
||||
}
|
||||
// Trying to back-transform arbitrary rects gives us really weird results. I believe
|
||||
// this is from points that lie beyond the vanishing point. As a workaround we transform
|
||||
// the overflow rect into screen space and compare in that coordinate system.
|
||||
|
||||
// Transform the overflow rect into screen space.
|
||||
nsPoint offset = aBuilder->ToReferenceFrame(this);
|
||||
nsRect trans = nsDisplayTransform::TransformRect(overflow + offset, this, offset);
|
||||
dirtyRect += offset;
|
||||
if (dirtyRect.Intersects(trans)) {
|
||||
// If they intersect, we take our whole overflow rect. We could instead take the intersection
|
||||
// and then reverse transform it but I doubt this extra work is worthwhile.
|
||||
dirtyRect = overflow;
|
||||
|
||||
nsRect untransformedDirtyRect;
|
||||
if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this, offset, &untransformedDirtyRect)) {
|
||||
dirtyRect = untransformedDirtyRect;
|
||||
} else {
|
||||
if (!Preserves3DChildren()) {
|
||||
return;
|
||||
}
|
||||
NS_WARNING("Unable to untransform dirty rect!");
|
||||
// This should only happen if the transform is singular, in which case nothing is visible anyway
|
||||
dirtyRect.SetEmpty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
# each font. Do not include the Unicode table in this list.
|
||||
|
||||
%ifdef XP_WIN
|
||||
font.mathfont-glyph-tables = MathJax_Main, STIXNonUnicode, STIXSizeOneSym, STIXSize1, Asana Math, Standard Symbols L, Symbol
|
||||
font.mathfont-glyph-tables = MathJax_Main, STIXNonUnicode, STIXSizeOneSym, Standard Symbols L, Symbol
|
||||
%else
|
||||
font.mathfont-glyph-tables = MathJax_Main, STIXNonUnicode, STIXSizeOneSym, STIXSize1, Asana Math, Standard Symbols L
|
||||
font.mathfont-glyph-tables = MathJax_Main, STIXNonUnicode, STIXSizeOneSym, Standard Symbols L
|
||||
%endif
|
||||
|
||||
# The ordered list of fonts with which to attempt to stretch MathML
|
||||
|
|
|
@ -1,145 +0,0 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# LOCALIZATION NOTE: FILE
|
||||
# Do not translate anything in this file
|
||||
|
||||
# This file contains the list of some stretchy MathML chars that
|
||||
# can be rendered with Asana Math font.
|
||||
|
||||
# [ T/L | M | B/R | G | size0 ... size{N-1} ]
|
||||
# (*) not in the MathML operator dictionary
|
||||
|
||||
\u0028 = \u239B\uFFFD\u239D\u239C\u0028\uDBFF\uDFF4\uDBFF\uDFF5\uDBFF\uDFF6 # (
|
||||
\u0029 = \u239E\uFFFD\u23A0\u239F\u0029\uDBFF\uDFF7\uDBFF\uDFF8\uDBFF\uDFF9 # )
|
||||
\u005B = \u23A1\uFFFD\u23A3\u23A2\u005B\uDBFF\uDFEE\uDBFF\uDFEF\uDBFF\uDFF0 # [
|
||||
\u005D = \u23A4\uFFFD\u23A6\u23A5\u005D\uDBFF\uDFF1\uDBFF\uDFF2\uDBFF\uDFF3 # ]
|
||||
\u007B = \u23A7\u23A8\u23A9\u23AA\u007B\uDBFF\uDFFA\uDBFF\uDFFB\uDBFF\uDFFC # {
|
||||
\u007C = \uFFFD\uFFFD\uFFFD\u007C\u007C\uDBFF\uDFD6\uDBFF\uDFD7\uDBFF\uDFD8\uDBFF\uDFD9 # |
|
||||
\u007D = \u23AB\u23AC\u23AD\u23AA\u007D\uDBFF\uDFFD\uDBFF\uDFFE\uDBFF\uDFFF # }
|
||||
\u2016 = \uFFFD\uFFFD\uFFFD\uDBFF\uDFD1\u2016\uDBFF\uDFCE\uDBFF\uDFCF\uDBFF\uDFD0\uDBFF\uDFD1 # DOUBLE VERTICAL LINE
|
||||
|
||||
\u2044 = \uFFFD\uFFFD\uFFFD\uFFFD\u2044\uDBFF\uDFD2\uDBFF\uDFD3\uDBFF\uDFD4\uDBFF\uDFD5 # FRACTION SLASH
|
||||
# \u2045 = \uDBFF\uDFB6\uDBFF\uDF53\uDBFF\uDFB7\uDBFF\uDFBA\u2045\uDBFF\uDFBB\uDBFF\uDFBC\uDBFF\uDFBD # LEFT SQUARE BRACKET WITH QUILL (*)
|
||||
# \u2046 = \uDBFF\uDFB8\uDBFF\uDF54\uDBFF\uDFB9\uDBFF\uDF52\u2046\uDBFF\uDFBE\uDBFF\uDFBF\uDBFF\uDFC0 # RIGHT SQUARE BRACKET WITH QUILL (*)
|
||||
|
||||
\u2191 = \u2191\uFFFD\uFFFD\uDBFF\uDEC6\u2191 # UPWARDS ARROW
|
||||
\u2193 = \uFFFD\uFFFD\u2193\uDBFF\uDEC6\u2193 # DOWNWARDS ARROW
|
||||
\u21D1 = \u21D1\uFFFD\uFFFD\uDBFF\uDEC7\u21D1 # UPWARDS DOUBLE ARROW
|
||||
\u21D3 = \uFFFD\uFFFD\u21D3\uDBFF\uDEC7\u21D3 # DOWNWARDS DOUBLE ARROW
|
||||
|
||||
\u220F = \uFFFD\uFFFD\uFFFD\uFFFD\u220F\uDBFF\uDF9F\uDBFF\uDFA0\uDBFF\uDFA1 # N-ARY PRODUCT
|
||||
\u2210 = \uFFFD\uFFFD\uFFFD\uFFFD\u2210\uDBFF\uDFA2\uDBFF\uDFA3\uDBFF\uDFA4 # N-ARY COPRODUCT
|
||||
\u2211 = \uFFFD\uFFFD\uFFFD\uFFFD\u2211\uDBFF\uDF9C\uDBFF\uDF9D\uDBFF\uDF9E # summation N-ARY SUMMATION
|
||||
\u221A = \uDBFF\uDF6D\uFFFD\u23B7\u20D3\u221A\uDBFF\uDF6E\uDBFF\uDF6F\uDBFF\uDF70\uDBFF\uDF71 # SQUARE ROOT
|
||||
\u2223 = \uFFFD\uFFFD\uFFFD\u2223\u2223 # DIVIDES
|
||||
\u2225 = \uFFFD\uFFFD\uFFFD\u2225\u2225 # PARALLEL TO
|
||||
\u222B = \u2320\uFFFD\u2321\u23AE\u222B\uDBFF\uDF99\uDBFF\uDF9A\uDBFF\uDF9B # INTEGRAL
|
||||
\u222C = \uFFFD\uFFFD\uFFFD\uFFFD\u222C\uDBFF\uDF6A\uDBFF\uDF6B\uDBFF\uDF6C # DOUBLE INTEGRAL
|
||||
\u222D = \uFFFD\uFFFD\uFFFD\uFFFD\u222D\uDBFF\uDF67\uDBFF\uDF68\uDBFF\uDF69 # TRIPLE INTEGRAL
|
||||
\u222E = \uFFFD\uFFFD\uFFFD\uFFFD\u222E\uDBFF\uDF64\uDBFF\uDF65\uDBFF\uDF66 # CONTOUR INTEGRAL
|
||||
\u222F = \uFFFD\uFFFD\uFFFD\uFFFD\u222F\uDBFF\uDF61\uDBFF\uDF62\uDBFF\uDF63 # SURFACE INTEGRAL
|
||||
\u2230 = \uFFFD\uFFFD\uFFFD\uFFFD\u2230\uDBFF\uDF5E\uDBFF\uDF5F\uDBFF\uDF60 # VOLUME INTEGRAL
|
||||
\u2231 = \uFFFD\uFFFD\uFFFD\uFFFD\u2231\uDBFF\uDF5B\uDBFF\uDF5C\uDBFF\uDF5D # CLOCKWISE INTEGRAL
|
||||
\u2232 = \uFFFD\uFFFD\uFFFD\uFFFD\u2232\uDBFF\uDF58\uDBFF\uDF59\uDBFF\uDF5A # CLOCKWISE CONTOUR INTEGRAL
|
||||
\u2233 = \uFFFD\uFFFD\uFFFD\uFFFD\u2233\uDBFF\uDF55\uDBFF\uDF56\uDBFF\uDF57 # ANTICLOCKWISE CONTOUR INTEGRAL
|
||||
|
||||
\u22C0 = \uFFFD\uFFFD\uFFFD\uFFFD\u22C0\uDBFF\uDF92\uDBFF\uDF93 # N-ARY LOGICAL AND
|
||||
\u22C1 = \uFFFD\uFFFD\uFFFD\uFFFD\u22C1\uDBFF\uDF94\uDBFF\uDF95 # N-ARY LOGICAL OR
|
||||
\u22C2 = \uFFFD\uFFFD\uFFFD\uFFFD\u22C2\uDBFF\uDF8E\uDBFF\uDF8F # N-ARY INTERSECTION
|
||||
\u22C3 = \uFFFD\uFFFD\uFFFD\uFFFD\u22C3\uDBFF\uDF8C\uDBFF\uDF8D # N-ARY UNION
|
||||
\u2308 = \u23A1\uFFFD\uFFFD\u23A2\u2308\uDBFF\uDFE2\uDBFF\uDFE3\uDBFF\uDFE4 # LEFT CEILING
|
||||
\u2309 = \u23A4\uFFFD\uFFFD\u23A5\u2309\uDBFF\uDFE5\uDBF\uDFE6\uDBFF\uDFE7 # RIGHT CEILING
|
||||
\u230A = \uFFFD\uFFFD\u23A3\u23A2\u230A\uDBFF\uDFE8\uDBFF\uDFE9\uDBFF\uDFEA # LEFT FLOOR
|
||||
\u230B = \uFFFD\uFFFD\u23A6\u23A5\u230B\u230B\uDBFF\uDFEB\uDBFF\uDFEC\uDBFF\uDFED # RIGHT FLOOR
|
||||
|
||||
# \u27C5 = \uFFFD\uFFFD\uFFFD\uFFFD\u27C5\uDBFF\uDDF3\uDBFF\uDDF5\uDBFF\uDDF7\uDBFF\uDDF9\uDBFF\uDDFB # LEFT S-SHAPED BAG DELIMITER (*)
|
||||
# \u27C6 = \uFFFD\uFFFD\uFFFD\uFFFD\uDBFF\uDDF4\uDBFF\uDDF6\uDBFF\uDDF8\uDBFF\uDDFA\uDBFF\uDDFC # RIGHT S-SHAPED BAG DELIMITER (*)
|
||||
\u27E6 = \uFFFD\uFFFD\uFFFD\uFFFD\u27E6\uDBFF\uDFDA\uDBFF\uDFDB\uDBFF\uDFDC\uDBFF\uDFDD # MATHEMATICAL LEFT WHITE SQUARE BRACKET
|
||||
\u27E7 = \uFFFD\uFFFD\uFFFD\uFFFD\u27E7\uDBFF\uDFDE\uDBFF\uDFDF\uDBFF\uDFE0\uDBFF\uDFE1 # MATHEMATICAL RIGHT WHITE SQUARE BRACKET
|
||||
\u27E8 = \uFFFD\uFFFD\uFFFD\uFFFD\u27E8\uDBFF\uDF89\uDBFF\uDF8A\uDBFF\uD8B # MATHEMATICAL LEFT ANGLE BRACKET
|
||||
\u27E9 = \uFFFD\uFFFD\uFFFD\uFFFD\u27E9\uDBFF\uDF7C\uDBFF\uDF7D\uDBFF\uDF7E # MATHEMATICAL RIGHT ANGLE BRACKET
|
||||
\u27EA = \uFFFD\uFFFD\uFFFD\uFFFD\u27EA\uDBFF\uDF76\uDBFF\uDF77\uDBFF\uDF78 # MATHEMATICAL LEFT DOUBLE ANGLE BRACKET
|
||||
\u27EB = \uFFFD\uFFFD\uFFFD\uFFFD\u27EB\uDBFF\uDF79\uDBFF\uDF7A\uDBFF\uDF7B # MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET
|
||||
|
||||
\u29FC = \uFFFD\uFFFD\uFFFD\uFFFD\u29FC\uDBFF\uDEC8\uDBFF\uDEC9\uDBFF\uDECA # LEFT-POINTING CURVED ANGLE BRACKET
|
||||
\u29FD = \uFFFD\uFFFD\uFFFD\uFFFD\u29FD\uDBFF\uDECB\uDBFF\uDECC\uDBFF\uDECD # RIGHT-POINTING CURVED ANGLE BRACKET
|
||||
|
||||
\u2A00 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A00\uDBFF\uDF96\uDBFF\uDF97 # N-ARY CIRCLED DOT OPERATOR
|
||||
\u2A01 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A01\uDBFF\uDF98\uDBFF\uDFA5 # N-ARY CIRCLED PLUS OPERATOR
|
||||
\u2A02 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A02\uDBFF\uDF7F\uDBFF\uDF80 # N-ARY CIRCLED TIMES OPERATOR
|
||||
\u2A03 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A03\uDBFF\uDF81\uDBFF\uDF82 # N-ARY UNION OPERATOR WITH DOT
|
||||
\u2A04 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A04\uDBFF\uDF90\uDBFF\uDF91 # N-ARY UNION OPERATOR WITH PLUS
|
||||
\u2A05 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A05\uDBFF\uDF83\uDBFF\uDF84 # N-ARY SQUARE INTERSECTION OPERATOR
|
||||
\u2A06 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A06\uDBFF\uDF85\uDBFF\uDF86 # N-ARY SQUARE UNION OPERATOR
|
||||
\u2A07 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A07\uDBFF\uDF72\uDBFF\uDF73 # TWO LOGICAL AND OPERATOR
|
||||
\u2A08 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A08\uDBFF\uDF74\uDBFF\uDF75 # TWO LOGICAL OR OPERATOR
|
||||
\u2A09 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A09\uDBFF\uDF87\uDBFF\uDF88 # N-ARY TIMES OPERATOR
|
||||
\u2A0C = \uFFFD\uFFFD\uFFFD\uFFFD\u2A0C\uDBFF\uDF1F\uDBFF\uDF20\uDBFF\uDF21 # QUADRUPLE INTEGRAL OPERATOR
|
||||
\u2A0D = \uFFFD\uFFFD\uFFFD\uFFFD\u2A0D\uDBFF\uDF22\uDBFF\uDF23\uDBFF\uDF24 # FINITE PART INTEGRAL
|
||||
\u2A0E = \uFFFD\uFFFD\uFFFD\uFFFD\u2A0E\uDBFF\uDF25\uDBFF\uDF26\uDBFF\uDF27 # INTEGRAL WITH DOUBLE STROKE
|
||||
\u2A0F = \uFFFD\uFFFD\uFFFD\uFFFD\u2A0F\uDBFF\uDF28\uDBFF\uDF29\uDBFF\uDF2A # INTEGRAL AVERAGE WITH SLASH
|
||||
\u2A10 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A10\uDBFF\uDF2B\uDBFF\uDF2C\uDBFF\uDF2D # CIRCULATION FUNCTION
|
||||
\u2A11 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A11\uDBFF\uDF2E\uDBFF\uDF2F\uDBFF\uDF30 # ANTICLOCKWISE INTEGRATION
|
||||
\u2A12 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A12\uDBFF\uDF31\uDBFF\uDF32\uDBFF\uDF33 # LINE INTEGRATION WITH RECTANGULAR PATH AROUND POLE
|
||||
\u2A13 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A13\uDBFF\uDF34\uDBFF\uDF35\uDBFF\uDF36 # LINE INTEGRATION WITH SEMICIRCULAR PATH AROUND POLE
|
||||
\u2A14 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A14\uDBFF\uDF37\uDBFF\uDF38\uDBFF\uDF39 # LINE INTEGRATION NOT INCLUDING THE POLE
|
||||
\u2A15 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A15\uDBFF\uDF3A\uDBFF\uDF3B\uDBFF\uDF3C # INTEGRAL AROUND A POINT OPERATOR
|
||||
\u2A16 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A16\uDBFF\uDF3D\uDBFF\uDF3E\uDBFF\uDF3F # QUATERNION INTEGRAL OPERATOR
|
||||
\u2A17 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A17\uDBFF\uDF40\uDBFF\uDF41\uDBFF\uDF42 # INTEGRAL WITH LEFTWARDS ARROW WITH HOOK
|
||||
\u2A18 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A18\uDBFF\uDF43\uDBFF\uDF44\uDBFF\uDF45 # INTEGRAL WITH TIMES SIGN
|
||||
\u2A19 = \uFFFD\uFFFD\uFFFD\uFFFD\u2A19\uDBFF\uDF46\uDBFF\uDF47\uDBFF\uDF48 # INTEGRAL WITH INTERSECTION
|
||||
\u2A1A = \uFFFD\uFFFD\uFFFD\uFFFD\u2A1A\uDBFF\uDF49\uDBFF\uDF4A\uDBFF\uDF4B # INTEGRAL WITH UNION
|
||||
\u2A1B = \uFFFD\uFFFD\uFFFD\uFFFD\u2A1B\uDBFF\uDF4C\uDBFF\uDF4D\uDBFF\uDF4E # INTEGRAL WITH OVERBAR
|
||||
\u2A1C = \uFFFD\uFFFD\uFFFD\uFFFD\u2A1C\uDBFF\uDF4F\uDBFF\uDF50\uDBFF\uDF51 # INTEGRAL WITH UNDERBAR
|
||||
|
||||
\u005E = \uFFFD\uFFFD\uFFFD\uFFFD\u005E\uDBFF\uDFA6\uDBFF\uDFA7\uDBFF\uDFA8 # CIRCUMFLEX ACCENT
|
||||
\u0302 = \uFFFD\uFFFD\uFFFD\uFFFD\u005E\uDBFF\uDFA6\uDBFF\uDFA7\uDBFF\uDFA8 # COMBINING CIRCUMFLEX ACCENT
|
||||
\u007E = \uFFFD\uFFFD\uFFFD\uFFFD\u007E\uDBFF\uDFAA\uDBFF\uDFAB\uDBFF\uDFAC\uDBFF\uDFAD # TILDE
|
||||
\u02DC = \uFFFD\uFFFD\uFFFD\uFFFD\u007E\uDBFF\uDFAA\uDBFF\uDFAB\uDBFF\uDFAC\uDBFF\uDFAD # SMALL TILDE
|
||||
# \u0303 = \uFFFD\uFFFD\uFFFD\uFFFD\u007E\uDBFF\uDFAA\uDBFF\uDFAB\uDBFF\uDFAC\uDBFF\uDFAD # COMBINING TILDE (*)
|
||||
# \u0305 = \uFFFD\uFFFD\uFFFD\uDBFF\uDF1E\u0305 COMBINING OVERLINE (*)
|
||||
# \u0306 = \uFFFD\uFFFD\uFFFD\uFFFD\u02D8\uDBFF\uDFB2\uDBFF\uDFB3\uDBFF\uDFB4\uDBFF\uDFB5 # COMBINING BREVE (*)
|
||||
# \u02D8 = \uFFFD\uFFFD\uFFFD\uFFFD\u02D8\uDBFF\uDFB2\uDBFF\uDFB3\uDBFF\uDFB4\uDBFF\uDFB5 # BREVE (not stretchy)
|
||||
\u02C7 = \uFFFD\uFFFD\uFFFD\uFFFD\u02C7\uDBFF\uDFAE\uDBFF\uDFAF\uDBFF\uDFB0\uDBFF\uDFB1 # CARON
|
||||
# \u030C = \uFFFD\uFFFD\uFFFD\uFFFD\u02C7\uDBFF\uDFAE\uDBFF\uDFAF\uDBFF\uDFB0\uDBFF\uDFB1 # COMBINING CARON (*)
|
||||
# \u0332 = \uFFFD\uFFFD\uFFFD\uDBFF\uDF1D\u0332\uDBFF\uDF1D\uDBFF\uDF18\uDBFF\uDF14 # COMBINING LOW LINE (*)
|
||||
# \u0333 = \uFFFD\uFFFD\uFFFD\uDBFF\uDF1C\u0333\uDBFF\uDF1C\uDBFF\uDF17\uDBFF\uDF13 # COMBINING DOUBLE LOW LINE (*)
|
||||
# \u033F = \uFFFD\uFFFD\uFFFD\uDBFF\uDF1B\u033F\uDBFF\uDF1B\uDBFF\uDF16\uDBFF\uDF12 # COMBINING DOUBLE OVERLINE (*)
|
||||
# \u20D0 = \u20D0\uFFFD\uFFFD\uDBFF\uDF1A\u20D0 # COMBINING LEFT HARPOON ABOVE (*)
|
||||
# \u20D1 = \uFFFD\uFFFD\u20D1\uDBFF\uDF1A\u20D1 # COMBINING RIGHT HARPOON ABOVE (*)
|
||||
# \u20D6 = \u20D6\uFFFD\uFFFD\uDBFF\uDF1A\u20D6\uDBFF\uDE4A\uDBFF\uDE4B\uDBFF\uDE4C\uDBFF\uDE4D # COMBINING LEFT ARROW ABOVE (*)
|
||||
# \u20D7 = \uFFFD\uFFFD\u20D7\uDBFF\uDF1A\u20D7\uDBFF\uDE4E\uDBFF\uDE4F\uDBFF\uDE50\uDBFF\uDE51 # COMBINING RIGHT ARROW ABOVE (*)
|
||||
# \u20E1 = \u20D6\uFFFD\u20D7\uDBFF\uDF1A\u20E1 # COMBINING LEFT RIGHT ARROW ABOVE (*)
|
||||
# \u20E9 = \uDBFF\uDEEC\uFFFD\uDBFF\uDEED\uDBFF\uDEEB\u20E9 # COMBINING WIDE BRIDGE ABOVE (*)
|
||||
|
||||
\u2190 = \uDBFF\uDF11\uFFFD\uDBFF\uDF10\u23AF\u2190 # LEFTWARDS ARROW
|
||||
\u2192 = \uDBFF\uDF0E\uFFFD\uDBFF\uDF0F\u23AF\u2192 # RIGHTWARDS ARROW
|
||||
\u2194 = \uDBFF\uDF11\uFFFD\uDBFF\uDF0F\u23AF\u2194 # LEFT RIGHT ARROW
|
||||
\u21A4 = \uDBFF\uDF11\uFFFD\uDBFF\uDF08\u23AF\u21A4 # LEFTWARDS ARROW FROM BAR
|
||||
\u21A6 = \uDBFF\uDF07\uFFFD\uDBFF\uDF0F\u23AF\u21A6 # RIGHTWARDS ARROW FROM BAR
|
||||
\u21A9 = \uDBFF\uDF11\uFFFD\uDBFF\uDF06\u23AF\u21A9 # LEFTWARDS ARROW WITH HOOK
|
||||
\u21AA = \uDBFF\uDF05\uFFFD\uDBFF\uDF0F\u23AF\u21AA # RIGHTWARDS ARROW WITH HOOK
|
||||
|
||||
\u21D0 = \uDBFF\uDF0D\uFFFD\uDBFF\uDF0C\uDBFF\uDF09\u21D0 # LEFTWARDS DOUBLE ARROW
|
||||
\u21D2 = \uDBFF\uDF0A\uFFFD\uDBFF\uDF0B\uDBFF\uDF09\u21D2 # RIGHTWARDS DOUBLE ARROW
|
||||
\u21D4 = \uDBFF\uDF0D\uFFFD\uDBFF\uDF0B\uDBFF\uDF09\u21D4 # LEFT RIGHT DOUBLE ARROW
|
||||
|
||||
\u23B4 = \uDBFF\uDEEC\uFFFD\uDBFF\uDEED\uDBFF\uDEEB\u23B4\uDBFF\uDEFD\uDBFF\uDEFE\uDBFF\uDEFF # TOP SQUARE BRACKET
|
||||
\u23B5 = \uDBFF\uDEEE\uFFFD\uDBFF\uDEEF\uDBFF\uDEEA\u23B5\uDBFF\uDF00\uDBFF\uDF01\uDBFF\uDF02 # BOTTOM SQUARE BRACKET
|
||||
|
||||
\u23DC = \uDBFF\uDFC7\uFFFD\uDBFF\uDFC9\uDBFF\uDFCA\u23DC\uDBFF\uDEF7\uDBFF\uDEF8\uDBFF\uDEF9 # TOP PARENTHESIS
|
||||
\uFE35 = \uDBFF\uDFC7\uFFFD\uDBFF\uDFC9\uDBFF\uDFCA\u23DC\uDBFF\uDEF7\uDBFF\uDEF8\uDBFF\uDEF9 # ⏜ (MathML 2.0)
|
||||
\u23DD = \uDBFF\uDFCB\uFFFD\uDBFF\uDFCD\uDBFF\uDEF0\u23DD\uDBFF\uDEFA\uDBFF\uDEFB\uDBFF\uDEFC # BOTTOM PARENTHESIS
|
||||
\uFE36 = \uDBFF\uDFCB\uFFFD\uDBFF\uDFCD\uDBFF\uDEF0\u23DD\uDBFF\uDEFA\uDBFF\uDEFB\uDBFF\uDEFC # ⏝ (MathML 2.0)
|
||||
|
||||
\u23DE = \uDBFF\uDFC7\uDBFF\uDFC8\uDBFF\uDFC9\uDBFF\uDFCA\u23DE\uDBFF\uDFC1\uDBFF\uDFC2\uDBFF\uDFC3 # TOP CURLY BRACKET
|
||||
\uFE37 = \uDBFF\uDFC7\uDBFF\uDFC8\uDBFF\uDFC9\uDBFF\uDFCA\u23DE\uDBFF\uDFC1\uDBFF\uDFC2\uDBFF\uDFC3 # ⏞ (MathML 2.0)
|
||||
\u23DF = \uDBFF\uDFCB\uDBFF\uDFCC\uDBFF\uDFCD\uDBFF\uDEF0\u23DF\uDBFF\uDFC4\uDBFF\uDFC5\uDBFF\uDFC6 # BOTTOM CURLY BRACKET
|
||||
\uFE38 = \uDBFF\uDFCB\uDBFF\uDFCC\uDBFF\uDFCD\uDBFF\uDEF0\u23DF\uDBFF\uDFC4\uDBFF\uDFC5\uDBFF\uDFC6 # ⏟ (MathML 2.0)
|
||||
\u23E0 = \uFFFD\uFFFD\uFFFD\uFFFD\u23E0\uDBFF\uDEF1\uDBFF\uDEF2\uDBFF\uDEF3 # TOP TORTOISE SHELL BRACKET
|
||||
\u23E1 = \uFFFD\uFFFD\uFFFD\uFFFD\u23E1\uDBFF\uDEF4\uDBFF\uDEF5\uDBFF\uDEF6 # BOTTOM TORTOISE SHELL BRACKET
|
||||
|
||||
\u2906 = \uDBFF\uDF0D\uFFFD\uDBFF\uDF04\uDBFF\uDF09\u2906 # LEFTWARDS DOUBLE ARROW FROM BAR
|
||||
\u2907 = \uDBFF\uDF03\uFFFD\uDBFF\uDF0B\uDBFF\uDF09\u2907 # RIGHTWARDS DOUBLE ARROW FROM BAR
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче