This commit is contained in:
Ryan VanderMeulen 2014-04-23 13:42:46 -07:00
Родитель 0d3f0a78f1 e519ca57d3
Коммит 267369f360
136 изменённых файлов: 2976 добавлений и 1461 удалений

Просмотреть файл

@ -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;

459
gfx/thebes/gfxMathTable.cpp Normal file
Просмотреть файл

@ -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);
}

122
gfx/thebes/gfxMathTable.h Normal file
Просмотреть файл

@ -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 &reg,
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 &reg,
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 &reg, const Register &scratch, Label *label);
void branchNegativeZeroFloat32(const FloatRegister &reg, 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, &notZero);
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(&notZero);
}
}
// 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, &notZero);
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(&notZero);
}
}
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 &reg, 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 &reg, 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 &reg, const Register &scratch);
Condition testNegativeZeroFloat32(const FloatRegister &reg, 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 &reg, 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 &reg, 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 &reg, const Register &scratch);
Condition testNegativeZeroFloat32(const FloatRegister &reg, 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 # &OverParenthesis; (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 # &UnderParenthesis; (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 # &OverBrace; (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 # &UnderBrace; (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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше