зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to fx-team.
This commit is contained in:
Коммит
36edb17218
|
@ -578,6 +578,7 @@ pref("dom.sysmsg.enabled", true);
|
|||
pref("media.plugins.enabled", false);
|
||||
pref("media.omx.enabled", true);
|
||||
pref("media.rtsp.enabled", true);
|
||||
pref("media.rtsp.video.enabled", false);
|
||||
|
||||
// Disable printing (particularly, window.print())
|
||||
pref("dom.disable_window_print", true);
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"revision": "d369bbb3b7c415097c7c1fa303843cf5683cd843",
|
||||
"revision": "7240a5ab28eff83c26891c7a9141613149f226e9",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0ff2eb11982b9d3c7a3333900cf0d3a5c0f77e32"/>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="61866f17977a440a3297aa8ade8f410c68d2cdd7"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="09064f43116d1b965cb3ab6516fa0f1fa3c98a4c"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="e9b6626eddbc85873eaa2a9174a9bd5101e5c05f"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/>
|
||||
|
|
|
@ -5221,12 +5221,23 @@ function UpdateCurrentCharset(target) {
|
|||
pref_item.setAttribute('checked', 'false');
|
||||
}
|
||||
|
||||
var menuitem = charsetMenuGetElement(target, "charset." + wnd.document.characterSet);
|
||||
var menuitem = charsetMenuGetElement(target, "charset." + FoldCharset(wnd.document.characterSet));
|
||||
if (menuitem) {
|
||||
menuitem.setAttribute('checked', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
function FoldCharset(charset) {
|
||||
// For substantially similar encodings, treat two encodings as the same
|
||||
// for the purpose of the check mark.
|
||||
if (charset == "ISO-8859-8-I") {
|
||||
return "windows-1255";
|
||||
} else if (charset == "gb18030") {
|
||||
return "gbk";
|
||||
}
|
||||
return charset;
|
||||
}
|
||||
|
||||
function UpdateCharsetDetector(target) {
|
||||
var prefvalue;
|
||||
|
||||
|
@ -5249,7 +5260,7 @@ function UpdateMenus(event) {
|
|||
}
|
||||
|
||||
function charsetLoadListener() {
|
||||
var charset = window.content.document.characterSet;
|
||||
var charset = FoldCharset(window.content.document.characterSet);
|
||||
|
||||
if (charset.length > 0 && (charset != gLastBrowserCharset)) {
|
||||
gPrevCharset = gLastBrowserCharset;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "ThreeDPoint.h"
|
||||
#include "AudioChannelFormat.h"
|
||||
#include "AudioParamTimeline.h"
|
||||
#include "AudioContext.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
|
@ -30,7 +31,7 @@ AudioNodeStream::~AudioNodeStream()
|
|||
}
|
||||
|
||||
void
|
||||
AudioNodeStream::SetStreamTimeParameter(uint32_t aIndex, MediaStream* aRelativeToStream,
|
||||
AudioNodeStream::SetStreamTimeParameter(uint32_t aIndex, AudioContext* aContext,
|
||||
double aStreamTime)
|
||||
{
|
||||
class Message : public ControlMessage {
|
||||
|
@ -50,7 +51,9 @@ AudioNodeStream::SetStreamTimeParameter(uint32_t aIndex, MediaStream* aRelativeT
|
|||
};
|
||||
|
||||
MOZ_ASSERT(this);
|
||||
GraphImpl()->AppendMessage(new Message(this, aIndex, aRelativeToStream, aStreamTime));
|
||||
GraphImpl()->AppendMessage(new Message(this, aIndex,
|
||||
aContext->DestinationStream(),
|
||||
aContext->DOMTimeToStreamTime(aStreamTime)));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -407,7 +410,12 @@ AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
|
|||
uint16_t outputCount = std::max(uint16_t(1), mEngine->OutputCount());
|
||||
mLastChunks.SetLength(outputCount);
|
||||
|
||||
if (mMuted || IsFinishedOnGraphThread()) {
|
||||
// Consider this stream blocked if it has already finished output. Normally
|
||||
// mBlocked would reflect this, but due to rounding errors our audio track may
|
||||
// appear to extend slightly beyond aFrom, so we might not be blocked yet.
|
||||
bool blocked = mFinished || mBlocked.GetAt(aFrom);
|
||||
// If the stream has finished at this time, it will be blocked.
|
||||
if (mMuted || blocked) {
|
||||
for (uint16_t i = 0; i < outputCount; ++i) {
|
||||
mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
}
|
||||
|
@ -439,16 +447,16 @@ AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
|
|||
if (finished) {
|
||||
mMarkAsFinishedAfterThisBlock = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mDisabledTrackIDs.Contains(static_cast<TrackID>(AUDIO_TRACK))) {
|
||||
for (uint32_t i = 0; i < mLastChunks.Length(); ++i) {
|
||||
mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
if (mDisabledTrackIDs.Contains(static_cast<TrackID>(AUDIO_TRACK))) {
|
||||
for (uint32_t i = 0; i < outputCount; ++i) {
|
||||
mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsFinishedOnGraphThread()) {
|
||||
// Don't output anything after we've finished!
|
||||
if (!blocked) {
|
||||
// Don't output anything while blocked
|
||||
AdvanceOutputSegment();
|
||||
if (mMarkAsFinishedAfterThisBlock && (aFlags & ALLOW_FINISH)) {
|
||||
// This stream was finished the last time that we looked at it, and all
|
||||
|
|
|
@ -16,6 +16,7 @@ namespace dom {
|
|||
struct ThreeDPoint;
|
||||
class AudioParamTimeline;
|
||||
class DelayNodeEngine;
|
||||
class AudioContext;
|
||||
}
|
||||
|
||||
class ThreadSharedFloatArrayBufferList;
|
||||
|
@ -33,6 +34,8 @@ class AudioNodeEngine;
|
|||
*/
|
||||
class AudioNodeStream : public ProcessedMediaStream {
|
||||
public:
|
||||
typedef mozilla::dom::AudioContext AudioContext;
|
||||
|
||||
enum { AUDIO_TRACK = 1 };
|
||||
|
||||
typedef nsAutoTArray<AudioChunk, 1> OutputChunks;
|
||||
|
@ -66,7 +69,7 @@ public:
|
|||
* Sets a parameter that's a time relative to some stream's played time.
|
||||
* This time is converted to a time relative to this stream when it's set.
|
||||
*/
|
||||
void SetStreamTimeParameter(uint32_t aIndex, MediaStream* aRelativeToStream,
|
||||
void SetStreamTimeParameter(uint32_t aIndex, AudioContext* aContext,
|
||||
double aStreamTime);
|
||||
void SetDoubleParameter(uint32_t aIndex, double aValue);
|
||||
void SetInt32Parameter(uint32_t aIndex, int32_t aValue);
|
||||
|
|
|
@ -172,7 +172,8 @@ class MediaRecorder::Session: public nsIObserver
|
|||
// We need to switch MediaRecorder to "Stop" state first to make sure
|
||||
// MediaRecorder is not associated with this Session anymore, then, it's
|
||||
// safe to delete this Session.
|
||||
if (recorder->mState != RecordingState::Inactive) {
|
||||
// Also avoid to run if this session already call stop before
|
||||
if (!mSession->mStopIssued) {
|
||||
ErrorResult result;
|
||||
recorder->Stop(result);
|
||||
NS_DispatchToMainThread(new DestroyRunnable(mSession.forget()));
|
||||
|
@ -182,8 +183,8 @@ class MediaRecorder::Session: public nsIObserver
|
|||
|
||||
// Dispatch stop event and clear MIME type.
|
||||
recorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop"));
|
||||
recorder->SetMimeType(NS_LITERAL_STRING(""));
|
||||
|
||||
mSession->mMimeType = NS_LITERAL_STRING("");
|
||||
recorder->SetMimeType(mSession->mMimeType);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -200,7 +201,8 @@ class MediaRecorder::Session: public nsIObserver
|
|||
public:
|
||||
Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
|
||||
: mRecorder(aRecorder),
|
||||
mTimeSlice(aTimeSlice)
|
||||
mTimeSlice(aTimeSlice),
|
||||
mStopIssued(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
|
@ -226,6 +228,7 @@ public:
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mStopIssued = true;
|
||||
CleanupStreams();
|
||||
nsContentUtils::UnregisterShutdownObserver(this);
|
||||
}
|
||||
|
@ -248,10 +251,7 @@ public:
|
|||
|
||||
already_AddRefed<nsIDOMBlob> GetEncodedData()
|
||||
{
|
||||
nsString mimeType;
|
||||
mRecorder->GetMimeType(mimeType);
|
||||
|
||||
return mEncodedBufferCache->ExtractBlob(mimeType);
|
||||
return mEncodedBufferCache->ExtractBlob(mMimeType);
|
||||
}
|
||||
|
||||
bool IsEncoderError()
|
||||
|
@ -274,10 +274,8 @@ private:
|
|||
|
||||
// Pull encoded media data from MediaEncoder
|
||||
nsTArray<nsTArray<uint8_t> > encodedBuf;
|
||||
nsString mimeType;
|
||||
mEncoder->GetEncodedData(&encodedBuf, mimeType);
|
||||
|
||||
mRecorder->SetMimeType(mimeType);
|
||||
mEncoder->GetEncodedData(&encodedBuf, mMimeType);
|
||||
mRecorder->SetMimeType(mMimeType);
|
||||
|
||||
// Append pulled data into cache buffer.
|
||||
for (uint32_t i = 0; i < encodedBuf.Length(); i++) {
|
||||
|
@ -325,8 +323,10 @@ private:
|
|||
return;
|
||||
}
|
||||
|
||||
// media stream is ready but has been issued stop command
|
||||
if (mRecorder->mState == RecordingState::Inactive) {
|
||||
// Media stream is ready but UA issues a stop method follow by start method.
|
||||
// The Session::stop would clean the mTrackUnionStream. If the AfterTracksAdded
|
||||
// comes after stop command, this function would crash.
|
||||
if (!mTrackUnionStream) {
|
||||
DoSessionEndTask(NS_OK);
|
||||
return;
|
||||
}
|
||||
|
@ -400,6 +400,8 @@ private:
|
|||
nsRefPtr<MediaEncoder> mEncoder;
|
||||
// A buffer to cache encoded meda data.
|
||||
nsAutoPtr<EncodedBufferCache> mEncodedBufferCache;
|
||||
// Current session mimeType
|
||||
nsString mMimeType;
|
||||
// Timestamp of the last fired dataavailable event.
|
||||
TimeStamp mLastBlobTimeStamp;
|
||||
// The interval of passing encoded data from EncodedBufferCache to onDataAvailable
|
||||
|
@ -407,6 +409,8 @@ private:
|
|||
// onDataAvailable, instead, it passive wait the client side pull encoded data
|
||||
// by calling requestData API.
|
||||
const int32_t mTimeSlice;
|
||||
// Indicate this session's stop has been called.
|
||||
bool mStopIssued;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(MediaRecorder::Session, nsIObserver)
|
||||
|
|
|
@ -1116,6 +1116,28 @@ MediaStreamGraphImpl::AllFinishedStreamsNotified()
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MediaStreamGraphImpl::PauseAllAudioOutputs()
|
||||
{
|
||||
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
|
||||
MediaStream* s = mStreams[i];
|
||||
for (uint32_t j = 0; j < s->mAudioOutputStreams.Length(); ++j) {
|
||||
s->mAudioOutputStreams[j].mStream->Pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaStreamGraphImpl::ResumeAllAudioOutputs()
|
||||
{
|
||||
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
|
||||
MediaStream* s = mStreams[i];
|
||||
for (uint32_t j = 0; j < s->mAudioOutputStreams.Length(); ++j) {
|
||||
s->mAudioOutputStreams[j].mStream->Resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MediaStreamGraphImpl::RunThread()
|
||||
{
|
||||
|
@ -1180,7 +1202,6 @@ MediaStreamGraphImpl::RunThread()
|
|||
RecomputeBlocking(endBlockingDecisions);
|
||||
|
||||
// Play stream contents.
|
||||
uint32_t audioStreamsActive = 0;
|
||||
bool allBlockedForever = true;
|
||||
// True when we've done ProduceOutput for all processed streams.
|
||||
bool doneAllProducing = false;
|
||||
|
@ -1221,7 +1242,6 @@ MediaStreamGraphImpl::RunThread()
|
|||
// Only playback audio and video in real-time mode
|
||||
CreateOrDestroyAudioStreams(prevComputedTime, stream);
|
||||
PlayAudio(stream, prevComputedTime, mStateComputedTime);
|
||||
audioStreamsActive += stream->mAudioOutputStreams.Length();
|
||||
PlayVideo(stream);
|
||||
}
|
||||
SourceMediaStream* is = stream->AsSourceStream();
|
||||
|
@ -1233,7 +1253,7 @@ MediaStreamGraphImpl::RunThread()
|
|||
allBlockedForever = false;
|
||||
}
|
||||
}
|
||||
if (ensureNextIteration || !allBlockedForever || audioStreamsActive > 0) {
|
||||
if (ensureNextIteration || !allBlockedForever) {
|
||||
EnsureNextIteration();
|
||||
}
|
||||
|
||||
|
@ -1261,6 +1281,7 @@ MediaStreamGraphImpl::RunThread()
|
|||
if (mRealtime) {
|
||||
PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
bool pausedOutputs = false;
|
||||
if (mNeedAnotherIteration) {
|
||||
int64_t timeoutMS = MEDIA_GRAPH_TARGET_PERIOD_MS -
|
||||
int64_t((now - mCurrentTimeStamp).ToMilliseconds());
|
||||
|
@ -1273,6 +1294,8 @@ MediaStreamGraphImpl::RunThread()
|
|||
mWaitState = WAITSTATE_WAITING_FOR_NEXT_ITERATION;
|
||||
} else {
|
||||
mWaitState = WAITSTATE_WAITING_INDEFINITELY;
|
||||
PauseAllAudioOutputs();
|
||||
pausedOutputs = true;
|
||||
}
|
||||
if (timeout > 0) {
|
||||
mMonitor.Wait(timeout);
|
||||
|
@ -1280,6 +1303,9 @@ MediaStreamGraphImpl::RunThread()
|
|||
(TimeStamp::Now() - mInitialTimeStamp).ToSeconds(),
|
||||
(TimeStamp::Now() - now).ToSeconds()));
|
||||
}
|
||||
if (pausedOutputs) {
|
||||
ResumeAllAudioOutputs();
|
||||
}
|
||||
}
|
||||
mWaitState = WAITSTATE_RUNNING;
|
||||
mNeedAnotherIteration = false;
|
||||
|
@ -1531,7 +1557,8 @@ MediaStreamGraphImpl::RunInStableState()
|
|||
}
|
||||
}
|
||||
|
||||
if (mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
|
||||
if (mForceShutDown &&
|
||||
mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
|
||||
// Defer calls to RunDuringShutdown() to happen while mMonitor is not held.
|
||||
for (uint32_t i = 0; i < mMessageQueue.Length(); ++i) {
|
||||
MessageBlock& mb = mMessageQueue[i];
|
||||
|
|
|
@ -370,6 +370,14 @@ public:
|
|||
{
|
||||
mStreamOrderDirty = true;
|
||||
}
|
||||
/**
|
||||
* Pause all AudioStreams being written to by MediaStreams
|
||||
*/
|
||||
void PauseAllAudioOutputs();
|
||||
/**
|
||||
* Resume all AudioStreams being written to by MediaStreams
|
||||
*/
|
||||
void ResumeAllAudioOutputs();
|
||||
|
||||
// Data members
|
||||
|
||||
|
|
|
@ -440,6 +440,32 @@ RtspMediaResource::OnMediaDataAvailable(uint8_t aTrackIdx,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// Bug 962309 - Video RTSP support should be disabled in 1.3
|
||||
bool
|
||||
RtspMediaResource::IsVideoEnabled()
|
||||
{
|
||||
return Preferences::GetBool("media.rtsp.video.enabled", false);
|
||||
}
|
||||
|
||||
bool
|
||||
RtspMediaResource::IsVideo(uint8_t tracks, nsIStreamingProtocolMetaData *meta)
|
||||
{
|
||||
bool isVideo = false;
|
||||
for (int i = 0; i < tracks; ++i) {
|
||||
nsCOMPtr<nsIStreamingProtocolMetaData> trackMeta;
|
||||
mMediaStreamController->GetTrackMetaData(i, getter_AddRefs(trackMeta));
|
||||
MOZ_ASSERT(trackMeta);
|
||||
uint32_t w = 0, h = 0;
|
||||
trackMeta->GetWidth(&w);
|
||||
trackMeta->GetHeight(&h);
|
||||
if (w > 0 || h > 0) {
|
||||
isVideo = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isVideo;
|
||||
}
|
||||
|
||||
nsresult
|
||||
RtspMediaResource::OnConnected(uint8_t aTrackIdx,
|
||||
nsIStreamingProtocolMetaData *meta)
|
||||
|
@ -453,6 +479,16 @@ RtspMediaResource::OnConnected(uint8_t aTrackIdx,
|
|||
|
||||
uint8_t tracks;
|
||||
mMediaStreamController->GetTotalTracks(&tracks);
|
||||
|
||||
// If the preference of RTSP video feature is not enabled and the streaming is
|
||||
// video, we give up moving forward.
|
||||
if (!IsVideoEnabled() && IsVideo(tracks, meta)) {
|
||||
// Give up, report error to media element.
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
|
||||
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
uint64_t duration = 0;
|
||||
for (int i = 0; i < tracks; ++i) {
|
||||
nsCString rtspTrackId("RtspTrack");
|
||||
|
@ -540,10 +576,9 @@ RtspMediaResource::OnDisconnected(uint8_t aTrackIdx, nsresult aReason)
|
|||
|
||||
if (aReason == NS_ERROR_NOT_INITIALIZED ||
|
||||
aReason == NS_ERROR_CONNECTION_REFUSED ||
|
||||
aReason == NS_ERROR_NOT_CONNECTED) {
|
||||
|
||||
aReason == NS_ERROR_NOT_CONNECTED ||
|
||||
aReason == NS_ERROR_NET_TIMEOUT) {
|
||||
RTSPMLOG("Error in OnDisconnected 0x%x", aReason);
|
||||
|
||||
mDecoder->NetworkError();
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -216,6 +216,8 @@ protected:
|
|||
nsRefPtr<Listener> mListener;
|
||||
|
||||
private:
|
||||
bool IsVideoEnabled();
|
||||
bool IsVideo(uint8_t tracks, nsIStreamingProtocolMetaData *meta);
|
||||
// These two members are created at |RtspMediaResource::OnConnected|.
|
||||
nsCOMPtr<nsIStreamingProtocolController> mMediaStreamController;
|
||||
nsTArray<nsAutoPtr<RtspTrackBuffer>> mTrackBuffer;
|
||||
|
|
|
@ -89,6 +89,7 @@ public:
|
|||
}
|
||||
|
||||
mStartTime = aStartTime;
|
||||
mReset = true;
|
||||
CueChanged();
|
||||
}
|
||||
|
||||
|
@ -104,6 +105,7 @@ public:
|
|||
}
|
||||
|
||||
mEndTime = aEndTime;
|
||||
mReset = true;
|
||||
CueChanged();
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,9 @@ protected:
|
|||
* The implementation records a mCurrent (the value at the current time)
|
||||
* and an array of "change times" (greater than the current time) and the
|
||||
* new value for each change time. This is a simple but dumb implementation.
|
||||
* We maintain the invariant that each change entry in the array must have
|
||||
* a different value to the value in the previous change entry (or, for
|
||||
* the first change entry, mCurrent).
|
||||
*/
|
||||
template <typename Time, typename T, uint32_t ReservedChanges>
|
||||
class TimeVarying : public TimeVaryingBase {
|
||||
|
@ -79,6 +82,9 @@ public:
|
|||
}
|
||||
mChanges.RemoveElementAt(i);
|
||||
}
|
||||
if (mCurrent == aValue) {
|
||||
return;
|
||||
}
|
||||
mChanges.InsertElementAt(0, Entry(aTime, aValue));
|
||||
}
|
||||
/**
|
||||
|
|
|
@ -24,8 +24,8 @@ public:
|
|||
virtual ~ContainerWriter() {}
|
||||
// Mapping to DOMLocalMediaStream::TrackTypeHints
|
||||
enum {
|
||||
HAS_AUDIO = 1 << 0,
|
||||
HAS_VIDEO = 1 << 1,
|
||||
CREATE_AUDIO_TRACK = 1 << 0,
|
||||
CREATE_VIDEO_TRACK = 1 << 1,
|
||||
};
|
||||
enum {
|
||||
END_OF_STREAM = 1 << 0
|
||||
|
|
|
@ -56,14 +56,25 @@ public:
|
|||
AVC_CSD, // AVC codec specific data
|
||||
UNKNOW // FrameType not set
|
||||
};
|
||||
nsresult SwapInFrameData(nsTArray<uint8_t>& aData)
|
||||
{
|
||||
mFrameData.SwapElements(aData);
|
||||
return NS_OK;
|
||||
}
|
||||
nsresult SwapOutFrameData(nsTArray<uint8_t>& aData)
|
||||
{
|
||||
if (mFrameType != UNKNOW) {
|
||||
// Reset this frame type to UNKNOW once the data is swapped out.
|
||||
mFrameData.SwapElements(aData);
|
||||
mFrameType = UNKNOW;
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
const nsTArray<uint8_t>& GetFrameData() const
|
||||
{
|
||||
return mFrameData;
|
||||
}
|
||||
void SetFrameData(nsTArray<uint8_t> *aData)
|
||||
{
|
||||
mFrameData.SwapElements(*aData);
|
||||
}
|
||||
uint64_t GetTimeStamp() const { return mTimeStamp; }
|
||||
void SetTimeStamp(uint64_t aTimeStamp) { mTimeStamp = aTimeStamp; }
|
||||
|
||||
|
|
|
@ -100,8 +100,8 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes)
|
|||
#ifdef MOZ_WEBM_ENCODER
|
||||
else if (MediaEncoder::IsWebMEncoderEnabled() &&
|
||||
(aMIMEType.EqualsLiteral(VIDEO_WEBM) ||
|
||||
(aTrackTypes & ContainerWriter::HAS_VIDEO))) {
|
||||
if (aTrackTypes & ContainerWriter::HAS_AUDIO) {
|
||||
(aTrackTypes & ContainerWriter::CREATE_VIDEO_TRACK))) {
|
||||
if (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK) {
|
||||
audioEncoder = new VorbisTrackEncoder();
|
||||
NS_ENSURE_TRUE(audioEncoder, nullptr);
|
||||
}
|
||||
|
@ -115,8 +115,8 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes)
|
|||
#ifdef MOZ_OMX_ENCODER
|
||||
else if (MediaEncoder::IsOMXEncoderEnabled() &&
|
||||
(aMIMEType.EqualsLiteral(VIDEO_MP4) ||
|
||||
(aTrackTypes & ContainerWriter::HAS_VIDEO))) {
|
||||
if (aTrackTypes & ContainerWriter::HAS_AUDIO) {
|
||||
(aTrackTypes & ContainerWriter::CREATE_VIDEO_TRACK))) {
|
||||
if (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK) {
|
||||
audioEncoder = new OmxAudioTrackEncoder();
|
||||
NS_ENSURE_TRUE(audioEncoder, nullptr);
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes)
|
|||
#ifdef MOZ_OGG
|
||||
else if (MediaDecoder::IsOggEnabled() && MediaDecoder::IsOpusEnabled() &&
|
||||
(aMIMEType.EqualsLiteral(AUDIO_OGG) ||
|
||||
(aTrackTypes & ContainerWriter::HAS_AUDIO))) {
|
||||
(aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK))) {
|
||||
writer = new OggWriter();
|
||||
audioEncoder = new OpusTrackEncoder();
|
||||
NS_ENSURE_TRUE(writer, nullptr);
|
||||
|
|
|
@ -93,7 +93,7 @@ public :
|
|||
* Ogg+Opus if it is empty.
|
||||
*/
|
||||
static already_AddRefed<MediaEncoder> CreateEncoder(const nsAString& aMIMEType,
|
||||
uint8_t aTrackTypes = ContainerWriter::HAS_AUDIO);
|
||||
uint8_t aTrackTypes = ContainerWriter::CREATE_AUDIO_TRACK);
|
||||
/**
|
||||
* Encodes the raw track data and returns the final container data. Assuming
|
||||
* it is called on a single worker thread. The buffer of container data is
|
||||
|
|
|
@ -121,6 +121,7 @@ OmxVideoTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
|
|||
}
|
||||
|
||||
// Dequeue an encoded frame from the output buffers of OMXCodecWrapper.
|
||||
nsresult rv;
|
||||
nsTArray<uint8_t> buffer;
|
||||
int outFlags = 0;
|
||||
int64_t outTimeStampUs = 0;
|
||||
|
@ -134,7 +135,8 @@ OmxVideoTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
|
|||
videoData->SetFrameType((outFlags & OMXCodecWrapper::BUFFER_SYNC_FRAME) ?
|
||||
EncodedFrame::I_FRAME : EncodedFrame::P_FRAME);
|
||||
}
|
||||
videoData->SetFrameData(&buffer);
|
||||
rv = videoData->SwapInFrameData(buffer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
videoData->SetTimeStamp(outTimeStampUs);
|
||||
aData.AppendEncodedFrame(videoData);
|
||||
}
|
||||
|
@ -213,7 +215,8 @@ OmxAudioTrackEncoder::AppendEncodedFrames(EncodedFrameContainer& aContainer)
|
|||
audiodata->SetFrameType(isCSD ?
|
||||
EncodedFrame::AAC_CSD : EncodedFrame::AUDIO_FRAME);
|
||||
audiodata->SetTimeStamp(outTimeUs);
|
||||
audiodata->SetFrameData(&frameData);
|
||||
rv = audiodata->SwapInFrameData(frameData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
aContainer.AppendEncodedFrame(audiodata);
|
||||
}
|
||||
|
||||
|
|
|
@ -374,7 +374,7 @@ OpusTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
|
|||
}
|
||||
}
|
||||
|
||||
audiodata->SetFrameData(&frameData);
|
||||
audiodata->SwapInFrameData(frameData);
|
||||
aData.AppendEncodedFrame(audiodata);
|
||||
return result >= 0 ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -164,6 +164,7 @@ VP8TrackEncoder::GetEncodedPartitions(EncodedFrameContainer& aData)
|
|||
vpx_codec_iter_t iter = nullptr;
|
||||
EncodedFrame::FrameType frameType = EncodedFrame::P_FRAME;
|
||||
nsTArray<uint8_t> frameData;
|
||||
nsresult rv;
|
||||
const vpx_codec_cx_pkt_t *pkt = nullptr;
|
||||
while ((pkt = vpx_codec_get_cx_data(mVPXContext, &iter)) != nullptr) {
|
||||
switch (pkt->kind) {
|
||||
|
@ -202,7 +203,8 @@ VP8TrackEncoder::GetEncodedPartitions(EncodedFrameContainer& aData)
|
|||
videoData->SetDuration(
|
||||
(uint64_t)FramesToUsecs(pkt->data.frame.duration, mTrackRate).value());
|
||||
}
|
||||
videoData->SetFrameData(&frameData);
|
||||
rv = videoData->SwapInFrameData(frameData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
VP8LOG("GetEncodedPartitions TimeStamp %lld Duration %lld\n",
|
||||
videoData->GetTimeStamp(), videoData->GetDuration());
|
||||
VP8LOG("frameType %d\n", videoData->GetFrameType());
|
||||
|
|
|
@ -147,7 +147,7 @@ VorbisTrackEncoder::GetEncodedFrames(EncodedFrameContainer& aData)
|
|||
audiodata->SetFrameType(EncodedFrame::AUDIO_FRAME);
|
||||
nsTArray<uint8_t> frameData;
|
||||
frameData.AppendElements(oggPacket.packet, oggPacket.bytes);
|
||||
audiodata->SetFrameData(&frameData);
|
||||
audiodata->SwapInFrameData(frameData);
|
||||
aData.AppendEncodedFrame(audiodata);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,10 +28,10 @@ ISOMediaWriter::ISOMediaWriter(uint32_t aType)
|
|||
, mBlobReady(false)
|
||||
, mType(0)
|
||||
{
|
||||
if (aType & HAS_AUDIO) {
|
||||
if (aType & CREATE_AUDIO_TRACK) {
|
||||
mType |= Audio_Track;
|
||||
}
|
||||
if (aType & HAS_VIDEO) {
|
||||
if (aType & CREATE_VIDEO_TRACK) {
|
||||
mType |= Video_Track;
|
||||
}
|
||||
mControl = new ISOControl();
|
||||
|
|
|
@ -256,6 +256,7 @@ support-files =
|
|||
[test_mediarecorder_reload_crash.html]
|
||||
[test_mediarecorder_record_immediate_stop.html]
|
||||
[test_mediarecorder_record_session.html]
|
||||
[test_mediarecorder_record_startstopstart.html]
|
||||
[test_mediarecorder_unsupported_src.html]
|
||||
[test_playback.html]
|
||||
[test_seekLies.html]
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test MediaRecorder crash on sequence start stop start method</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
function startTest() {
|
||||
var ac = new window.AudioContext();
|
||||
var dest = ac.createMediaStreamDestination();
|
||||
var recorder = new MediaRecorder(dest.stream);
|
||||
var stopCount = 0;
|
||||
var dataavailable = 0;
|
||||
// mobile device may produce another format, but not landed.
|
||||
// In audio only case, we should produce opus type.
|
||||
var expectedMimeType = 'audio/ogg';
|
||||
recorder.onstop = function (e) {
|
||||
info('onstop fired');
|
||||
is(recorder.stream, dest.stream,
|
||||
'Media recorder stream = element stream post recording');
|
||||
stopCount++;
|
||||
if (stopCount == 2) {
|
||||
is(2, dataavailable, 'Should has two dataavailable event');
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
recorder.ondataavailable = function (evt) {
|
||||
info('ondataavailable fired');
|
||||
ok(evt instanceof BlobEvent,
|
||||
'Events fired from ondataavailable should be BlobEvent');
|
||||
is(evt.type, 'dataavailable',
|
||||
'Event type should dataavailable');
|
||||
// If script runs slower, it may generate header data in blob from encoder
|
||||
if (evt.data.size > 0) {
|
||||
info('blob size = ' + evt.data.size);
|
||||
is(evt.data.type, expectedMimeType,
|
||||
'Blob data received should have type = ' + expectedMimeType);
|
||||
} else {
|
||||
is(evt.data.type, '',
|
||||
'Blob data received should have empty type');
|
||||
}
|
||||
dataavailable++;
|
||||
}
|
||||
recorder.onerror = function (e) {
|
||||
ok(false, 'it should execute normally without exception');
|
||||
}
|
||||
recorder.onwarning = function() {
|
||||
ok(false, 'onwarning unexpectedly fired');
|
||||
};
|
||||
|
||||
recorder.start(2000);
|
||||
is(recorder.state, 'recording', 'Media recorder should be recording');
|
||||
recorder.stop();
|
||||
is(recorder.state, 'inactive', 'check recording status is inactive');
|
||||
recorder.start(10000); // This bug would crash on this line without this fix.
|
||||
is(recorder.state, 'recording', 'check recording status is recording');
|
||||
// Simulate delay stop, only delay stop no no stop can trigger crash.
|
||||
setTimeout(function() {
|
||||
recorder.stop();
|
||||
is(recorder.state, 'inactive','check recording status is recording');
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
startTest();
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -532,7 +532,7 @@ AudioBufferSourceNode::Start(double aWhen, double aOffset,
|
|||
|
||||
// Don't set parameter unnecessarily
|
||||
if (aWhen > 0.0) {
|
||||
ns->SetStreamTimeParameter(START, Context()->DestinationStream(), aWhen);
|
||||
ns->SetStreamTimeParameter(START, Context(), aWhen);
|
||||
}
|
||||
|
||||
MarkActive();
|
||||
|
@ -616,8 +616,7 @@ AudioBufferSourceNode::Stop(double aWhen, ErrorResult& aRv)
|
|||
return;
|
||||
}
|
||||
|
||||
ns->SetStreamTimeParameter(STOP, Context()->DestinationStream(),
|
||||
std::max(0.0, aWhen));
|
||||
ns->SetStreamTimeParameter(STOP, Context(), std::max(0.0, aWhen));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -84,6 +84,7 @@ AudioContext::AudioContext(nsPIDOMWindow* aWindow,
|
|||
: nsDOMEventTargetHelper(aWindow)
|
||||
, mSampleRate(GetSampleRateForAudioContext(aIsOffline, aSampleRate))
|
||||
, mNumberOfChannels(aNumberOfChannels)
|
||||
, mNodeCount(0)
|
||||
, mIsOffline(aIsOffline)
|
||||
, mIsStarted(!aIsOffline)
|
||||
, mIsShutDown(false)
|
||||
|
@ -95,6 +96,10 @@ AudioContext::AudioContext(nsPIDOMWindow* aWindow,
|
|||
mDestination = new AudioDestinationNode(this, aIsOffline, aNumberOfChannels,
|
||||
aLength, aSampleRate);
|
||||
mDestination->Stream()->AddAudioOutput(&gWebAudioOutputKey);
|
||||
// We skip calling SetIsOnlyNodeForContext during mDestination's constructor,
|
||||
// because we can only call SetIsOnlyNodeForContext after mDestination has
|
||||
// been set up.
|
||||
mDestination->SetIsOnlyNodeForContext(true);
|
||||
}
|
||||
|
||||
AudioContext::~AudioContext()
|
||||
|
@ -543,7 +548,8 @@ AudioContext::DestinationStream() const
|
|||
double
|
||||
AudioContext::CurrentTime() const
|
||||
{
|
||||
return MediaTimeToSeconds(Destination()->Stream()->GetCurrentTime());
|
||||
return MediaTimeToSeconds(Destination()->Stream()->GetCurrentTime()) +
|
||||
ExtraCurrentTime();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -589,6 +595,18 @@ AudioContext::Resume()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioContext::UpdateNodeCount(int32_t aDelta)
|
||||
{
|
||||
bool firstNode = mNodeCount == 0;
|
||||
mNodeCount += aDelta;
|
||||
MOZ_ASSERT(mNodeCount >= 0);
|
||||
// mDestinationNode may be null when we're destroying nodes unlinked by CC
|
||||
if (!firstNode && mDestination) {
|
||||
mDestination->SetIsOnlyNodeForContext(mNodeCount == 1);
|
||||
}
|
||||
}
|
||||
|
||||
JSContext*
|
||||
AudioContext::GetJSContext() const
|
||||
{
|
||||
|
@ -679,5 +697,11 @@ AudioContext::CollectReports(nsIHandleReportCallback* aHandleReport,
|
|||
amount, "Memory used by AudioContext objects (Web Audio).");
|
||||
}
|
||||
|
||||
double
|
||||
AudioContext::ExtraCurrentTime() const
|
||||
{
|
||||
return mDestination->ExtraCurrentTime();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -246,7 +246,23 @@ public:
|
|||
AudioChannel MozAudioChannelType() const;
|
||||
void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
|
||||
|
||||
void UpdateNodeCount(int32_t aDelta);
|
||||
|
||||
double DOMTimeToStreamTime(double aTime) const
|
||||
{
|
||||
return aTime - ExtraCurrentTime();
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns the amount of extra time added to the current time of the
|
||||
* AudioDestinationNode's MediaStream to get this AudioContext's currentTime.
|
||||
* Must be subtracted from all DOM API parameter times that are on the same
|
||||
* timeline as AudioContext's currentTime to get times we can pass to the
|
||||
* MediaStreamGraph.
|
||||
*/
|
||||
double ExtraCurrentTime() const;
|
||||
|
||||
void RemoveFromDecodeQueue(WebAudioDecodeJob* aDecodeJob);
|
||||
void ShutdownDecoder();
|
||||
|
||||
|
@ -272,6 +288,8 @@ private:
|
|||
nsTHashtable<nsPtrHashKey<PannerNode> > mPannerNodes;
|
||||
// Number of channels passed in the OfflineAudioContext ctor.
|
||||
uint32_t mNumberOfChannels;
|
||||
// Number of nodes that currently exist for this AudioContext
|
||||
int32_t mNodeCount;
|
||||
bool mIsOffline;
|
||||
bool mIsStarted;
|
||||
bool mIsShutDown;
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include "nsIPermissionManager.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsIAppShell.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -217,6 +219,9 @@ AudioDestinationNode::AudioDestinationNode(AudioContext* aContext,
|
|||
, mAudioChannel(AudioChannel::Normal)
|
||||
, mIsOffline(aIsOffline)
|
||||
, mHasFinished(false)
|
||||
, mExtraCurrentTime(0)
|
||||
, mExtraCurrentTimeSinceLastStartedBlocking(0)
|
||||
, mExtraCurrentTimeUpdatedSinceLastStableState(false)
|
||||
{
|
||||
MediaStreamGraph* graph = aIsOffline ?
|
||||
MediaStreamGraph::CreateNonRealtimeInstance() :
|
||||
|
@ -486,6 +491,76 @@ AudioDestinationNode::CreateAudioChannelAgent()
|
|||
mAudioChannelAgent->StartPlaying(&state);
|
||||
SetCanPlay(state == AudioChannelState::AUDIO_CHANNEL_STATE_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
AudioDestinationNode::NotifyStableState()
|
||||
{
|
||||
mExtraCurrentTimeUpdatedSinceLastStableState = false;
|
||||
}
|
||||
|
||||
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
|
||||
|
||||
void
|
||||
AudioDestinationNode::ScheduleStableStateNotification()
|
||||
{
|
||||
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
|
||||
if (appShell) {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &AudioDestinationNode::NotifyStableState);
|
||||
appShell->RunInStableState(event);
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
AudioDestinationNode::ExtraCurrentTime()
|
||||
{
|
||||
if (!mStartedBlockingDueToBeingOnlyNode.IsNull() &&
|
||||
!mExtraCurrentTimeUpdatedSinceLastStableState) {
|
||||
mExtraCurrentTimeUpdatedSinceLastStableState = true;
|
||||
mExtraCurrentTimeSinceLastStartedBlocking =
|
||||
(TimeStamp::Now() - mStartedBlockingDueToBeingOnlyNode).ToSeconds();
|
||||
ScheduleStableStateNotification();
|
||||
}
|
||||
return mExtraCurrentTime + mExtraCurrentTimeSinceLastStartedBlocking;
|
||||
}
|
||||
|
||||
void
|
||||
AudioDestinationNode::SetIsOnlyNodeForContext(bool aIsOnlyNode)
|
||||
{
|
||||
if (!mStartedBlockingDueToBeingOnlyNode.IsNull() == aIsOnlyNode) {
|
||||
// Nothing changed.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mStream) {
|
||||
// DestroyMediaStream has been called, presumably during CC Unlink().
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIsOffline) {
|
||||
// Don't block the destination stream for offline AudioContexts, since
|
||||
// we expect the zero data produced when there are no other nodes to
|
||||
// show up in its result buffer. Also, we would get confused by adding
|
||||
// ExtraCurrentTime before StartRendering has even been called.
|
||||
return;
|
||||
}
|
||||
|
||||
if (aIsOnlyNode) {
|
||||
mStream->ChangeExplicitBlockerCount(1);
|
||||
mStartedBlockingDueToBeingOnlyNode = TimeStamp::Now();
|
||||
mExtraCurrentTimeSinceLastStartedBlocking = 0;
|
||||
// Don't do an update of mExtraCurrentTimeSinceLastStartedBlocking until the next stable state.
|
||||
mExtraCurrentTimeUpdatedSinceLastStableState = true;
|
||||
ScheduleStableStateNotification();
|
||||
} else {
|
||||
// Force update of mExtraCurrentTimeSinceLastStartedBlocking if necessary
|
||||
ExtraCurrentTime();
|
||||
mExtraCurrentTime += mExtraCurrentTimeSinceLastStartedBlocking;
|
||||
mStream->ChangeExplicitBlockerCount(-1);
|
||||
mStartedBlockingDueToBeingOnlyNode = TimeStamp();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -70,12 +70,22 @@ public:
|
|||
virtual void NotifyMainThreadStateChanged() MOZ_OVERRIDE;
|
||||
void FireOfflineCompletionEvent();
|
||||
|
||||
// An amount that should be added to the MediaStream's current time to
|
||||
// get the AudioContext.currentTime.
|
||||
double ExtraCurrentTime();
|
||||
|
||||
// When aIsOnlyNode is true, this is the only node for the AudioContext.
|
||||
void SetIsOnlyNodeForContext(bool aIsOnlyNode);
|
||||
|
||||
private:
|
||||
bool CheckAudioChannelPermissions(AudioChannel aValue);
|
||||
void CreateAudioChannelAgent();
|
||||
|
||||
void SetCanPlay(bool aCanPlay);
|
||||
|
||||
void NotifyStableState();
|
||||
void ScheduleStableStateNotification();
|
||||
|
||||
SelfReference<AudioDestinationNode> mOfflineRenderingRef;
|
||||
uint32_t mFramesToProduce;
|
||||
|
||||
|
@ -85,6 +95,11 @@ private:
|
|||
AudioChannel mAudioChannel;
|
||||
bool mIsOffline;
|
||||
bool mHasFinished;
|
||||
|
||||
TimeStamp mStartedBlockingDueToBeingOnlyNode;
|
||||
double mExtraCurrentTime;
|
||||
double mExtraCurrentTimeSinceLastStartedBlocking;
|
||||
bool mExtraCurrentTimeUpdatedSinceLastStableState;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(AudioNode)
|
|||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(AudioNode, nsDOMEventTargetHelper)
|
||||
tmp->DisconnectFromGraph();
|
||||
tmp->mContext->UpdateNodeCount(-1);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputNodes)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputParams)
|
||||
|
@ -58,6 +59,9 @@ AudioNode::AudioNode(AudioContext* aContext,
|
|||
, mChannelInterpretation(aChannelInterpretation)
|
||||
{
|
||||
MOZ_ASSERT(aContext);
|
||||
nsDOMEventTargetHelper::BindToOwner(aContext->GetParentObject());
|
||||
SetIsDOMBinding();
|
||||
aContext->UpdateNodeCount(1);
|
||||
}
|
||||
|
||||
AudioNode::~AudioNode()
|
||||
|
@ -65,6 +69,9 @@ AudioNode::~AudioNode()
|
|||
MOZ_ASSERT(mInputNodes.IsEmpty());
|
||||
MOZ_ASSERT(mOutputNodes.IsEmpty());
|
||||
MOZ_ASSERT(mOutputParams.IsEmpty());
|
||||
if (mContext) {
|
||||
mContext->UpdateNodeCount(-1);
|
||||
}
|
||||
}
|
||||
|
||||
template <class InputNode>
|
||||
|
|
|
@ -42,6 +42,11 @@ public:
|
|||
return mNode->Context();
|
||||
}
|
||||
|
||||
double DOMTimeToStreamTime(double aTime) const
|
||||
{
|
||||
return mNode->Context()->DOMTimeToStreamTime(aTime);
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
|
||||
|
@ -54,7 +59,7 @@ public:
|
|||
return;
|
||||
}
|
||||
AudioParamTimeline::SetValueCurveAtTime(aValues.Data(), aValues.Length(),
|
||||
aStartTime, aDuration, aRv);
|
||||
DOMTimeToStreamTime(aStartTime), aDuration, aRv);
|
||||
mCallback(mNode);
|
||||
}
|
||||
|
||||
|
@ -76,7 +81,7 @@ public:
|
|||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return;
|
||||
}
|
||||
AudioParamTimeline::SetValueAtTime(aValue, aStartTime, aRv);
|
||||
AudioParamTimeline::SetValueAtTime(aValue, DOMTimeToStreamTime(aStartTime), aRv);
|
||||
mCallback(mNode);
|
||||
}
|
||||
void LinearRampToValueAtTime(float aValue, double aEndTime, ErrorResult& aRv)
|
||||
|
@ -85,7 +90,7 @@ public:
|
|||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return;
|
||||
}
|
||||
AudioParamTimeline::LinearRampToValueAtTime(aValue, aEndTime, aRv);
|
||||
AudioParamTimeline::LinearRampToValueAtTime(aValue, DOMTimeToStreamTime(aEndTime), aRv);
|
||||
mCallback(mNode);
|
||||
}
|
||||
void ExponentialRampToValueAtTime(float aValue, double aEndTime, ErrorResult& aRv)
|
||||
|
@ -94,7 +99,7 @@ public:
|
|||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return;
|
||||
}
|
||||
AudioParamTimeline::ExponentialRampToValueAtTime(aValue, aEndTime, aRv);
|
||||
AudioParamTimeline::ExponentialRampToValueAtTime(aValue, DOMTimeToStreamTime(aEndTime), aRv);
|
||||
mCallback(mNode);
|
||||
}
|
||||
void SetTargetAtTime(float aTarget, double aStartTime, double aTimeConstant, ErrorResult& aRv)
|
||||
|
@ -104,7 +109,7 @@ public:
|
|||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return;
|
||||
}
|
||||
AudioParamTimeline::SetTargetAtTime(aTarget, aStartTime, aTimeConstant, aRv);
|
||||
AudioParamTimeline::SetTargetAtTime(aTarget, DOMTimeToStreamTime(aStartTime), aTimeConstant, aRv);
|
||||
mCallback(mNode);
|
||||
}
|
||||
void SetTargetValueAtTime(float aTarget, double aStartTime, double aTimeConstant, ErrorResult& aRv)
|
||||
|
@ -117,7 +122,7 @@ public:
|
|||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return;
|
||||
}
|
||||
AudioParamTimeline::CancelScheduledValues(aStartTime);
|
||||
AudioParamTimeline::CancelScheduledValues(DOMTimeToStreamTime(aStartTime));
|
||||
mCallback(mNode);
|
||||
}
|
||||
|
||||
|
|
|
@ -578,8 +578,7 @@ OscillatorNode::Start(double aWhen, ErrorResult& aRv)
|
|||
|
||||
// TODO: Perhaps we need to do more here.
|
||||
ns->SetStreamTimeParameter(OscillatorNodeEngine::START,
|
||||
Context()->DestinationStream(),
|
||||
aWhen);
|
||||
Context(), aWhen);
|
||||
|
||||
MarkActive();
|
||||
}
|
||||
|
@ -605,8 +604,7 @@ OscillatorNode::Stop(double aWhen, ErrorResult& aRv)
|
|||
|
||||
// TODO: Perhaps we need to do more here.
|
||||
ns->SetStreamTimeParameter(OscillatorNodeEngine::STOP,
|
||||
Context()->DestinationStream(),
|
||||
std::max(0.0, aWhen));
|
||||
Context(), std::max(0.0, aWhen));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -9,6 +9,7 @@ PARALLEL_DIRS += ['blink', 'test']
|
|||
TEST_TOOL_DIRS += ['compiledtest']
|
||||
|
||||
EXPORTS += [
|
||||
'AudioContext.h',
|
||||
'AudioParamTimeline.h',
|
||||
'MediaBufferDecoder.h',
|
||||
'ThreeDPoint.h',
|
||||
|
|
|
@ -53,10 +53,11 @@ function compareChannels(buf1, buf2,
|
|||
}
|
||||
};
|
||||
|
||||
is(difference, 0, "Found " + difference + " different samples, maxDifference: " +
|
||||
maxDifference + ", first bad index: " + firstBadIndex +
|
||||
" with source offset " + sourceOffset + " and destination offset " +
|
||||
destOffset);
|
||||
is(difference, 0, "maxDifference: " + maxDifference +
|
||||
", first bad index: " + firstBadIndex +
|
||||
" with test-data offset " + sourceOffset + " and expected-data offset " +
|
||||
destOffset + "; corresponding values " + buf1[firstBadIndex + sourceOffset] +
|
||||
" and " + buf2[firstBadIndex + destOffset] + " --- differences");
|
||||
}
|
||||
|
||||
function compareBuffers(got, expected) {
|
||||
|
|
|
@ -44,7 +44,7 @@ WebMWriter::SetMetadata(TrackMetadataBase* aMetadata)
|
|||
MOZ_ASSERT(meta, "Cannot find vp8 encoder metadata");
|
||||
mEbmlComposer->SetVideoConfig(meta->mWidth, meta->mHeight,
|
||||
meta->mEncodedFrameRate);
|
||||
mMetadataRequiredFlag = mMetadataRequiredFlag & ~ContainerWriter::HAS_VIDEO;
|
||||
mMetadataRequiredFlag = mMetadataRequiredFlag & ~ContainerWriter::CREATE_VIDEO_TRACK;
|
||||
}
|
||||
|
||||
if (aMetadata->GetKind() == TrackMetadataBase::METADATA_VORBIS) {
|
||||
|
@ -52,7 +52,7 @@ WebMWriter::SetMetadata(TrackMetadataBase* aMetadata)
|
|||
MOZ_ASSERT(meta, "Cannot find vorbis encoder metadata");
|
||||
mEbmlComposer->SetAudioConfig(meta->mSamplingFrequency, meta->mChannels, meta->mBitDepth);
|
||||
mEbmlComposer->SetAudioCodecPrivateData(meta->mData);
|
||||
mMetadataRequiredFlag = mMetadataRequiredFlag & ~ContainerWriter::HAS_AUDIO;
|
||||
mMetadataRequiredFlag = mMetadataRequiredFlag & ~ContainerWriter::CREATE_AUDIO_TRACK;
|
||||
}
|
||||
|
||||
if (!mMetadataRequiredFlag) {
|
||||
|
|
|
@ -8,8 +8,25 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
|
|||
* Code below is vtt.js the JS WebVTTParser.
|
||||
* Current source code can be found at http://github.com/andreasgal/vtt.js
|
||||
*
|
||||
* Code taken from commit 33a837b1ceef138a61a3b2f6fac90d5c70bd90d9
|
||||
* Code taken from commit d819872e198d051dfcebcfb7ecf260462c9a9c6f
|
||||
*/
|
||||
/**
|
||||
* Copyright 2013 vtt.js Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
(function(global) {
|
||||
|
||||
function ParsingError(message) {
|
||||
|
@ -130,7 +147,7 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
|
|||
throw new ParsingError("Malformed time stamp.");
|
||||
}
|
||||
// Remove time stamp from input.
|
||||
input = input.replace(/^[^\s-]+/, "");
|
||||
input = input.replace(/^[^\sa-zA-Z-]+/, "");
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
@ -632,13 +649,19 @@ this.EXPORTED_SYMBOLS = ["WebVTTParser"];
|
|||
(cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) {
|
||||
return cue.line;
|
||||
}
|
||||
if (!cue.track) {
|
||||
if (!cue.track || !cue.track.textTrackList ||
|
||||
!cue.track.textTrackList.mediaElement) {
|
||||
return -1;
|
||||
}
|
||||
// TODO: Have to figure out a way to determine what the position of the
|
||||
// Track is in the Media element's list of TextTracks and return that + 1,
|
||||
// negated.
|
||||
return 100;
|
||||
var track = cue.track,
|
||||
trackList = track.textTrackList,
|
||||
count = 0;
|
||||
for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
|
||||
if (trackList[i].mode === "showing") {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return ++count * -1;
|
||||
}
|
||||
|
||||
function BoundingBox() {
|
||||
|
|
|
@ -30,6 +30,8 @@ enum StructuredCloneTags {
|
|||
SCTAG_DOM_IMAGEDATA,
|
||||
SCTAG_DOM_MESSAGEPORT,
|
||||
|
||||
SCTAG_DOM_FUNCTION,
|
||||
|
||||
SCTAG_DOM_MAX
|
||||
};
|
||||
|
||||
|
|
|
@ -1630,8 +1630,8 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
|
|||
const nsDOMClassInfoData *ci_data,
|
||||
const nsGlobalNameStruct *name_struct,
|
||||
nsScriptNameSpaceManager *nameSpaceManager,
|
||||
JSObject *dot_prototype, bool install, bool *did_resolve);
|
||||
|
||||
JSObject *dot_prototype,
|
||||
JS::MutableHandle<JSPropertyDescriptor> ctorDesc);
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * aProto)
|
||||
|
@ -1727,19 +1727,30 @@ nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * aProto)
|
|||
}
|
||||
|
||||
// Don't overwrite a property set by content.
|
||||
bool found;
|
||||
bool contentDefinedProperty;
|
||||
if (!::JS_AlreadyHasOwnUCProperty(cx, global, reinterpret_cast<const jschar*>(mData->mNameUTF16),
|
||||
NS_strlen(mData->mNameUTF16), &found)) {
|
||||
NS_strlen(mData->mNameUTF16),
|
||||
&contentDefinedProperty)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
|
||||
NS_ENSURE_TRUE(nameSpaceManager, NS_OK);
|
||||
|
||||
bool unused;
|
||||
return ResolvePrototype(sXPConnect, win, cx, global, mData->mNameUTF16,
|
||||
mData, nullptr, nameSpaceManager, proto, !found,
|
||||
&unused);
|
||||
JS::Rooted<JSPropertyDescriptor> desc(cx);
|
||||
nsresult rv = ResolvePrototype(sXPConnect, win, cx, global, mData->mNameUTF16,
|
||||
mData, nullptr, nameSpaceManager, proto,
|
||||
&desc);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!contentDefinedProperty && desc.object() && !desc.value().isUndefined() &&
|
||||
!JS_DefineUCProperty(cx, global, mData->mNameUTF16,
|
||||
NS_strlen(mData->mNameUTF16),
|
||||
desc.value(), desc.getter(), desc.setter(),
|
||||
desc.attributes())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -2181,20 +2192,6 @@ public:
|
|||
JS::Handle<JSObject*> obj, const jsval &val, bool *bp,
|
||||
bool *_retval);
|
||||
|
||||
nsresult Install(JSContext *cx, JS::Handle<JSObject*> target,
|
||||
JS::Handle<JS::Value> aThisAsVal)
|
||||
{
|
||||
JS::Rooted<JS::Value> thisAsVal(cx, aThisAsVal);
|
||||
// The 'attrs' argument used to be JSPROP_PERMANENT. See bug 628612.
|
||||
bool ok = JS_WrapValue(cx, &thisAsVal) &&
|
||||
::JS_DefineUCProperty(cx, target,
|
||||
reinterpret_cast<const jschar *>(mClassName),
|
||||
NS_strlen(mClassName), thisAsVal, JS_PropertyStub,
|
||||
JS_StrictPropertyStub, 0);
|
||||
|
||||
return ok ? NS_OK : NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsresult ResolveInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj);
|
||||
|
||||
private:
|
||||
|
@ -2616,7 +2613,8 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
|
|||
const nsDOMClassInfoData *ci_data,
|
||||
const nsGlobalNameStruct *name_struct,
|
||||
nsScriptNameSpaceManager *nameSpaceManager,
|
||||
JSObject* aDot_prototype, bool install, bool *did_resolve)
|
||||
JSObject* aDot_prototype,
|
||||
JS::MutableHandle<JSPropertyDescriptor> ctorDesc)
|
||||
{
|
||||
JS::Rooted<JSObject*> dot_prototype(cx, aDot_prototype);
|
||||
NS_ASSERTION(ci_data ||
|
||||
|
@ -2635,9 +2633,12 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
|
|||
false, &v);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (install) {
|
||||
rv = constructor->Install(cx, obj, v);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
FillPropertyDescriptor(ctorDesc, obj, 0, v);
|
||||
// And make sure we wrap the value into the right compartment. Note that we
|
||||
// do this with ctorDesc.value(), not with v, because we need v to be in the
|
||||
// right compartment (that of the reflector of |constructor|) below.
|
||||
if (!JS_WrapValue(cx, ctorDesc.value())) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> class_obj(cx, &v.toObject());
|
||||
|
@ -2766,8 +2767,6 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
|
|||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
*did_resolve = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2800,13 +2799,20 @@ OldBindingConstructorEnabled(const nsGlobalNameStruct *aStruct,
|
|||
return true;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
LookupComponentsShim(JSContext *cx, JS::Handle<JSObject*> global,
|
||||
nsPIDOMWindow *win,
|
||||
JS::MutableHandle<JSPropertyDescriptor> desc);
|
||||
|
||||
// static
|
||||
nsresult
|
||||
nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
||||
JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
|
||||
bool *did_resolve)
|
||||
JS::MutableHandle<JSPropertyDescriptor> desc)
|
||||
{
|
||||
*did_resolve = false;
|
||||
if (id == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS)) {
|
||||
return LookupComponentsShim(cx, obj, aWin, desc);
|
||||
}
|
||||
|
||||
nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
|
||||
NS_ENSURE_TRUE(nameSpaceManager, NS_ERROR_NOT_INITIALIZED);
|
||||
|
@ -2821,6 +2827,9 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// The class_name had better match our name
|
||||
MOZ_ASSERT(name.Equals(class_name));
|
||||
|
||||
NS_ENSURE_TRUE(class_name, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
@ -2838,64 +2847,86 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
|||
name_struct->mType == nsGlobalNameStruct::eTypeClassProto ||
|
||||
name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
|
||||
// Lookup new DOM bindings.
|
||||
mozilla::dom::DefineInterface define =
|
||||
DefineInterface getOrCreateInterfaceObject =
|
||||
name_struct->mDefineDOMInterface;
|
||||
if (define) {
|
||||
if (getOrCreateInterfaceObject) {
|
||||
if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor &&
|
||||
!OldBindingConstructorEnabled(name_struct, aWin, cx)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
JS::Rooted<JSObject*> global(cx);
|
||||
bool defineOnXray = xpc::WrapperFactory::IsXrayWrapper(obj);
|
||||
if (defineOnXray) {
|
||||
// Check whether to define this property on the Xray first. This allows
|
||||
// consumers to opt in to defining on the xray even if they don't want
|
||||
// to define on the underlying global.
|
||||
if (name_struct->mConstructorEnabled &&
|
||||
!(*name_struct->mConstructorEnabled)(cx, obj)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
global = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
|
||||
if (!global) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
ac.construct(cx, global);
|
||||
} else {
|
||||
global = obj;
|
||||
}
|
||||
|
||||
// Check whether to define on the global too. Note that at this point cx
|
||||
// is in the compartment of global even if we were coming in via an Xray.
|
||||
bool defineOnGlobal = !name_struct->mConstructorEnabled ||
|
||||
(*name_struct->mConstructorEnabled)(cx, global);
|
||||
|
||||
if (!defineOnGlobal && !defineOnXray) {
|
||||
ConstructorEnabled* checkEnabledForScope = name_struct->mConstructorEnabled;
|
||||
if (checkEnabledForScope && !checkEnabledForScope(cx, obj)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
JS::Rooted<JSObject*> interfaceObject(cx, define(cx, global, id,
|
||||
defineOnGlobal));
|
||||
if (!interfaceObject) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (defineOnXray) {
|
||||
// This really should be handled by the Xray for the window.
|
||||
ac.destroy();
|
||||
if (!JS_WrapObject(cx, &interfaceObject) ||
|
||||
!JS_DefinePropertyById(cx, obj, id,
|
||||
JS::ObjectValue(*interfaceObject), JS_PropertyStub,
|
||||
JS_StrictPropertyStub, 0)) {
|
||||
// The DOM constructor resolve machinery interacts with Xrays in tricky
|
||||
// ways, and there are some asymmetries that are important to understand.
|
||||
//
|
||||
// In the regular (non-Xray) case, we only want to resolve constructors
|
||||
// once (so that if they're deleted, they don't reappear). We do this by
|
||||
// stashing the constructor in a slot on the global, such that we can see
|
||||
// during resolve whether we've created it already. This is rather
|
||||
// memory-intensive, so we don't try to maintain these semantics when
|
||||
// manipulating a global over Xray (so the properties just re-resolve if
|
||||
// they've been deleted).
|
||||
//
|
||||
// Unfortunately, there's a bit of an impedance-mismatch between the Xray
|
||||
// and non-Xray machinery. The Xray machinery wants an API that returns a
|
||||
// JSPropertyDescriptor, so that the resolve hook doesn't have to get
|
||||
// snared up with trying to define a property on the Xray holder. At the
|
||||
// same time, the DefineInterface callbacks are set up to define things
|
||||
// directly on the global. And re-jiggering them to return property
|
||||
// descriptors is tricky, because some DefineInterface callbacks define
|
||||
// multiple things (like the Image() alias for HTMLImageElement).
|
||||
//
|
||||
// So the setup is as-follows:
|
||||
//
|
||||
// * The resolve function takes a JSPropertyDescriptor, but in the
|
||||
// non-Xray case, callees may define things directly on the global, and
|
||||
// set the value on the property descriptor to |undefined| to indicate
|
||||
// that there's nothing more for the caller to do. We assert against
|
||||
// this behavior in the Xray case.
|
||||
//
|
||||
// * We make sure that we do a non-Xray resolve first, so that all the
|
||||
// slots are set up. In the Xray case, this means unwrapping and doing
|
||||
// a non-Xray resolve before doing the Xray resolve.
|
||||
//
|
||||
// This all could use some grand refactoring, but for now we just limp
|
||||
// along.
|
||||
if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
|
||||
JS::Rooted<JSObject*> global(cx,
|
||||
js::CheckedUnwrap(obj, /* stopAtOuter = */ false));
|
||||
if (!global) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
JS::Rooted<JSObject*> interfaceObject(cx);
|
||||
{
|
||||
JSAutoCompartment ac(cx, global);
|
||||
interfaceObject = getOrCreateInterfaceObject(cx, global, id, false);
|
||||
}
|
||||
if (NS_WARN_IF(!interfaceObject)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!JS_WrapObject(cx, &interfaceObject)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
FillPropertyDescriptor(desc, obj, 0, JS::ObjectValue(*interfaceObject));
|
||||
} else {
|
||||
JS::Rooted<JSObject*> interfaceObject(cx,
|
||||
getOrCreateInterfaceObject(cx, obj, id, true));
|
||||
if (NS_WARN_IF(!interfaceObject)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// We've already defined the property. We indicate this to the caller
|
||||
// by filling a property descriptor with JS::UndefinedValue() as the
|
||||
// value. We still have to fill in a property descriptor, though, so
|
||||
// that the caller knows the property is in fact on this object. It
|
||||
// doesn't matter what we pass for the "readonly" argument here.
|
||||
FillPropertyDescriptor(desc, obj, JS::UndefinedValue(), false);
|
||||
}
|
||||
|
||||
*did_resolve = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
@ -2916,20 +2947,22 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
|||
false, &v);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = constructor->Install(cx, obj, v);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
JS::Rooted<JSObject*> class_obj(cx, &v.toObject());
|
||||
|
||||
// ... and define the constants from the DOM interface on that
|
||||
// constructor object.
|
||||
|
||||
JSAutoCompartment ac(cx, class_obj);
|
||||
rv = DefineInterfaceConstants(cx, class_obj, &name_struct->mIID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
{
|
||||
JSAutoCompartment ac(cx, class_obj);
|
||||
rv = DefineInterfaceConstants(cx, class_obj, &name_struct->mIID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
*did_resolve = true;
|
||||
if (!JS_WrapValue(cx, &v)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
FillPropertyDescriptor(desc, obj, 0, v);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -2940,38 +2973,42 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
|||
}
|
||||
|
||||
// Create the XPConnect prototype for our classinfo, PostCreateProto will
|
||||
// set up the prototype chain.
|
||||
// set up the prototype chain. This will go ahead and define things on the
|
||||
// actual window's global.
|
||||
nsCOMPtr<nsIXPConnectJSObjectHolder> proto_holder;
|
||||
rv = GetXPCProto(sXPConnect, cx, aWin, name_struct,
|
||||
getter_AddRefs(proto_holder));
|
||||
|
||||
if (NS_SUCCEEDED(rv) && obj != aWin->GetGlobalJSObject()) {
|
||||
JS::Rooted<JSObject*> dot_prototype(cx, proto_holder->GetJSObject());
|
||||
NS_ENSURE_STATE(dot_prototype);
|
||||
|
||||
const nsDOMClassInfoData *ci_data;
|
||||
if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
|
||||
ci_data = &sClassInfoData[name_struct->mDOMClassInfoID];
|
||||
} else {
|
||||
ci_data = name_struct->mData;
|
||||
}
|
||||
|
||||
return ResolvePrototype(sXPConnect, aWin, cx, obj, class_name, ci_data,
|
||||
name_struct, nameSpaceManager, dot_prototype,
|
||||
true, did_resolve);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
bool isXray = xpc::WrapperFactory::IsXrayWrapper(obj);
|
||||
MOZ_ASSERT_IF(obj != aWin->GetGlobalJSObject(), isXray);
|
||||
if (!isXray) {
|
||||
// GetXPCProto already defined the property for us
|
||||
FillPropertyDescriptor(desc, obj, JS::UndefinedValue(), false);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
*did_resolve = NS_SUCCEEDED(rv);
|
||||
// This is the Xray case. Look up the constructor object for this
|
||||
// prototype.
|
||||
JS::Rooted<JSObject*> dot_prototype(cx, proto_holder->GetJSObject());
|
||||
NS_ENSURE_STATE(dot_prototype);
|
||||
|
||||
return rv;
|
||||
const nsDOMClassInfoData *ci_data;
|
||||
if (name_struct->mType == nsGlobalNameStruct::eTypeClassConstructor) {
|
||||
ci_data = &sClassInfoData[name_struct->mDOMClassInfoID];
|
||||
} else {
|
||||
ci_data = name_struct->mData;
|
||||
}
|
||||
|
||||
return ResolvePrototype(sXPConnect, aWin, cx, obj, class_name, ci_data,
|
||||
name_struct, nameSpaceManager, dot_prototype,
|
||||
desc);
|
||||
}
|
||||
|
||||
if (name_struct->mType == nsGlobalNameStruct::eTypeClassProto) {
|
||||
// We don't have a XPConnect prototype object, let ResolvePrototype create
|
||||
// one.
|
||||
return ResolvePrototype(sXPConnect, aWin, cx, obj, class_name, nullptr,
|
||||
name_struct, nameSpaceManager, nullptr, true,
|
||||
did_resolve);
|
||||
name_struct, nameSpaceManager, nullptr, desc);
|
||||
}
|
||||
|
||||
if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructorAlias) {
|
||||
|
@ -3000,8 +3037,7 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
|||
}
|
||||
|
||||
return ResolvePrototype(sXPConnect, aWin, cx, obj, class_name, ci_data,
|
||||
name_struct, nameSpaceManager, nullptr, true,
|
||||
did_resolve);
|
||||
name_struct, nameSpaceManager, nullptr, desc);
|
||||
}
|
||||
|
||||
if (name_struct->mType == nsGlobalNameStruct::eTypeExternalConstructor) {
|
||||
|
@ -3013,15 +3049,12 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
|||
|
||||
JS::Rooted<JS::Value> val(cx);
|
||||
rv = WrapNative(cx, obj, constructor, &NS_GET_IID(nsIDOMDOMConstructor),
|
||||
false, &val);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = constructor->Install(cx, obj, val);
|
||||
true, &val);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ASSERTION(val.isObject(), "Why didn't we get a JSObject?");
|
||||
|
||||
*did_resolve = true;
|
||||
FillPropertyDescriptor(desc, obj, 0, val);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -3070,26 +3103,9 @@ nsWindowSH::GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
|||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
bool ok = ::JS_DefinePropertyById(cx, obj, id, prop_val,
|
||||
JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JSPROP_ENUMERATE);
|
||||
FillPropertyDescriptor(desc, obj, prop_val, false);
|
||||
|
||||
*did_resolve = true;
|
||||
|
||||
return ok ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (name_struct->mType == nsGlobalNameStruct::eTypeDynamicNameSet) {
|
||||
nsCOMPtr<nsIScriptExternalNameSet> nameset =
|
||||
do_CreateInstance(name_struct->mCID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsIScriptContext *context = aWin->GetContext();
|
||||
NS_ENSURE_TRUE(context, NS_ERROR_UNEXPECTED);
|
||||
|
||||
rv = nameset->InitializeNameSet(context);
|
||||
|
||||
*did_resolve = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
@ -3201,7 +3217,9 @@ const InterfaceShimEntry kInterfaceShimMap[] =
|
|||
{ "nsIDOMXPathResult", "XPathResult" } };
|
||||
|
||||
static nsresult
|
||||
DefineComponentsShim(JSContext *cx, JS::Handle<JSObject*> global, nsPIDOMWindow *win)
|
||||
LookupComponentsShim(JSContext *cx, JS::Handle<JSObject*> global,
|
||||
nsPIDOMWindow *win,
|
||||
JS::MutableHandle<JSPropertyDescriptor> desc)
|
||||
{
|
||||
// Keep track of how often this happens.
|
||||
Telemetry::Accumulate(Telemetry::COMPONENTS_SHIM_ACCESSED_BY_CONTENT, true);
|
||||
|
@ -3215,16 +3233,14 @@ DefineComponentsShim(JSContext *cx, JS::Handle<JSObject*> global, nsPIDOMWindow
|
|||
// Create a fake Components object.
|
||||
JS::Rooted<JSObject*> components(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), global));
|
||||
NS_ENSURE_TRUE(components, NS_ERROR_OUT_OF_MEMORY);
|
||||
bool ok = JS_DefineProperty(cx, global, "Components", JS::ObjectValue(*components),
|
||||
JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// Create a fake interfaces object.
|
||||
JS::Rooted<JSObject*> interfaces(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), global));
|
||||
NS_ENSURE_TRUE(interfaces, NS_ERROR_OUT_OF_MEMORY);
|
||||
ok = JS_DefineProperty(cx, components, "interfaces", JS::ObjectValue(*interfaces),
|
||||
JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
|
||||
bool ok =
|
||||
JS_DefineProperty(cx, components, "interfaces", JS::ObjectValue(*interfaces),
|
||||
JS_PropertyStub, JS_StrictPropertyStub,
|
||||
JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY);
|
||||
NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
// Define a bunch of shims from the Ci.nsIDOMFoo to window.Foo for DOM
|
||||
|
@ -3251,6 +3267,8 @@ DefineComponentsShim(JSContext *cx, JS::Handle<JSObject*> global, nsPIDOMWindow
|
|||
NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
FillPropertyDescriptor(desc, global, JS::ObjectValue(*components), false);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -3271,11 +3289,6 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
|||
nsGlobalWindow *win = nsGlobalWindow::FromWrapper(wrapper);
|
||||
MOZ_ASSERT(win->IsInnerWindow());
|
||||
|
||||
if (id == XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS)) {
|
||||
*objp = obj;
|
||||
return DefineComponentsShim(cx, obj, win);
|
||||
}
|
||||
|
||||
// Don't resolve standard classes on XrayWrappers, only resolve them if we're
|
||||
// resolving on the real global object.
|
||||
bool isXray = xpc::WrapperFactory::IsXrayWrapper(obj);
|
||||
|
@ -3352,11 +3365,50 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool did_resolve = false;
|
||||
nsresult rv = GlobalResolve(win, cx, obj, id, &did_resolve);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (isXray) {
|
||||
// We promise to resolve on the underlying object first. That will create
|
||||
// the actual interface object if needed and store it in a data structure
|
||||
// hanging off the global. Then our second call will wrap up in an Xray as
|
||||
// needed. We do things this way because we use the existence of the
|
||||
// object in that data structure as a flag that indicates that its name
|
||||
// (and any relevant named constructor names) has been resolved before;
|
||||
// this allows us to avoid re-resolving in the Xray case if the property is
|
||||
// deleted by page script.
|
||||
JS::Rooted<JSObject*> global(cx,
|
||||
js::UncheckedUnwrap(obj, /* stopAtOuter = */ false));
|
||||
JSAutoCompartment ac(cx, global);
|
||||
JS::Rooted<JSPropertyDescriptor> desc(cx);
|
||||
if (!win->DoNewResolve(cx, global, id, &desc)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// If we have an object here, that means we resolved the property.
|
||||
// But if the value is undefined, that means that GlobalResolve
|
||||
// also already defined it, so we don't have to.
|
||||
if (desc.object() && !desc.value().isUndefined() &&
|
||||
!JS_DefinePropertyById(cx, global, id, desc.value(),
|
||||
desc.getter(), desc.setter(),
|
||||
desc.attributes())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
JS::Rooted<JSPropertyDescriptor> desc(cx);
|
||||
if (!win->DoNewResolve(cx, obj, id, &desc)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (desc.object()) {
|
||||
// If we have an object here, that means we resolved the property.
|
||||
// But if the value is undefined, that means that GlobalResolve
|
||||
// also already defined it, so we don't have to. Note that in the
|
||||
// Xray case we should never see undefined.
|
||||
MOZ_ASSERT_IF(isXray, !desc.value().isUndefined());
|
||||
if (!desc.value().isUndefined() &&
|
||||
!JS_DefinePropertyById(cx, obj, id, desc.value(),
|
||||
desc.getter(), desc.setter(),
|
||||
desc.attributes())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (did_resolve) {
|
||||
*objp = obj;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -3364,8 +3416,8 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
|
|||
if (!(flags & JSRESOLVE_ASSIGNING) && sDocument_id == id) {
|
||||
nsCOMPtr<nsIDocument> document = win->GetDoc();
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
rv = WrapNative(cx, JS::CurrentGlobalOrNull(cx), document, document,
|
||||
&NS_GET_IID(nsIDOMDocument), &v, false);
|
||||
nsresult rv = WrapNative(cx, JS::CurrentGlobalOrNull(cx), document, document,
|
||||
&NS_GET_IID(nsIDOMDocument), &v, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// nsIDocument::WrapObject will handle defining the property.
|
||||
|
|
|
@ -256,8 +256,9 @@ protected:
|
|||
|
||||
static nsresult GlobalResolve(nsGlobalWindow *aWin, JSContext *cx,
|
||||
JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
|
||||
bool *did_resolve);
|
||||
JS::MutableHandle<JSPropertyDescriptor> desc);
|
||||
|
||||
friend class nsGlobalWindow;
|
||||
public:
|
||||
NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
|
||||
JSObject *globalObj, JSObject **parentObj) MOZ_OVERRIDE;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "nsIScriptContext.h"
|
||||
#include "nsIScriptTimeoutHandler.h"
|
||||
#include "nsIController.h"
|
||||
#include "nsScriptNameSpaceManager.h"
|
||||
|
||||
// Helper Classes
|
||||
#include "nsJSUtils.h"
|
||||
|
@ -4052,6 +4053,43 @@ nsGlobalWindow::GetSupportedNames(nsTArray<nsString>& aNames)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsGlobalWindow::DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
JS::Handle<jsid> aId,
|
||||
JS::MutableHandle<JSPropertyDescriptor> aDesc)
|
||||
{
|
||||
MOZ_ASSERT(IsInnerWindow());
|
||||
|
||||
if (!JSID_IS_STRING(aId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult rv = nsWindowSH::GlobalResolve(this, aCx, aObj, aId, aDesc);
|
||||
if (NS_FAILED(rv)) {
|
||||
return Throw(aCx, rv);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
EnumerateGlobalName(const nsAString& aName, void* aClosure)
|
||||
{
|
||||
nsTArray<nsString>* arr = static_cast<nsTArray<nsString>*>(aClosure);
|
||||
arr->AppendElement(aName);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsScriptNameSpaceManager* nameSpaceManager = GetNameSpaceManager();
|
||||
if (nameSpaceManager) {
|
||||
nameSpaceManager->EnumerateGlobalNames(EnumerateGlobalName, &aNames);
|
||||
}
|
||||
}
|
||||
|
||||
nsIDOMOfflineResourceList*
|
||||
nsGlobalWindow::GetApplicationCache(ErrorResult& aError)
|
||||
{
|
||||
|
@ -4130,13 +4168,6 @@ nsGlobalWindow::GetCrypto(nsIDOMCrypto** aCrypto)
|
|||
return rv.ErrorCode();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalWindow::GetPkcs11(nsIDOMPkcs11** aPkcs11)
|
||||
{
|
||||
*aPkcs11 = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIControllers*
|
||||
nsGlobalWindow::GetControllers(ErrorResult& aError)
|
||||
{
|
||||
|
@ -10491,6 +10522,11 @@ nsGlobalWindow::ShowSlowScriptDialog()
|
|||
return KillSlowScript;
|
||||
}
|
||||
|
||||
// If our document is not active, just kill the script: we've been unloaded
|
||||
if (!HasActiveDocument()) {
|
||||
return KillSlowScript;
|
||||
}
|
||||
|
||||
// Get the nsIPrompt interface from the docshell
|
||||
nsCOMPtr<nsIDocShell> ds = GetDocShell();
|
||||
NS_ENSURE_TRUE(ds, KillSlowScript);
|
||||
|
|
|
@ -475,6 +475,13 @@ public:
|
|||
|
||||
void GetSupportedNames(nsTArray<nsString>& aNames);
|
||||
|
||||
bool DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
JS::Handle<jsid> aId,
|
||||
JS::MutableHandle<JSPropertyDescriptor> aDesc);
|
||||
|
||||
void GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
||||
// Object Management
|
||||
nsGlobalWindow(nsGlobalWindow *aOuterWindow);
|
||||
|
||||
|
@ -902,10 +909,6 @@ public:
|
|||
int64_t GetMozAnimationStartTime(mozilla::ErrorResult& aError);
|
||||
void SizeToContent(mozilla::ErrorResult& aError);
|
||||
nsIDOMCrypto* GetCrypto(mozilla::ErrorResult& aError);
|
||||
nsIDOMPkcs11* GetPkcs11()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
nsIControllers* GetControllers(mozilla::ErrorResult& aError);
|
||||
float GetMozInnerScreenX(mozilla::ErrorResult& aError);
|
||||
float GetMozInnerScreenY(mozilla::ErrorResult& aError);
|
||||
|
|
|
@ -25,9 +25,6 @@
|
|||
#define JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY \
|
||||
"JavaScript-global-static-nameset"
|
||||
|
||||
#define JAVASCRIPT_GLOBAL_DYNAMIC_NAMESET_CATEGORY \
|
||||
"JavaScript-global-dynamic-nameset"
|
||||
|
||||
#define JAVASCRIPT_DOM_CLASS \
|
||||
"JavaScript-DOM-class"
|
||||
|
||||
|
|
|
@ -369,9 +369,6 @@ nsScriptNameSpaceManager::Init()
|
|||
rv = FillHash(cm, JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = FillHash(cm, JAVASCRIPT_GLOBAL_DYNAMIC_NAMESET_CATEGORY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = FillHash(cm, JAVASCRIPT_NAVIGATOR_PROPERTY_CATEGORY);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
@ -626,8 +623,6 @@ nsScriptNameSpaceManager::OperateCategoryEntryHash(nsICategoryManager* aCategory
|
|||
type = nsGlobalNameStruct::eTypeNavigatorProperty;
|
||||
} else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY) == 0) {
|
||||
type = nsGlobalNameStruct::eTypeStaticNameSet;
|
||||
} else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_DYNAMIC_NAMESET_CATEGORY) == 0) {
|
||||
type = nsGlobalNameStruct::eTypeDynamicNameSet;
|
||||
} else {
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ struct nsGlobalNameStruct
|
|||
eTypeNavigatorProperty,
|
||||
eTypeExternalConstructor,
|
||||
eTypeStaticNameSet,
|
||||
eTypeDynamicNameSet,
|
||||
eTypeClassConstructor,
|
||||
eTypeClassProto,
|
||||
eTypeExternalClassInfoCreator,
|
||||
|
|
|
@ -2168,7 +2168,7 @@ FinalizeGlobal(JSFreeOp* aFreeOp, JSObject* aObj)
|
|||
|
||||
bool
|
||||
ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
JS::MutableHandle<jsid> aId, unsigned aFlags,
|
||||
JS::Handle<jsid> aId, unsigned aFlags,
|
||||
JS::MutableHandle<JSObject*> aObjp)
|
||||
{
|
||||
bool resolved;
|
||||
|
|
|
@ -2357,7 +2357,7 @@ FinalizeGlobal(JSFreeOp* aFop, JSObject* aObj);
|
|||
|
||||
bool
|
||||
ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
JS::MutableHandle<jsid> aId, unsigned aFlags,
|
||||
JS::Handle<jsid> aId, unsigned aFlags,
|
||||
JS::MutableHandle<JSObject*> aObjp);
|
||||
|
||||
bool
|
||||
|
|
|
@ -1935,7 +1935,6 @@ addExternalIface('nsIEditor', nativeType='nsIEditor', notflattened=True)
|
|||
addExternalIface('nsIVariant', nativeType='nsIVariant', notflattened=True)
|
||||
addExternalIface('OutputStream', nativeType='nsIOutputStream',
|
||||
notflattened=True)
|
||||
addExternalIface('Pkcs11')
|
||||
addExternalIface('Principal', nativeType='nsIPrincipal',
|
||||
headerFile='nsIPrincipal.h', notflattened=True)
|
||||
addExternalIface('StackFrame', nativeType='nsIStackFrame',
|
||||
|
|
|
@ -2477,6 +2477,7 @@ class CGClearCachedValueMethod(CGAbstractMethod):
|
|||
regetMember = ("\n"
|
||||
"JS::Rooted<JS::Value> temp(aCx);\n"
|
||||
"JSJitGetterCallArgs args(&temp);\n"
|
||||
"JSAutoCompartment ac(aCx, obj);\n"
|
||||
"if (!get_%s(aCx, obj, aObject, args)) {\n"
|
||||
" js::SetReservedSlot(obj, %s, oldValue);\n"
|
||||
" nsJSUtils::ReportPendingException(aCx);\n"
|
||||
|
@ -6021,6 +6022,20 @@ class CGNewResolveHook(CGAbstractBindingMethod):
|
|||
"objp.set(obj);\n"
|
||||
"return true;"))
|
||||
|
||||
def definition_body(self):
|
||||
if self.descriptor.interface.getExtendedAttribute("Global"):
|
||||
# Resolve standard classes
|
||||
prefix = CGIndenter(CGGeneric(
|
||||
"if (!ResolveGlobal(cx, obj, id, flags, objp)) {\n"
|
||||
" return false;\n"
|
||||
"}\n"
|
||||
"if (objp) {\n"
|
||||
" return true;\n"
|
||||
"}\n\n")).define()
|
||||
else:
|
||||
prefix = ""
|
||||
return prefix + CGAbstractBindingMethod.definition_body(self)
|
||||
|
||||
class CGEnumerateHook(CGAbstractBindingMethod):
|
||||
"""
|
||||
Enumerate hook for objects with custom hooks.
|
||||
|
@ -6052,6 +6067,17 @@ class CGEnumerateHook(CGAbstractBindingMethod):
|
|||
"}\n"
|
||||
"return true;"))
|
||||
|
||||
def definition_body(self):
|
||||
if self.descriptor.interface.getExtendedAttribute("Global"):
|
||||
# Enumerate standard classes
|
||||
prefix = CGIndenter(CGGeneric(
|
||||
"if (!EnumerateGlobal(cx, obj)) {\n"
|
||||
" return false;\n"
|
||||
"}\n\n")).define()
|
||||
else:
|
||||
prefix = ""
|
||||
return prefix + CGAbstractBindingMethod.definition_body(self)
|
||||
|
||||
class CppKeywords():
|
||||
"""
|
||||
A class for checking if method names declared in webidl
|
||||
|
@ -7732,6 +7758,29 @@ class CGResolveOwnPropertyViaNewresolve(CGAbstractBindingMethod):
|
|||
callArgs="")
|
||||
def generate_code(self):
|
||||
return CGIndenter(CGGeneric(
|
||||
"{\n"
|
||||
" // Since we're dealing with an Xray, do the resolve on the\n"
|
||||
" // underlying object first. That gives it a chance to\n"
|
||||
" // define properties on the actual object as needed, and\n"
|
||||
" // then use the fact that it created the objects as a flag\n"
|
||||
" // o avoid re-resolving the properties if someone deletes\n"
|
||||
" // them.\n"
|
||||
" JSAutoCompartment ac(cx, obj);\n"
|
||||
" JS::Rooted<JSPropertyDescriptor> objDesc(cx);\n"
|
||||
" if (!self->DoNewResolve(cx, obj, id, &objDesc)) {\n"
|
||||
" return false;\n"
|
||||
" }\n"
|
||||
" // If desc.value() is undefined, then the DoNewResolve call\n"
|
||||
" // has already defined the property on the object. Don't\n"
|
||||
" // try to also define it.\n"
|
||||
" if (objDesc.object() &&\n"
|
||||
" !objDesc.value().isUndefined() &&\n"
|
||||
" !JS_DefinePropertyById(cx, obj, id, objDesc.value(),\n"
|
||||
" objDesc.getter(), objDesc.setter(),\n"
|
||||
" objDesc.attributes())) {\n"
|
||||
" return false;\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"return self->DoNewResolve(cx, wrapper, id, desc);"))
|
||||
|
||||
class CGEnumerateOwnProperties(CGAbstractStaticMethod):
|
||||
|
|
|
@ -165,6 +165,18 @@ FillPropertyDescriptor(JS::MutableHandle<JSPropertyDescriptor> desc, JSObject* o
|
|||
FillPropertyDescriptor(desc, obj, readonly);
|
||||
}
|
||||
|
||||
inline void
|
||||
FillPropertyDescriptor(JS::MutableHandle<JSPropertyDescriptor> desc,
|
||||
JSObject* obj, unsigned attributes, JS::Value v)
|
||||
{
|
||||
desc.object().set(obj);
|
||||
desc.value().set(v);
|
||||
desc.setAttributes(attributes);
|
||||
desc.setGetter(nullptr);
|
||||
desc.setSetter(nullptr);
|
||||
desc.setShortId(0);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -378,10 +378,7 @@ NS_IMETHODIMP JSStackFrame::GetFilename(nsACString& aFilename)
|
|||
if (!mFilenameInitialized) {
|
||||
JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
|
||||
if (desc.script()) {
|
||||
// This cx dance is silly, since JS_GetScriptFilename ignores
|
||||
// its cx argument.
|
||||
JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
|
||||
JSAutoRequest ar(cx);
|
||||
ThreadsafeAutoSafeJSContext cx;
|
||||
JSAutoCompartment ac(cx, desc.script());
|
||||
const char* filename = JS_GetScriptFilename(cx, desc.script());
|
||||
if (filename) {
|
||||
|
@ -407,8 +404,7 @@ NS_IMETHODIMP JSStackFrame::GetName(nsACString& aFunction)
|
|||
if (!mFunnameInitialized) {
|
||||
JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
|
||||
if (desc.fun() && desc.script()) {
|
||||
JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
|
||||
JSAutoRequest ar(cx);
|
||||
ThreadsafeAutoSafeJSContext cx;
|
||||
JSAutoCompartment ac(cx, desc.script());
|
||||
JS::Rooted<JSFunction*> fun(cx, desc.fun());
|
||||
JS::Rooted<JSString*> funid(cx, JS_GetFunctionDisplayId(fun));
|
||||
|
|
|
@ -25,7 +25,6 @@ const MAX_REQUESTS = 25;
|
|||
|
||||
Cu.import("resource://gre/modules/DataStoreCursor.jsm");
|
||||
Cu.import("resource://gre/modules/DataStoreDB.jsm");
|
||||
Cu.import("resource://gre/modules/ObjectWrapper.jsm");
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.importGlobalProperties(["indexedDB"]);
|
||||
|
@ -142,7 +141,7 @@ this.DataStore.prototype = {
|
|||
|
||||
function getInternalSuccess(aEvent, aPos) {
|
||||
debug("GetInternal success. Record: " + aEvent.target.result);
|
||||
results[aPos] = ObjectWrapper.wrap(aEvent.target.result, self._window);
|
||||
results[aPos] = Cu.cloneInto(aEvent.target.result, self._window);
|
||||
if (!--pendingIds) {
|
||||
aCallback(results);
|
||||
return;
|
||||
|
|
|
@ -27,7 +27,6 @@ const REVISION_REMOVED = 'removed';
|
|||
const REVISION_VOID = 'void';
|
||||
const REVISION_SKIP = 'skip'
|
||||
|
||||
Cu.import('resource://gre/modules/ObjectWrapper.jsm');
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
|
||||
/**
|
||||
|
@ -151,7 +150,7 @@ this.DataStoreCursor.prototype = {
|
|||
self._revision = aEvent.target.result.value;
|
||||
self._objectId = 0;
|
||||
self._state = STATE_SEND_ALL;
|
||||
aResolve(ObjectWrapper.wrap({ operation: 'clear' }, self._window));
|
||||
aResolve(Cu.cloneInto({ operation: 'clear' }, self._window));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -291,7 +290,7 @@ this.DataStoreCursor.prototype = {
|
|||
if (self._revision.revisionId != aEvent.target.result.value.revisionId) {
|
||||
self._revision = aEvent.target.result.value;
|
||||
self._objectId = 0;
|
||||
aResolve(ObjectWrapper.wrap({ operation: 'clear' }, self._window));
|
||||
aResolve(Cu.cloneInto({ operation: 'clear' }, self._window));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -305,8 +304,8 @@ this.DataStoreCursor.prototype = {
|
|||
}
|
||||
|
||||
self._objectId = cursor.key;
|
||||
aResolve(ObjectWrapper.wrap({ operation: 'add', id: self._objectId,
|
||||
data: cursor.value }, self._window));
|
||||
aResolve(Cu.cloneInto({ operation: 'add', id: self._objectId,
|
||||
data: cursor.value }, self._window));
|
||||
};
|
||||
};
|
||||
},
|
||||
|
@ -324,8 +323,8 @@ this.DataStoreCursor.prototype = {
|
|||
|
||||
switch (this._revision.operation) {
|
||||
case REVISION_REMOVED:
|
||||
aResolve(ObjectWrapper.wrap({ operation: 'remove', id: this._revision.objectId },
|
||||
this._window));
|
||||
aResolve(Cu.cloneInto({ operation: 'remove', id: this._revision.objectId },
|
||||
this._window));
|
||||
break;
|
||||
|
||||
case REVISION_ADDED: {
|
||||
|
@ -337,8 +336,8 @@ this.DataStoreCursor.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
aResolve(ObjectWrapper.wrap({ operation: 'add', id: self._revision.objectId,
|
||||
data: aEvent.target.result }, self._window));
|
||||
aResolve(Cu.cloneInto({ operation: 'add', id: self._revision.objectId,
|
||||
data: aEvent.target.result }, self._window));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -357,8 +356,8 @@ this.DataStoreCursor.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
aResolve(ObjectWrapper.wrap({ operation: 'update', id: self._revision.objectId,
|
||||
data: aEvent.target.result }, self._window));
|
||||
aResolve(Cu.cloneInto({ operation: 'update', id: self._revision.objectId,
|
||||
data: aEvent.target.result }, self._window));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -377,8 +376,8 @@ this.DataStoreCursor.prototype = {
|
|||
|
||||
stateMachineDone: function(aStore, aRevisionStore, aResolve, aReject) {
|
||||
this.close();
|
||||
aResolve(ObjectWrapper.wrap({ revisionId: this._revision.revisionId,
|
||||
operation: 'done' }, this._window));
|
||||
aResolve(Cu.cloneInto({ revisionId: this._revision.revisionId,
|
||||
operation: 'done' }, this._window));
|
||||
},
|
||||
|
||||
// public interface
|
||||
|
|
|
@ -77,7 +77,6 @@ interface nsIDOMRange;
|
|||
|
||||
// Crypto
|
||||
interface nsIDOMCrypto;
|
||||
interface nsIDOMPkcs11;
|
||||
|
||||
// Used font face (for inspector)
|
||||
interface nsIDOMFontFace;
|
||||
|
|
|
@ -23,7 +23,6 @@ XPIDL_SOURCES += [
|
|||
'nsIDOMLocation.idl',
|
||||
'nsIDOMModalContentWindow.idl',
|
||||
'nsIDOMNavigator.idl',
|
||||
'nsIDOMPkcs11.idl',
|
||||
'nsIDOMScreen.idl',
|
||||
'nsIDOMWindow.idl',
|
||||
'nsIDOMWindowCollection.idl',
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "domstubs.idl"
|
||||
|
||||
[scriptable, uuid(9fd42950-25e7-11d4-8a7d-006008c844c3)]
|
||||
interface nsIDOMPkcs11 : nsISupports
|
||||
{
|
||||
long deletemodule(in DOMString moduleName);
|
||||
long addmodule(in DOMString moduleName,
|
||||
in DOMString libraryFullPath,
|
||||
in long cryptoMechanismFlags,
|
||||
in long cipherFlags);
|
||||
};
|
|
@ -24,7 +24,7 @@ interface nsIVariant;
|
|||
* @see <http://www.whatwg.org/html/#window>
|
||||
*/
|
||||
|
||||
[scriptable, uuid(f2d1d383-f7b5-46f8-aadf-b69a0ebfb16f)]
|
||||
[scriptable, uuid(97b6784b-ab12-4f79-8422-d7868a4cc7dc)]
|
||||
interface nsIDOMWindow : nsISupports
|
||||
{
|
||||
// the current browsing context
|
||||
|
@ -374,7 +374,6 @@ interface nsIDOMWindow : nsISupports
|
|||
readonly attribute boolean closed;
|
||||
|
||||
readonly attribute nsIDOMCrypto crypto;
|
||||
readonly attribute nsIDOMPkcs11 pkcs11;
|
||||
|
||||
// Note: this is [ChromeOnly] scriptable via WebIDL.
|
||||
[noscript] readonly attribute nsIControllers controllers;
|
||||
|
|
|
@ -18,11 +18,10 @@
|
|||
interface ApplicationCache;
|
||||
interface MozFrameRequestCallback;
|
||||
interface nsIDOMCrypto;
|
||||
interface Pkcs11;
|
||||
typedef any Transferable;
|
||||
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/
|
||||
[Global]
|
||||
[Global, NeedNewResolve]
|
||||
/*sealed*/ interface Window : EventTarget {
|
||||
// the current browsing context
|
||||
[Unforgeable, Throws,
|
||||
|
@ -257,8 +256,6 @@ partial interface Window {
|
|||
*/
|
||||
[Throws] void sizeToContent();
|
||||
|
||||
readonly attribute Pkcs11? pkcs11;
|
||||
|
||||
// XXX Shouldn't this be in nsIDOMChromeWindow?
|
||||
[ChromeOnly, Replaceable, Throws] readonly attribute MozControllers controllers;
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ HBSOURCES = \
|
|||
hb-buffer.cc \
|
||||
hb-cache-private.hh \
|
||||
hb-common.cc \
|
||||
hb-fallback-shape.cc \
|
||||
hb-face-private.hh \
|
||||
hb-face.cc \
|
||||
hb-font-private.hh \
|
||||
|
@ -92,6 +91,8 @@ HBSOURCES += \
|
|||
hb-ot-shape-complex-arabic-fallback.hh \
|
||||
hb-ot-shape-complex-arabic-table.hh \
|
||||
hb-ot-shape-complex-default.cc \
|
||||
hb-ot-shape-complex-hangul.cc \
|
||||
hb-ot-shape-complex-hebrew.cc \
|
||||
hb-ot-shape-complex-indic.cc \
|
||||
hb-ot-shape-complex-indic-machine.hh \
|
||||
hb-ot-shape-complex-indic-private.hh \
|
||||
|
@ -101,6 +102,7 @@ HBSOURCES += \
|
|||
hb-ot-shape-complex-sea.cc \
|
||||
hb-ot-shape-complex-sea-machine.hh \
|
||||
hb-ot-shape-complex-thai.cc \
|
||||
hb-ot-shape-complex-tibetan.cc \
|
||||
hb-ot-shape-complex-private.hh \
|
||||
hb-ot-shape-normalize-private.hh \
|
||||
hb-ot-shape-normalize.cc \
|
||||
|
@ -111,10 +113,15 @@ HBSOURCES += \
|
|||
HBHEADERS += \
|
||||
hb-ot.h \
|
||||
hb-ot-layout.h \
|
||||
hb-ot-shape.h \
|
||||
hb-ot-tag.h \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
if HAVE_FALLBACK
|
||||
HBSOURCES += hb-fallback-shape.cc
|
||||
endif
|
||||
|
||||
if HAVE_PTHREAD
|
||||
HBCFLAGS += $(PTHREAD_CFLAGS)
|
||||
HBLIBS += $(PTHREAD_LIBS)
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
|
||||
#include "hb-private.hh"
|
||||
|
||||
#include "hb-blob.h"
|
||||
#include "hb-object-private.hh"
|
||||
|
||||
#ifdef HAVE_SYS_MMAN_H
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#define HB_BUFFER_PRIVATE_HH
|
||||
|
||||
#include "hb-private.hh"
|
||||
#include "hb-buffer.h"
|
||||
#include "hb-object-private.hh"
|
||||
#include "hb-unicode-private.hh"
|
||||
|
||||
|
|
|
@ -204,7 +204,7 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer,
|
|||
|
||||
*p++ = '+';
|
||||
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance));
|
||||
if (pos->y_advance)
|
||||
if (pos[i].y_advance)
|
||||
p += MAX (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance));
|
||||
}
|
||||
|
||||
|
|
|
@ -28,8 +28,6 @@
|
|||
|
||||
#include "hb-private.hh"
|
||||
|
||||
#include "hb-version.h"
|
||||
|
||||
#include "hb-mutex-private.hh"
|
||||
#include "hb-object-private.hh"
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ typedef uint32_t hb_tag_t;
|
|||
#define HB_UNTAG(tag) ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_t)((tag)>>8)), ((uint8_t)(tag))
|
||||
|
||||
#define HB_TAG_NONE HB_TAG(0,0,0,0)
|
||||
#define HB_TAG_MAX HB_TAG(0xff,0xff,0xff,0xff)
|
||||
|
||||
/* len=-1 means str is NUL-terminated. */
|
||||
hb_tag_t
|
||||
|
@ -270,7 +271,12 @@ typedef enum
|
|||
/*6.1*/ HB_SCRIPT_TAKRI = HB_TAG ('T','a','k','r'),
|
||||
|
||||
/* No script set. */
|
||||
/*---*/ HB_SCRIPT_INVALID = HB_TAG_NONE
|
||||
/*---*/ HB_SCRIPT_INVALID = HB_TAG_NONE,
|
||||
|
||||
/* Dummy value to ensure any hb_tag_t value can be passed/stored as hb_script_t
|
||||
* without risking undefined behavior. */
|
||||
/*---*/ _HB_SCRIPT_MAX_VALUE = HB_TAG_MAX
|
||||
|
||||
} hb_script_t;
|
||||
|
||||
/* These are moved out of hb_script_t because glib-mkenums chokes otherwise. */
|
||||
|
|
|
@ -591,7 +591,6 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
|
|||
|
||||
CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (NULL, chars_len);
|
||||
CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref);
|
||||
CFRelease (string_ref);
|
||||
CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
|
||||
kCTFontAttributeName, font_data->ct_font);
|
||||
|
||||
|
@ -671,23 +670,33 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
|
|||
if (buffer->in_error)
|
||||
FAIL ("Buffer resize failed");
|
||||
hb_glyph_info_t *info = buffer->info + buffer->len;
|
||||
buffer->len += range.length;
|
||||
|
||||
for (CFIndex j = 0; j < range.length; j++)
|
||||
CGGlyph notdef = 0;
|
||||
double advance = CTFontGetAdvancesForGlyphs (font_data->ct_font, kCTFontHorizontalOrientation, ¬def, NULL, 1);
|
||||
|
||||
for (CFIndex j = range.location; j < range.location + range.length; j++)
|
||||
{
|
||||
CGGlyph notdef = 0;
|
||||
double advance = CTFontGetAdvancesForGlyphs (font_data->ct_font, kCTFontHorizontalOrientation, ¬def, NULL, 1);
|
||||
UniChar ch = CFStringGetCharacterAtIndex (string_ref, j);
|
||||
if (hb_in_range<UniChar> (ch, 0xDC00, 0xDFFF) && range.location < j)
|
||||
{
|
||||
ch = CFStringGetCharacterAtIndex (string_ref, j - 1);
|
||||
if (hb_in_range<UniChar> (ch, 0xD800, 0xDBFF))
|
||||
/* This is the second of a surrogate pair. Don't need .notdef
|
||||
* for this one. */
|
||||
continue;
|
||||
}
|
||||
|
||||
info->codepoint = notdef;
|
||||
/* TODO We have to fixup clusters later. See vis_clusters in
|
||||
* hb-uniscribe.cc for example. */
|
||||
info->cluster = range.location + j;
|
||||
info->cluster = j;
|
||||
|
||||
info->mask = advance;
|
||||
info->var1.u32 = 0;
|
||||
info->var2.u32 = 0;
|
||||
|
||||
info++;
|
||||
buffer->len++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -796,6 +805,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan,
|
|||
}
|
||||
}
|
||||
|
||||
CFRelease (string_ref);
|
||||
CFRelease (line);
|
||||
|
||||
return true;
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
|
||||
#include "hb-private.hh"
|
||||
|
||||
#include "hb-font.h"
|
||||
#include "hb-object-private.hh"
|
||||
#include "hb-shaper-private.hh"
|
||||
#include "hb-shape-plan-private.hh"
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include "hb-ot-layout-private.hh"
|
||||
|
||||
#include "hb-font-private.hh"
|
||||
#include "hb-blob.h"
|
||||
#include "hb-open-file-private.hh"
|
||||
#include "hb-ot-head-table.hh"
|
||||
#include "hb-ot-maxp-table.hh"
|
||||
|
|
|
@ -105,8 +105,9 @@ _hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED,
|
|||
* shaper which many people unfortunately still request.
|
||||
*/
|
||||
|
||||
bool has_space;
|
||||
hb_codepoint_t space;
|
||||
font->get_glyph (' ', 0, &space);
|
||||
has_space = font->get_glyph (' ', 0, &space);
|
||||
|
||||
buffer->clear_positions ();
|
||||
|
||||
|
@ -114,7 +115,7 @@ _hb_fallback_shape (hb_shape_plan_t *shape_plan HB_UNUSED,
|
|||
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
if (buffer->unicode->is_default_ignorable (buffer->info[i].codepoint)) {
|
||||
if (has_space && buffer->unicode->is_default_ignorable (buffer->info[i].codepoint)) {
|
||||
buffer->info[i].codepoint = space;
|
||||
buffer->pos[i].x_advance = 0;
|
||||
buffer->pos[i].y_advance = 0;
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
|
||||
#include "hb-private.hh"
|
||||
|
||||
#include "hb-font.h"
|
||||
#include "hb-object-private.hh"
|
||||
#include "hb-face-private.hh"
|
||||
#include "hb-shaper-private.hh"
|
||||
|
@ -145,6 +144,12 @@ struct hb_font_t {
|
|||
|
||||
/* Public getters */
|
||||
|
||||
inline hb_bool_t has_glyph (hb_codepoint_t unicode)
|
||||
{
|
||||
hb_codepoint_t glyph;
|
||||
return get_glyph (unicode, 0, &glyph);
|
||||
}
|
||||
|
||||
inline hb_bool_t get_glyph (hb_codepoint_t unicode, hb_codepoint_t variation_selector,
|
||||
hb_codepoint_t *glyph)
|
||||
{
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include "hb-ot-layout-private.hh"
|
||||
|
||||
#include "hb-font-private.hh"
|
||||
#include "hb-blob.h"
|
||||
#include "hb-open-file-private.hh"
|
||||
#include "hb-ot-head-table.hh"
|
||||
#include "hb-ot-maxp-table.hh"
|
||||
|
|
|
@ -34,8 +34,6 @@
|
|||
|
||||
#include <graphite2/Segment.h>
|
||||
|
||||
#include "hb-ot-tag.h"
|
||||
|
||||
|
||||
HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, face)
|
||||
HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, font)
|
||||
|
@ -109,7 +107,7 @@ _hb_graphite2_shaper_face_data_create (hb_face_t *face)
|
|||
|
||||
hb_graphite2_shaper_face_data_t *data = (hb_graphite2_shaper_face_data_t *) calloc (1, sizeof (hb_graphite2_shaper_face_data_t));
|
||||
if (unlikely (!data))
|
||||
hb_blob_destroy (silf_blob);
|
||||
return NULL;
|
||||
|
||||
data->face = face;
|
||||
data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_preloadAll);
|
||||
|
|
|
@ -31,8 +31,6 @@
|
|||
|
||||
#include "hb-private.hh"
|
||||
|
||||
#include "hb-blob.h"
|
||||
|
||||
|
||||
namespace OT {
|
||||
|
||||
|
@ -594,7 +592,7 @@ struct LONGDATETIME
|
|||
TRACE_SANITIZE (this);
|
||||
return TRACE_RETURN (likely (c->check_struct (this)));
|
||||
}
|
||||
private:
|
||||
protected:
|
||||
LONG major;
|
||||
ULONG minor;
|
||||
public:
|
||||
|
|
|
@ -109,11 +109,13 @@ struct ValueFormat : USHORT
|
|||
if (format & xPlacement) glyph_pos.x_offset += font->em_scale_x (get_short (values++));
|
||||
if (format & yPlacement) glyph_pos.y_offset += font->em_scale_y (get_short (values++));
|
||||
if (format & xAdvance) {
|
||||
if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values++)); else values++;
|
||||
if (likely (horizontal)) glyph_pos.x_advance += font->em_scale_x (get_short (values));
|
||||
values++;
|
||||
}
|
||||
/* y_advance values grow downward but font-space grows upward, hence negation */
|
||||
if (format & yAdvance) {
|
||||
if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values++)); else values++;
|
||||
if (unlikely (!horizontal)) glyph_pos.y_advance -= font->em_scale_y (get_short (values));
|
||||
values++;
|
||||
}
|
||||
|
||||
if (!has_device ()) return;
|
||||
|
@ -125,17 +127,21 @@ struct ValueFormat : USHORT
|
|||
|
||||
/* pixel -> fractional pixel */
|
||||
if (format & xPlaDevice) {
|
||||
if (x_ppem) glyph_pos.x_offset += (base + get_device (values++)).get_x_delta (font); else values++;
|
||||
if (x_ppem) glyph_pos.x_offset += (base + get_device (values)).get_x_delta (font);
|
||||
values++;
|
||||
}
|
||||
if (format & yPlaDevice) {
|
||||
if (y_ppem) glyph_pos.y_offset += (base + get_device (values++)).get_y_delta (font); else values++;
|
||||
if (y_ppem) glyph_pos.y_offset += (base + get_device (values)).get_y_delta (font);
|
||||
values++;
|
||||
}
|
||||
if (format & xAdvDevice) {
|
||||
if (horizontal && x_ppem) glyph_pos.x_advance += (base + get_device (values++)).get_x_delta (font); else values++;
|
||||
if (horizontal && x_ppem) glyph_pos.x_advance += (base + get_device (values)).get_x_delta (font);
|
||||
values++;
|
||||
}
|
||||
if (format & yAdvDevice) {
|
||||
/* y_advance values grow downward but font-space grows upward, hence negation */
|
||||
if (!horizontal && y_ppem) glyph_pos.y_advance -= (base + get_device (values++)).get_y_delta (font); else values++;
|
||||
if (!horizontal && y_ppem) glyph_pos.y_advance -= (base + get_device (values)).get_y_delta (font);
|
||||
values++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,12 +246,12 @@ struct AnchorFormat2
|
|||
unsigned int x_ppem = font->x_ppem;
|
||||
unsigned int y_ppem = font->y_ppem;
|
||||
hb_position_t cx, cy;
|
||||
hb_bool_t ret = false;
|
||||
hb_bool_t ret;
|
||||
|
||||
if (x_ppem || y_ppem)
|
||||
ret = font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
|
||||
*x = x_ppem && ret ? cx : font->em_scale_x (xCoordinate);
|
||||
*y = y_ppem && ret ? cy : font->em_scale_y (yCoordinate);
|
||||
ret = (x_ppem || y_ppem) &&
|
||||
font->get_glyph_contour_point_for_origin (glyph_id, anchorPoint, HB_DIRECTION_LTR, &cx, &cy);
|
||||
*x = ret && x_ppem ? cx : font->em_scale_x (xCoordinate);
|
||||
*y = ret && y_ppem ? cy : font->em_scale_y (yCoordinate);
|
||||
}
|
||||
|
||||
inline bool sanitize (hb_sanitize_context_t *c) {
|
||||
|
|
|
@ -31,8 +31,6 @@
|
|||
|
||||
#include "hb-private.hh"
|
||||
|
||||
#include "hb-ot-layout.h"
|
||||
|
||||
#include "hb-font-private.hh"
|
||||
#include "hb-buffer-private.hh"
|
||||
#include "hb-set-private.hh"
|
||||
|
|
|
@ -193,11 +193,6 @@ hb_ot_layout_collect_lookups (hb_face_t *face,
|
|||
const hb_tag_t *features,
|
||||
hb_set_t *lookup_indexes /* OUT */);
|
||||
|
||||
void
|
||||
hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
|
||||
hb_tag_t table_tag,
|
||||
hb_set_t *lookup_indexes /* OUT */);
|
||||
|
||||
void
|
||||
hb_ot_layout_lookup_collect_glyphs (hb_face_t *face,
|
||||
hb_tag_t table_tag,
|
||||
|
|
|
@ -199,7 +199,6 @@ collect_features_arabic (hb_ot_shape_planner_t *plan)
|
|||
map->add_global_bool_feature (HB_TAG('c','a','l','t'));
|
||||
map->add_gsub_pause (NULL);
|
||||
|
||||
map->add_global_bool_feature (HB_TAG('c','s','w','h'));
|
||||
map->add_global_bool_feature (HB_TAG('m','s','e','t'));
|
||||
}
|
||||
|
||||
|
@ -366,7 +365,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
|
|||
data_create_arabic,
|
||||
data_destroy_arabic,
|
||||
NULL, /* preprocess_text_arabic */
|
||||
NULL, /* normalization_preference */
|
||||
HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
|
||||
NULL, /* decompose */
|
||||
NULL, /* compose */
|
||||
setup_masks_arabic,
|
||||
|
|
|
@ -27,194 +27,18 @@
|
|||
#include "hb-ot-shape-complex-private.hh"
|
||||
|
||||
|
||||
/* TODO Add kana, and other small shapers here */
|
||||
|
||||
|
||||
/* The default shaper *only* adds additional per-script features.*/
|
||||
|
||||
static const hb_tag_t hangul_features[] =
|
||||
{
|
||||
HB_TAG('l','j','m','o'),
|
||||
HB_TAG('v','j','m','o'),
|
||||
HB_TAG('t','j','m','o'),
|
||||
HB_TAG_NONE
|
||||
};
|
||||
|
||||
static const hb_tag_t tibetan_features[] =
|
||||
{
|
||||
HB_TAG('a','b','v','s'),
|
||||
HB_TAG('b','l','w','s'),
|
||||
HB_TAG('a','b','v','m'),
|
||||
HB_TAG('b','l','w','m'),
|
||||
HB_TAG_NONE
|
||||
};
|
||||
|
||||
static void
|
||||
collect_features_default (hb_ot_shape_planner_t *plan)
|
||||
{
|
||||
const hb_tag_t *script_features = NULL;
|
||||
|
||||
switch ((hb_tag_t) plan->props.script)
|
||||
{
|
||||
/* Unicode-1.1 additions */
|
||||
case HB_SCRIPT_HANGUL:
|
||||
script_features = hangul_features;
|
||||
break;
|
||||
|
||||
/* Unicode-2.0 additions */
|
||||
case HB_SCRIPT_TIBETAN:
|
||||
script_features = tibetan_features;
|
||||
break;
|
||||
}
|
||||
|
||||
for (; script_features && *script_features; script_features++)
|
||||
plan->map.add_global_bool_feature (*script_features);
|
||||
}
|
||||
|
||||
static hb_ot_shape_normalization_mode_t
|
||||
normalization_preference_default (const hb_segment_properties_t *props)
|
||||
{
|
||||
return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
|
||||
}
|
||||
|
||||
static bool
|
||||
compose_default (const hb_ot_shape_normalize_context_t *c,
|
||||
hb_codepoint_t a,
|
||||
hb_codepoint_t b,
|
||||
hb_codepoint_t *ab)
|
||||
{
|
||||
/* Hebrew presentation-form shaping.
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=728866
|
||||
* Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
|
||||
* Note that some letters do not have a dagesh presForm encoded.
|
||||
*/
|
||||
static const hb_codepoint_t sDageshForms[0x05EA - 0x05D0 + 1] = {
|
||||
0xFB30, /* ALEF */
|
||||
0xFB31, /* BET */
|
||||
0xFB32, /* GIMEL */
|
||||
0xFB33, /* DALET */
|
||||
0xFB34, /* HE */
|
||||
0xFB35, /* VAV */
|
||||
0xFB36, /* ZAYIN */
|
||||
0x0000, /* HET */
|
||||
0xFB38, /* TET */
|
||||
0xFB39, /* YOD */
|
||||
0xFB3A, /* FINAL KAF */
|
||||
0xFB3B, /* KAF */
|
||||
0xFB3C, /* LAMED */
|
||||
0x0000, /* FINAL MEM */
|
||||
0xFB3E, /* MEM */
|
||||
0x0000, /* FINAL NUN */
|
||||
0xFB40, /* NUN */
|
||||
0xFB41, /* SAMEKH */
|
||||
0x0000, /* AYIN */
|
||||
0xFB43, /* FINAL PE */
|
||||
0xFB44, /* PE */
|
||||
0x0000, /* FINAL TSADI */
|
||||
0xFB46, /* TSADI */
|
||||
0xFB47, /* QOF */
|
||||
0xFB48, /* RESH */
|
||||
0xFB49, /* SHIN */
|
||||
0xFB4A /* TAV */
|
||||
};
|
||||
|
||||
bool found = c->unicode->compose (a, b, ab);
|
||||
|
||||
if (!found && (b & ~0x7F) == 0x0580) {
|
||||
/* Special-case Hebrew presentation forms that are excluded from
|
||||
* standard normalization, but wanted for old fonts. */
|
||||
switch (b) {
|
||||
case 0x05B4: /* HIRIQ */
|
||||
if (a == 0x05D9) { /* YOD */
|
||||
*ab = 0xFB1D;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
case 0x05B7: /* patah */
|
||||
if (a == 0x05F2) { /* YIDDISH YOD YOD */
|
||||
*ab = 0xFB1F;
|
||||
found = true;
|
||||
} else if (a == 0x05D0) { /* ALEF */
|
||||
*ab = 0xFB2E;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
case 0x05B8: /* QAMATS */
|
||||
if (a == 0x05D0) { /* ALEF */
|
||||
*ab = 0xFB2F;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
case 0x05B9: /* HOLAM */
|
||||
if (a == 0x05D5) { /* VAV */
|
||||
*ab = 0xFB4B;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
case 0x05BC: /* DAGESH */
|
||||
if (a >= 0x05D0 && a <= 0x05EA) {
|
||||
*ab = sDageshForms[a - 0x05D0];
|
||||
found = (*ab != 0);
|
||||
} else if (a == 0xFB2A) { /* SHIN WITH SHIN DOT */
|
||||
*ab = 0xFB2C;
|
||||
found = true;
|
||||
} else if (a == 0xFB2B) { /* SHIN WITH SIN DOT */
|
||||
*ab = 0xFB2D;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
case 0x05BF: /* RAFE */
|
||||
switch (a) {
|
||||
case 0x05D1: /* BET */
|
||||
*ab = 0xFB4C;
|
||||
found = true;
|
||||
break;
|
||||
case 0x05DB: /* KAF */
|
||||
*ab = 0xFB4D;
|
||||
found = true;
|
||||
break;
|
||||
case 0x05E4: /* PE */
|
||||
*ab = 0xFB4E;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x05C1: /* SHIN DOT */
|
||||
if (a == 0x05E9) { /* SHIN */
|
||||
*ab = 0xFB2A;
|
||||
found = true;
|
||||
} else if (a == 0xFB49) { /* SHIN WITH DAGESH */
|
||||
*ab = 0xFB2C;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
case 0x05C2: /* SIN DOT */
|
||||
if (a == 0x05E9) { /* SHIN */
|
||||
*ab = 0xFB2B;
|
||||
found = true;
|
||||
} else if (a == 0xFB49) { /* SHIN WITH DAGESH */
|
||||
*ab = 0xFB2D;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default =
|
||||
{
|
||||
"default",
|
||||
collect_features_default,
|
||||
NULL, /* collect_features */
|
||||
NULL, /* override_features */
|
||||
NULL, /* data_create */
|
||||
NULL, /* data_destroy */
|
||||
NULL, /* preprocess_text */
|
||||
normalization_preference_default,
|
||||
HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
|
||||
NULL, /* decompose */
|
||||
compose_default,
|
||||
NULL, /* compose */
|
||||
NULL, /* setup_masks */
|
||||
HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE,
|
||||
HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT,
|
||||
true, /* fallback_position */
|
||||
};
|
||||
|
|
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* Copyright © 2013 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Google Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#include "hb-ot-shape-complex-private.hh"
|
||||
|
||||
|
||||
/* Hangul shaper */
|
||||
|
||||
|
||||
/* Same order as the feature array below */
|
||||
enum {
|
||||
NONE,
|
||||
|
||||
LJMO,
|
||||
VJMO,
|
||||
TJMO,
|
||||
|
||||
FIRST_HANGUL_FEATURE = LJMO,
|
||||
HANGUL_FEATURE_COUNT = TJMO + 1
|
||||
};
|
||||
|
||||
static const hb_tag_t hangul_features[HANGUL_FEATURE_COUNT] =
|
||||
{
|
||||
HB_TAG_NONE,
|
||||
HB_TAG('l','j','m','o'),
|
||||
HB_TAG('v','j','m','o'),
|
||||
HB_TAG('t','j','m','o')
|
||||
};
|
||||
|
||||
static void
|
||||
collect_features_hangul (hb_ot_shape_planner_t *plan)
|
||||
{
|
||||
hb_ot_map_builder_t *map = &plan->map;
|
||||
|
||||
for (unsigned int i = FIRST_HANGUL_FEATURE; i < HANGUL_FEATURE_COUNT; i++)
|
||||
map->add_feature (hangul_features[i], 1, F_NONE);
|
||||
}
|
||||
|
||||
struct hangul_shape_plan_t
|
||||
{
|
||||
ASSERT_POD ();
|
||||
|
||||
hb_mask_t mask_array[HANGUL_FEATURE_COUNT];
|
||||
};
|
||||
|
||||
static void *
|
||||
data_create_hangul (const hb_ot_shape_plan_t *plan)
|
||||
{
|
||||
hangul_shape_plan_t *hangul_plan = (hangul_shape_plan_t *) calloc (1, sizeof (hangul_shape_plan_t));
|
||||
if (unlikely (!hangul_plan))
|
||||
return NULL;
|
||||
|
||||
for (unsigned int i = 0; i < HANGUL_FEATURE_COUNT; i++)
|
||||
hangul_plan->mask_array[i] = plan->map.get_1_mask (hangul_features[i]);
|
||||
|
||||
return hangul_plan;
|
||||
}
|
||||
|
||||
static void
|
||||
data_destroy_hangul (void *data)
|
||||
{
|
||||
free (data);
|
||||
}
|
||||
|
||||
/* Constants for algorithmic hangul syllable [de]composition. */
|
||||
#define LBase 0x1100
|
||||
#define VBase 0x1161
|
||||
#define TBase 0x11A7
|
||||
#define LCount 19
|
||||
#define VCount 21
|
||||
#define TCount 28
|
||||
#define SBase 0xAC00
|
||||
#define NCount (VCount * TCount)
|
||||
#define SCount (LCount * NCount)
|
||||
|
||||
#define isCombiningL(u) (hb_in_range<hb_codepoint_t> ((u), LBase, LBase+LCount-1))
|
||||
#define isCombiningV(u) (hb_in_range<hb_codepoint_t> ((u), VBase, VBase+VCount-1))
|
||||
#define isCombiningT(u) (hb_in_range<hb_codepoint_t> ((u), TBase+1, TBase+TCount-1))
|
||||
#define isCombinedS(u) (hb_in_range<hb_codepoint_t> ((u), SBase, SBase+SCount-1))
|
||||
|
||||
#define isL(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x1100, 0x115F, 0xA960, 0xA97C))
|
||||
#define isV(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x1160, 0x11A7, 0xD7B0, 0xD7C6))
|
||||
#define isT(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x11A8, 0x11FF, 0xD7CB, 0xD7FB))
|
||||
|
||||
#define isHangulTone(u) (hb_in_range<hb_codepoint_t> ((u), 0x302e, 0x302f))
|
||||
|
||||
/* buffer var allocations */
|
||||
#define hangul_shaping_feature() complex_var_u8_0() /* hangul jamo shaping feature */
|
||||
|
||||
static bool
|
||||
is_zero_width_char (hb_font_t *font,
|
||||
hb_codepoint_t unicode)
|
||||
{
|
||||
hb_codepoint_t glyph;
|
||||
return hb_font_get_glyph (font, unicode, 0, &glyph) && hb_font_get_glyph_h_advance (font, glyph) == 0;
|
||||
}
|
||||
|
||||
static void
|
||||
preprocess_text_hangul (const hb_ot_shape_plan_t *plan,
|
||||
hb_buffer_t *buffer,
|
||||
hb_font_t *font)
|
||||
{
|
||||
HB_BUFFER_ALLOCATE_VAR (buffer, hangul_shaping_feature);
|
||||
|
||||
/* Hangul syllables come in two shapes: LV, and LVT. Of those:
|
||||
*
|
||||
* - LV can be precomposed, or decomposed. Lets call those
|
||||
* <LV> and <L,V>,
|
||||
* - LVT can be fully precomposed, partically precomposed, or
|
||||
* fully decomposed. Ie. <LVT>, <LV,T>, or <L,V,T>.
|
||||
*
|
||||
* The composition / decomposition is mechanical. However, not
|
||||
* all <L,V> sequences compose, and not all <LV,T> sequences
|
||||
* compose.
|
||||
*
|
||||
* Here are the specifics:
|
||||
*
|
||||
* - <L>: U+1100..115F, U+A960..A97F
|
||||
* - <V>: U+1160..11A7, U+D7B0..D7C7
|
||||
* - <T>: U+11A8..11FF, U+D7CB..D7FB
|
||||
*
|
||||
* - Only the <L,V> sequences for the 11xx ranges combine.
|
||||
* - Only <LV,T> sequences for T in U+11A8..11C3 combine.
|
||||
*
|
||||
* Here is what we want to accomplish in this shaper:
|
||||
*
|
||||
* - If the whole syllable can be precomposed, do that,
|
||||
* - Otherwise, fully decompose and apply ljmo/vjmo/tjmo features.
|
||||
* - If a valid syllable is followed by a Hangul tone mark, reorder the tone
|
||||
* mark to precede the whole syllable - unless it is a zero-width glyph, in
|
||||
* which case we leave it untouched, assuming it's designed to overstrike.
|
||||
*
|
||||
* That is, of the different possible syllables:
|
||||
*
|
||||
* <L>
|
||||
* <L,V>
|
||||
* <L,V,T>
|
||||
* <LV>
|
||||
* <LVT>
|
||||
* <LV, T>
|
||||
*
|
||||
* - <L> needs no work.
|
||||
*
|
||||
* - <LV> and <LVT> can stay the way they are if the font supports them, otherwise we
|
||||
* should fully decompose them if font supports.
|
||||
*
|
||||
* - <L,V> and <L,V,T> we should compose if the whole thing can be composed.
|
||||
*
|
||||
* - <LV,T> we should compose if the whole thing can be composed, otherwise we should
|
||||
* decompose.
|
||||
*/
|
||||
|
||||
buffer->clear_output ();
|
||||
unsigned int start = 0, end = 0; /* Extent of most recently seen syllable;
|
||||
* valid only if start < end
|
||||
*/
|
||||
unsigned int count = buffer->len;
|
||||
|
||||
for (buffer->idx = 0; buffer->idx < count;)
|
||||
{
|
||||
hb_codepoint_t u = buffer->cur().codepoint;
|
||||
|
||||
if (isHangulTone (u))
|
||||
{
|
||||
/*
|
||||
* We could cache the width of the tone marks and the existence of dotted-circle,
|
||||
* but the use of the Hangul tone mark characters seems to be rare enough that
|
||||
* I didn't bother for now.
|
||||
*/
|
||||
if (start < end && end == buffer->out_len)
|
||||
{
|
||||
/* Tone mark follows a valid syllable; move it in front, unless it's zero width. */
|
||||
buffer->next_glyph ();
|
||||
if (!is_zero_width_char (font, u))
|
||||
{
|
||||
hb_glyph_info_t *info = buffer->out_info;
|
||||
hb_glyph_info_t tone = info[end];
|
||||
memmove (&info[start + 1], &info[start], (end - start) * sizeof (hb_glyph_info_t));
|
||||
info[start] = tone;
|
||||
}
|
||||
/* Merge clusters across the (possibly reordered) syllable+tone.
|
||||
* We want to merge even in the zero-width tone mark case here,
|
||||
* so that clustering behavior isn't dependent on how the tone mark
|
||||
* is handled by the font.
|
||||
*/
|
||||
buffer->merge_out_clusters (start, end + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No valid syllable as base for tone mark; try to insert dotted circle. */
|
||||
if (font->has_glyph (0x25cc))
|
||||
{
|
||||
hb_codepoint_t chars[2];
|
||||
if (!is_zero_width_char (font, u)) {
|
||||
chars[0] = u;
|
||||
chars[1] = 0x25cc;
|
||||
} else {
|
||||
chars[0] = 0x25cc;
|
||||
chars[1] = u;
|
||||
}
|
||||
buffer->replace_glyphs (1, 2, chars);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No dotted circle available in the font; just leave tone mark untouched. */
|
||||
buffer->next_glyph ();
|
||||
}
|
||||
}
|
||||
start = end = buffer->out_len;
|
||||
continue;
|
||||
}
|
||||
|
||||
start = buffer->out_len; /* Remember current position as a potential syllable start;
|
||||
* will only be used if we set end to a later position.
|
||||
*/
|
||||
|
||||
if (isL (u) && buffer->idx + 1 < count)
|
||||
{
|
||||
hb_codepoint_t l = u;
|
||||
hb_codepoint_t v = buffer->cur(+1).codepoint;
|
||||
if (isV (v))
|
||||
{
|
||||
/* Have <L,V> or <L,V,T>. */
|
||||
hb_codepoint_t t = 0;
|
||||
unsigned int tindex = 0;
|
||||
if (buffer->idx + 2 < count)
|
||||
{
|
||||
t = buffer->cur(+2).codepoint;
|
||||
if (isT (t))
|
||||
tindex = t - TBase; /* Only used if isCombiningT (t); otherwise invalid. */
|
||||
else
|
||||
t = 0; /* The next character was not a trailing jamo. */
|
||||
}
|
||||
|
||||
/* We've got a syllable <L,V,T?>; see if it can potentially be composed. */
|
||||
if (isCombiningL (l) && isCombiningV (v) && (t == 0 || isCombiningT (t)))
|
||||
{
|
||||
/* Try to compose; if this succeeds, end is set to start+1. */
|
||||
hb_codepoint_t s = SBase + (l - LBase) * NCount + (v - VBase) * TCount + tindex;
|
||||
if (font->has_glyph (s))
|
||||
{
|
||||
buffer->replace_glyphs (t ? 3 : 2, 1, &s);
|
||||
if (unlikely (buffer->in_error))
|
||||
return;
|
||||
end = start + 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* We didn't compose, either because it's an Old Hangul syllable without a
|
||||
* precomposed character in Unicode, or because the font didn't support the
|
||||
* necessary precomposed glyph.
|
||||
* Set jamo features on the individual glyphs, and advance past them.
|
||||
*/
|
||||
buffer->cur().hangul_shaping_feature() = LJMO;
|
||||
buffer->next_glyph ();
|
||||
buffer->cur().hangul_shaping_feature() = VJMO;
|
||||
buffer->next_glyph ();
|
||||
if (t)
|
||||
{
|
||||
buffer->cur().hangul_shaping_feature() = TJMO;
|
||||
buffer->next_glyph ();
|
||||
end = start + 3;
|
||||
}
|
||||
else
|
||||
end = start + 2;
|
||||
buffer->merge_out_clusters (start, end);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
else if (isCombinedS (u))
|
||||
{
|
||||
/* Have <LV>, <LVT>, or <LV,T> */
|
||||
hb_codepoint_t s = u;
|
||||
bool has_glyph = font->has_glyph (s);
|
||||
unsigned int lindex = (s - SBase) / NCount;
|
||||
unsigned int nindex = (s - SBase) % NCount;
|
||||
unsigned int vindex = nindex / TCount;
|
||||
unsigned int tindex = nindex % TCount;
|
||||
|
||||
if (!tindex &&
|
||||
buffer->idx + 1 < count &&
|
||||
isCombiningT (buffer->cur(+1).codepoint))
|
||||
{
|
||||
/* <LV,T>, try to combine. */
|
||||
unsigned int new_tindex = buffer->cur(+1).codepoint - TBase;
|
||||
hb_codepoint_t new_s = s + new_tindex;
|
||||
if (font->has_glyph (new_s))
|
||||
{
|
||||
buffer->replace_glyphs (2, 1, &new_s);
|
||||
if (unlikely (buffer->in_error))
|
||||
return;
|
||||
end = start + 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Otherwise, decompose if font doesn't support <LV> or <LVT>,
|
||||
* or if having non-combining <LV,T>. Note that we already handled
|
||||
* combining <LV,T> above. */
|
||||
if (!has_glyph ||
|
||||
(!tindex &&
|
||||
buffer->idx + 1 < count &&
|
||||
isT (buffer->cur(+1).codepoint)))
|
||||
{
|
||||
hb_codepoint_t decomposed[3] = {LBase + lindex,
|
||||
VBase + vindex,
|
||||
TBase + tindex};
|
||||
if (font->has_glyph (decomposed[0]) &&
|
||||
font->has_glyph (decomposed[1]) &&
|
||||
(!tindex || font->has_glyph (decomposed[2])))
|
||||
{
|
||||
unsigned int s_len = tindex ? 3 : 2;
|
||||
buffer->replace_glyphs (1, s_len, decomposed);
|
||||
if (unlikely (buffer->in_error))
|
||||
return;
|
||||
|
||||
/* We decomposed S: apply jamo features to the individual glyphs
|
||||
* that are now in buffer->out_info.
|
||||
*/
|
||||
hb_glyph_info_t *info = buffer->out_info;
|
||||
|
||||
/* If we decomposed an LV because of a non-combining T following,
|
||||
* we want to include this T in the syllable.
|
||||
*/
|
||||
if (has_glyph && !tindex)
|
||||
{
|
||||
buffer->next_glyph ();
|
||||
s_len++;
|
||||
}
|
||||
end = start + s_len;
|
||||
|
||||
unsigned int i = start;
|
||||
info[i++].hangul_shaping_feature() = LJMO;
|
||||
info[i++].hangul_shaping_feature() = VJMO;
|
||||
if (i < end)
|
||||
info[i++].hangul_shaping_feature() = TJMO;
|
||||
buffer->merge_out_clusters (start, end);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_glyph)
|
||||
{
|
||||
/* We didn't decompose the S, so just advance past it. */
|
||||
end = start + 1;
|
||||
buffer->next_glyph ();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Didn't find a recognizable syllable, so we leave end <= start;
|
||||
* this will prevent tone-mark reordering happening.
|
||||
*/
|
||||
buffer->next_glyph ();
|
||||
}
|
||||
buffer->swap_buffers ();
|
||||
}
|
||||
|
||||
static void
|
||||
setup_masks_hangul (const hb_ot_shape_plan_t *plan,
|
||||
hb_buffer_t *buffer,
|
||||
hb_font_t *font HB_UNUSED)
|
||||
{
|
||||
const hangul_shape_plan_t *hangul_plan = (const hangul_shape_plan_t *) plan->data;
|
||||
|
||||
if (likely (hangul_plan))
|
||||
{
|
||||
unsigned int count = buffer->len;
|
||||
hb_glyph_info_t *info = buffer->info;
|
||||
for (unsigned int i = 0; i < count; i++, info++)
|
||||
info->mask |= hangul_plan->mask_array[info->hangul_shaping_feature()];
|
||||
}
|
||||
|
||||
HB_BUFFER_DEALLOCATE_VAR (buffer, hangul_shaping_feature);
|
||||
}
|
||||
|
||||
|
||||
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hangul =
|
||||
{
|
||||
"hangul",
|
||||
collect_features_hangul,
|
||||
NULL, /* override_features */
|
||||
data_create_hangul, /* data_create */
|
||||
data_destroy_hangul, /* data_destroy */
|
||||
preprocess_text_hangul,
|
||||
HB_OT_SHAPE_NORMALIZATION_MODE_NONE,
|
||||
NULL, /* decompose */
|
||||
NULL, /* compose */
|
||||
setup_masks_hangul, /* setup_masks */
|
||||
HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
|
||||
false, /* fallback_position */
|
||||
};
|
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright © 2010,2012 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Google Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#include "hb-ot-shape-complex-private.hh"
|
||||
|
||||
|
||||
static bool
|
||||
compose_hebrew (const hb_ot_shape_normalize_context_t *c,
|
||||
hb_codepoint_t a,
|
||||
hb_codepoint_t b,
|
||||
hb_codepoint_t *ab)
|
||||
{
|
||||
/* Hebrew presentation-form shaping.
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=728866
|
||||
* Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
|
||||
* Note that some letters do not have a dagesh presForm encoded.
|
||||
*/
|
||||
static const hb_codepoint_t sDageshForms[0x05EA - 0x05D0 + 1] = {
|
||||
0xFB30, /* ALEF */
|
||||
0xFB31, /* BET */
|
||||
0xFB32, /* GIMEL */
|
||||
0xFB33, /* DALET */
|
||||
0xFB34, /* HE */
|
||||
0xFB35, /* VAV */
|
||||
0xFB36, /* ZAYIN */
|
||||
0x0000, /* HET */
|
||||
0xFB38, /* TET */
|
||||
0xFB39, /* YOD */
|
||||
0xFB3A, /* FINAL KAF */
|
||||
0xFB3B, /* KAF */
|
||||
0xFB3C, /* LAMED */
|
||||
0x0000, /* FINAL MEM */
|
||||
0xFB3E, /* MEM */
|
||||
0x0000, /* FINAL NUN */
|
||||
0xFB40, /* NUN */
|
||||
0xFB41, /* SAMEKH */
|
||||
0x0000, /* AYIN */
|
||||
0xFB43, /* FINAL PE */
|
||||
0xFB44, /* PE */
|
||||
0x0000, /* FINAL TSADI */
|
||||
0xFB46, /* TSADI */
|
||||
0xFB47, /* QOF */
|
||||
0xFB48, /* RESH */
|
||||
0xFB49, /* SHIN */
|
||||
0xFB4A /* TAV */
|
||||
};
|
||||
|
||||
bool found = c->unicode->compose (a, b, ab);
|
||||
|
||||
if (!found)
|
||||
{
|
||||
/* Special-case Hebrew presentation forms that are excluded from
|
||||
* standard normalization, but wanted for old fonts. */
|
||||
switch (b) {
|
||||
case 0x05B4: /* HIRIQ */
|
||||
if (a == 0x05D9) { /* YOD */
|
||||
*ab = 0xFB1D;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
case 0x05B7: /* patah */
|
||||
if (a == 0x05F2) { /* YIDDISH YOD YOD */
|
||||
*ab = 0xFB1F;
|
||||
found = true;
|
||||
} else if (a == 0x05D0) { /* ALEF */
|
||||
*ab = 0xFB2E;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
case 0x05B8: /* QAMATS */
|
||||
if (a == 0x05D0) { /* ALEF */
|
||||
*ab = 0xFB2F;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
case 0x05B9: /* HOLAM */
|
||||
if (a == 0x05D5) { /* VAV */
|
||||
*ab = 0xFB4B;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
case 0x05BC: /* DAGESH */
|
||||
if (a >= 0x05D0 && a <= 0x05EA) {
|
||||
*ab = sDageshForms[a - 0x05D0];
|
||||
found = (*ab != 0);
|
||||
} else if (a == 0xFB2A) { /* SHIN WITH SHIN DOT */
|
||||
*ab = 0xFB2C;
|
||||
found = true;
|
||||
} else if (a == 0xFB2B) { /* SHIN WITH SIN DOT */
|
||||
*ab = 0xFB2D;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
case 0x05BF: /* RAFE */
|
||||
switch (a) {
|
||||
case 0x05D1: /* BET */
|
||||
*ab = 0xFB4C;
|
||||
found = true;
|
||||
break;
|
||||
case 0x05DB: /* KAF */
|
||||
*ab = 0xFB4D;
|
||||
found = true;
|
||||
break;
|
||||
case 0x05E4: /* PE */
|
||||
*ab = 0xFB4E;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 0x05C1: /* SHIN DOT */
|
||||
if (a == 0x05E9) { /* SHIN */
|
||||
*ab = 0xFB2A;
|
||||
found = true;
|
||||
} else if (a == 0xFB49) { /* SHIN WITH DAGESH */
|
||||
*ab = 0xFB2C;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
case 0x05C2: /* SIN DOT */
|
||||
if (a == 0x05E9) { /* SHIN */
|
||||
*ab = 0xFB2B;
|
||||
found = true;
|
||||
} else if (a == 0xFB49) { /* SHIN WITH DAGESH */
|
||||
*ab = 0xFB2D;
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_hebrew =
|
||||
{
|
||||
"hebrew",
|
||||
NULL, /* collect_features */
|
||||
NULL, /* override_features */
|
||||
NULL, /* data_create */
|
||||
NULL, /* data_destroy */
|
||||
NULL, /* preprocess_text */
|
||||
HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
|
||||
NULL, /* decompose */
|
||||
compose_hebrew,
|
||||
NULL, /* setup_masks */
|
||||
HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT,
|
||||
true, /* fallback_position */
|
||||
};
|
|
@ -1690,12 +1690,6 @@ clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
|
|||
}
|
||||
|
||||
|
||||
static hb_ot_shape_normalization_mode_t
|
||||
normalization_preference_indic (const hb_segment_properties_t *props HB_UNUSED)
|
||||
{
|
||||
return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
|
||||
}
|
||||
|
||||
static bool
|
||||
decompose_indic (const hb_ot_shape_normalize_context_t *c,
|
||||
hb_codepoint_t ab,
|
||||
|
@ -1806,7 +1800,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic =
|
|||
data_create_indic,
|
||||
data_destroy_indic,
|
||||
NULL, /* preprocess_text */
|
||||
normalization_preference_indic,
|
||||
HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
|
||||
decompose_indic,
|
||||
compose_indic,
|
||||
setup_masks_indic,
|
||||
|
|
|
@ -541,13 +541,6 @@ final_reordering (const hb_ot_shape_plan_t *plan,
|
|||
}
|
||||
|
||||
|
||||
static hb_ot_shape_normalization_mode_t
|
||||
normalization_preference_myanmar (const hb_segment_properties_t *props HB_UNUSED)
|
||||
{
|
||||
return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
|
||||
}
|
||||
|
||||
|
||||
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
|
||||
{
|
||||
"myanmar",
|
||||
|
@ -556,7 +549,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
|
|||
NULL, /* data_create */
|
||||
NULL, /* data_destroy */
|
||||
NULL, /* preprocess_text */
|
||||
normalization_preference_myanmar,
|
||||
HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
|
||||
NULL, /* decompose */
|
||||
NULL, /* compose */
|
||||
setup_masks_myanmar,
|
||||
|
|
|
@ -44,7 +44,9 @@ enum hb_ot_shape_zero_width_marks_type_t {
|
|||
// HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_EARLY,
|
||||
HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE,
|
||||
HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
|
||||
HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE
|
||||
HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
|
||||
|
||||
HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT = HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE
|
||||
};
|
||||
|
||||
|
||||
|
@ -52,10 +54,13 @@ enum hb_ot_shape_zero_width_marks_type_t {
|
|||
#define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
|
||||
HB_COMPLEX_SHAPER_IMPLEMENT (default) /* should be first */ \
|
||||
HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \
|
||||
HB_COMPLEX_SHAPER_IMPLEMENT (hangul) \
|
||||
HB_COMPLEX_SHAPER_IMPLEMENT (hebrew) \
|
||||
HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
|
||||
HB_COMPLEX_SHAPER_IMPLEMENT (myanmar) \
|
||||
HB_COMPLEX_SHAPER_IMPLEMENT (sea) \
|
||||
HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
|
||||
HB_COMPLEX_SHAPER_IMPLEMENT (tibetan) \
|
||||
/* ^--- Add new shapers here */
|
||||
|
||||
|
||||
|
@ -105,12 +110,7 @@ struct hb_ot_complex_shaper_t
|
|||
hb_font_t *font);
|
||||
|
||||
|
||||
/* normalization_preference()
|
||||
* Called during shape().
|
||||
* May be NULL.
|
||||
*/
|
||||
hb_ot_shape_normalization_mode_t
|
||||
(*normalization_preference) (const hb_segment_properties_t *props);
|
||||
hb_ot_shape_normalization_mode_t normalization_preference;
|
||||
|
||||
/* decompose()
|
||||
* Called during shape()'s normalization.
|
||||
|
@ -189,19 +189,22 @@ hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner)
|
|||
return &_hb_ot_complex_shaper_thai;
|
||||
|
||||
|
||||
#if 0
|
||||
/* Note:
|
||||
* Currently we don't have a separate Hangul shaper. The default shaper handles
|
||||
* Hangul by enabling jamo features. We may want to implement a separate shaper
|
||||
* in the future. See this thread for details of what such a shaper would do:
|
||||
*
|
||||
* http://lists.freedesktop.org/archives/harfbuzz/2013-April/003070.html
|
||||
*/
|
||||
/* Unicode-1.1 additions */
|
||||
case HB_SCRIPT_HANGUL:
|
||||
|
||||
return &_hb_ot_complex_shaper_hangul;
|
||||
#endif
|
||||
|
||||
|
||||
/* Unicode-2.0 additions */
|
||||
case HB_SCRIPT_TIBETAN:
|
||||
|
||||
return &_hb_ot_complex_shaper_tibetan;
|
||||
|
||||
|
||||
/* Unicode-1.1 additions */
|
||||
case HB_SCRIPT_HEBREW:
|
||||
|
||||
return &_hb_ot_complex_shaper_hebrew;
|
||||
|
||||
|
||||
/* ^--- Add new shapers here */
|
||||
|
@ -241,9 +244,6 @@ hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner)
|
|||
case HB_SCRIPT_LAO:
|
||||
case HB_SCRIPT_THAI:
|
||||
|
||||
/* Unicode-2.0 additions */
|
||||
case HB_SCRIPT_TIBETAN:
|
||||
|
||||
/* Unicode-3.2 additions */
|
||||
case HB_SCRIPT_TAGALOG:
|
||||
case HB_SCRIPT_TAGBANWA:
|
||||
|
|
|
@ -360,13 +360,6 @@ final_reordering (const hb_ot_shape_plan_t *plan,
|
|||
}
|
||||
|
||||
|
||||
static hb_ot_shape_normalization_mode_t
|
||||
normalization_preference_sea (const hb_segment_properties_t *props HB_UNUSED)
|
||||
{
|
||||
return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
|
||||
}
|
||||
|
||||
|
||||
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_sea =
|
||||
{
|
||||
"sea",
|
||||
|
@ -375,7 +368,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_sea =
|
|||
NULL, /* data_create */
|
||||
NULL, /* data_destroy */
|
||||
NULL, /* preprocess_text */
|
||||
normalization_preference_sea,
|
||||
HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
|
||||
NULL, /* decompose */
|
||||
NULL, /* compose */
|
||||
setup_masks_sea,
|
||||
|
|
|
@ -369,10 +369,10 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_thai =
|
|||
NULL, /* data_create */
|
||||
NULL, /* data_destroy */
|
||||
preprocess_text_thai,
|
||||
NULL, /* normalization_preference */
|
||||
HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
|
||||
NULL, /* decompose */
|
||||
NULL, /* compose */
|
||||
NULL, /* setup_masks */
|
||||
HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE_LATE,
|
||||
HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT,
|
||||
false,/* fallback_position */
|
||||
};
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright © 2010,2012 Google, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Google Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#include "hb-ot-shape-complex-private.hh"
|
||||
|
||||
|
||||
static const hb_tag_t tibetan_features[] =
|
||||
{
|
||||
HB_TAG('a','b','v','s'),
|
||||
HB_TAG('b','l','w','s'),
|
||||
HB_TAG('a','b','v','m'),
|
||||
HB_TAG('b','l','w','m'),
|
||||
HB_TAG_NONE
|
||||
};
|
||||
|
||||
static void
|
||||
collect_features_tibetan (hb_ot_shape_planner_t *plan)
|
||||
{
|
||||
for (const hb_tag_t *script_features = tibetan_features; script_features && *script_features; script_features++)
|
||||
plan->map.add_global_bool_feature (*script_features);
|
||||
}
|
||||
|
||||
|
||||
const hb_ot_complex_shaper_t _hb_ot_complex_shaper_tibetan =
|
||||
{
|
||||
"default",
|
||||
collect_features_tibetan,
|
||||
NULL, /* override_features */
|
||||
NULL, /* data_create */
|
||||
NULL, /* data_destroy */
|
||||
NULL, /* preprocess_text */
|
||||
HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT,
|
||||
NULL, /* decompose */
|
||||
NULL, /* compose */
|
||||
NULL, /* setup_masks */
|
||||
HB_OT_SHAPE_ZERO_WIDTH_MARKS_DEFAULT,
|
||||
true, /* fallback_position */
|
||||
};
|
|
@ -430,14 +430,12 @@ _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan,
|
|||
hb_font_t *font,
|
||||
hb_buffer_t *buffer)
|
||||
{
|
||||
hb_mask_t kern_mask = plan->map.get_1_mask (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction) ?
|
||||
HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n'));
|
||||
if (!kern_mask) return;
|
||||
if (!plan->has_kern) return;
|
||||
|
||||
unsigned int count = buffer->len;
|
||||
|
||||
OT::hb_apply_context_t c (1, font, buffer);
|
||||
c.set_lookup_mask (kern_mask);
|
||||
c.set_lookup_mask (plan->kern_mask);
|
||||
c.set_lookup_props (OT::LookupFlag::IgnoreMarks);
|
||||
|
||||
hb_glyph_info_t *info = buffer->info;
|
||||
|
|
|
@ -29,8 +29,6 @@
|
|||
|
||||
#include "hb-private.hh"
|
||||
|
||||
#include "hb-font.h"
|
||||
#include "hb-buffer.h"
|
||||
|
||||
/* buffer var allocations, used during the normalization process */
|
||||
#define glyph_index() var1.u32
|
||||
|
@ -38,6 +36,7 @@
|
|||
struct hb_ot_shape_plan_t;
|
||||
|
||||
enum hb_ot_shape_normalization_mode_t {
|
||||
HB_OT_SHAPE_NORMALIZATION_MODE_NONE,
|
||||
HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED,
|
||||
HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* never composes base-to-base */
|
||||
HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, /* always fully decomposes and then recompose back */
|
||||
|
|
|
@ -213,8 +213,9 @@ decompose_current_character (const hb_ot_shape_normalize_context_t *c, bool shor
|
|||
}
|
||||
|
||||
static inline void
|
||||
handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end)
|
||||
handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool short_circuit)
|
||||
{
|
||||
/* TODO Currently if there's a variation-selector we give-up, it's just too hard. */
|
||||
hb_buffer_t * const buffer = c->buffer;
|
||||
hb_font_t * const font = c->font;
|
||||
for (; buffer->idx < end - 1;) {
|
||||
|
@ -250,27 +251,26 @@ handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, uns
|
|||
}
|
||||
|
||||
static inline void
|
||||
decompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end)
|
||||
decompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool short_circuit)
|
||||
{
|
||||
hb_buffer_t * const buffer = c->buffer;
|
||||
/* TODO Currently if there's a variation-selector we give-up, it's just too hard. */
|
||||
for (unsigned int i = buffer->idx; i < end; i++)
|
||||
if (unlikely (buffer->unicode->is_variation_selector (buffer->info[i].codepoint))) {
|
||||
handle_variation_selector_cluster (c, end);
|
||||
handle_variation_selector_cluster (c, end, short_circuit);
|
||||
return;
|
||||
}
|
||||
|
||||
while (buffer->idx < end)
|
||||
decompose_current_character (c, false);
|
||||
decompose_current_character (c, short_circuit);
|
||||
}
|
||||
|
||||
static inline void
|
||||
decompose_cluster (const hb_ot_shape_normalize_context_t *c, bool short_circuit, unsigned int end)
|
||||
decompose_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool might_short_circuit, bool always_short_circuit)
|
||||
{
|
||||
if (likely (c->buffer->idx + 1 == end))
|
||||
decompose_current_character (c, short_circuit);
|
||||
decompose_current_character (c, might_short_circuit);
|
||||
else
|
||||
decompose_multi_char_cluster (c, end);
|
||||
decompose_multi_char_cluster (c, end, always_short_circuit);
|
||||
}
|
||||
|
||||
|
||||
|
@ -289,9 +289,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan,
|
|||
hb_buffer_t *buffer,
|
||||
hb_font_t *font)
|
||||
{
|
||||
hb_ot_shape_normalization_mode_t mode = plan->shaper->normalization_preference ?
|
||||
plan->shaper->normalization_preference (&buffer->props) :
|
||||
HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT;
|
||||
hb_ot_shape_normalization_mode_t mode = plan->shaper->normalization_preference;
|
||||
const hb_ot_shape_normalize_context_t c = {
|
||||
plan,
|
||||
buffer,
|
||||
|
@ -301,8 +299,10 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan,
|
|||
plan->shaper->compose ? plan->shaper->compose : compose_unicode
|
||||
};
|
||||
|
||||
bool short_circuit = mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED &&
|
||||
mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
|
||||
bool always_short_circuit = mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE;
|
||||
bool might_short_circuit = always_short_circuit ||
|
||||
(mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED &&
|
||||
mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT);
|
||||
unsigned int count;
|
||||
|
||||
/* We do a fairly straightforward yet custom normalization process in three
|
||||
|
@ -323,7 +323,7 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan,
|
|||
if (buffer->cur().cluster != buffer->info[end].cluster)
|
||||
break;
|
||||
|
||||
decompose_cluster (&c, short_circuit, end);
|
||||
decompose_cluster (&c, end, might_short_circuit, always_short_circuit);
|
||||
}
|
||||
buffer->swap_buffers ();
|
||||
|
||||
|
@ -355,7 +355,8 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan,
|
|||
}
|
||||
|
||||
|
||||
if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED)
|
||||
if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE ||
|
||||
mode == HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED)
|
||||
return;
|
||||
|
||||
/* Third round, recompose */
|
||||
|
@ -393,8 +394,9 @@ _hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan,
|
|||
return;
|
||||
buffer->merge_out_clusters (starter, buffer->out_len);
|
||||
buffer->out_len--; /* Remove the second composable. */
|
||||
buffer->out_info[starter].codepoint = composed; /* Modify starter and carry on. */
|
||||
set_glyph (buffer->out_info[starter], font);
|
||||
/* Modify starter and carry on. */
|
||||
buffer->out_info[starter].codepoint = composed;
|
||||
buffer->out_info[starter].glyph_index() = glyph;
|
||||
_hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer->unicode);
|
||||
|
||||
continue;
|
||||
|
|
|
@ -40,6 +40,10 @@ struct hb_ot_shape_plan_t
|
|||
const struct hb_ot_complex_shaper_t *shaper;
|
||||
hb_ot_map_t map;
|
||||
const void *data;
|
||||
hb_mask_t rtlm_mask, frac_mask, numr_mask, dnom_mask;
|
||||
hb_mask_t kern_mask;
|
||||
unsigned int has_frac : 1;
|
||||
unsigned int has_kern : 1;
|
||||
|
||||
inline void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const
|
||||
{
|
||||
|
@ -77,6 +81,17 @@ struct hb_ot_shape_planner_t
|
|||
plan.props = props;
|
||||
plan.shaper = shaper;
|
||||
map.compile (plan.map);
|
||||
|
||||
plan.rtlm_mask = plan.map.get_1_mask (HB_TAG ('r','t','l','m'));
|
||||
plan.frac_mask = plan.map.get_1_mask (HB_TAG ('f','r','a','c'));
|
||||
plan.numr_mask = plan.map.get_1_mask (HB_TAG ('n','u','m','r'));
|
||||
plan.dnom_mask = plan.map.get_1_mask (HB_TAG ('d','n','o','m'));
|
||||
|
||||
plan.kern_mask = plan.map.get_mask (HB_DIRECTION_IS_HORIZONTAL (plan.props.direction) ?
|
||||
HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n'));
|
||||
|
||||
plan.has_frac = plan.frac_mask || (plan.numr_mask && plan.dnom_mask);
|
||||
plan.has_kern = !!plan.kern_mask;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -88,6 +88,10 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner,
|
|||
break;
|
||||
}
|
||||
|
||||
map->add_feature (HB_TAG ('f','r','a','c'), 1, F_NONE);
|
||||
map->add_feature (HB_TAG ('n','u','m','r'), 1, F_NONE);
|
||||
map->add_feature (HB_TAG ('d','n','o','m'), 1, F_NONE);
|
||||
|
||||
if (planner->shaper->collect_features)
|
||||
planner->shaper->collect_features (planner);
|
||||
|
||||
|
@ -234,8 +238,7 @@ hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font)
|
|||
HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
|
||||
return;
|
||||
|
||||
hb_codepoint_t dottedcircle_glyph;
|
||||
if (!font->get_glyph (0x25CC, 0, &dottedcircle_glyph))
|
||||
if (!font->has_glyph (0x25CC))
|
||||
return;
|
||||
|
||||
hb_glyph_info_t dottedcircle;
|
||||
|
@ -292,7 +295,7 @@ hb_ot_mirror_chars (hb_ot_shape_context_t *c)
|
|||
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
hb_unicode_funcs_t *unicode = buffer->unicode;
|
||||
hb_mask_t rtlm_mask = c->plan->map.get_1_mask (HB_TAG ('r','t','l','m'));
|
||||
hb_mask_t rtlm_mask = c->plan->rtlm_mask;
|
||||
|
||||
unsigned int count = buffer->len;
|
||||
hb_glyph_info_t *info = buffer->info;
|
||||
|
@ -306,13 +309,58 @@ hb_ot_mirror_chars (hb_ot_shape_context_t *c)
|
|||
}
|
||||
|
||||
static inline void
|
||||
hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
|
||||
hb_ot_shape_setup_masks_fraction (hb_ot_shape_context_t *c)
|
||||
{
|
||||
if (!c->plan->has_frac)
|
||||
return;
|
||||
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
|
||||
/* TODO look in pre/post context text also. */
|
||||
unsigned int count = buffer->len;
|
||||
hb_glyph_info_t *info = buffer->info;
|
||||
for (unsigned int i = 0; i < count; i++)
|
||||
{
|
||||
if (info[i].codepoint == 0x2044) /* FRACTION SLASH */
|
||||
{
|
||||
unsigned int start = i, end = i + 1;
|
||||
while (start &&
|
||||
_hb_glyph_info_get_general_category (&info[start - 1]) ==
|
||||
HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
|
||||
start--;
|
||||
while (end < count &&
|
||||
_hb_glyph_info_get_general_category (&info[end]) ==
|
||||
HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER)
|
||||
end++;
|
||||
|
||||
for (unsigned int j = start; j < i; j++)
|
||||
info[j].mask |= c->plan->numr_mask | c->plan->frac_mask;
|
||||
info[i].mask |= c->plan->frac_mask;
|
||||
for (unsigned int j = i + 1; j < end; j++)
|
||||
info[j].mask |= c->plan->frac_mask | c->plan->dnom_mask;
|
||||
|
||||
i = end - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
hb_ot_shape_initialize_masks (hb_ot_shape_context_t *c)
|
||||
{
|
||||
hb_ot_map_t *map = &c->plan->map;
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
|
||||
hb_mask_t global_mask = map->get_global_mask ();
|
||||
buffer->reset_masks (global_mask);
|
||||
}
|
||||
|
||||
static inline void
|
||||
hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
|
||||
{
|
||||
hb_ot_map_t *map = &c->plan->map;
|
||||
hb_buffer_t *buffer = c->buffer;
|
||||
|
||||
hb_ot_shape_setup_masks_fraction (c);
|
||||
|
||||
if (c->plan->shaper->setup_masks)
|
||||
c->plan->shaper->setup_masks (c->plan, buffer, c->font);
|
||||
|
@ -358,6 +406,8 @@ hb_ot_substitute_default (hb_ot_shape_context_t *c)
|
|||
if (c->plan->shaper->preprocess_text)
|
||||
c->plan->shaper->preprocess_text (c->plan, buffer, c->font);
|
||||
|
||||
hb_ot_shape_initialize_masks (c);
|
||||
|
||||
hb_ot_mirror_chars (c);
|
||||
|
||||
HB_BUFFER_ALLOCATE_VAR (buffer, glyph_index);
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright © 2013 Red Hat, Inc.
|
||||
*
|
||||
* This is part of HarfBuzz, a text shaping library.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without
|
||||
* license or royalty fees, to use, copy, modify, and distribute this
|
||||
* software and its documentation for any purpose, provided that the
|
||||
* above copyright notice and the following two paragraphs appear in
|
||||
* all copies of this software.
|
||||
*
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
|
||||
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
||||
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
|
||||
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
* DAMAGE.
|
||||
*
|
||||
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
|
||||
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
|
||||
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
*
|
||||
* Red Hat Author(s): Behdad Esfahbod
|
||||
*/
|
||||
|
||||
#ifndef HB_OT_SHAPE_H
|
||||
#define HB_OT_SHAPE_H
|
||||
#define HB_OT_SHAPE_H_IN
|
||||
|
||||
#include "hb.h"
|
||||
|
||||
#include "hb-ot-layout.h"
|
||||
#include "hb-ot-tag.h"
|
||||
|
||||
HB_BEGIN_DECLS
|
||||
|
||||
/* TODO port to shape-plan / set. */
|
||||
void
|
||||
hb_ot_shape_glyphs_closure (hb_font_t *font,
|
||||
hb_buffer_t *buffer,
|
||||
const hb_feature_t *features,
|
||||
unsigned int num_features,
|
||||
hb_set_t *glyphs);
|
||||
|
||||
void
|
||||
hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
|
||||
hb_tag_t table_tag,
|
||||
hb_set_t *lookup_indexes /* OUT */);
|
||||
|
||||
HB_END_DECLS
|
||||
|
||||
#undef HB_OT_SHAPE_H_IN
|
||||
#endif /* HB_OT_SHAPE_H */
|
|
@ -27,7 +27,6 @@
|
|||
*/
|
||||
|
||||
#include "hb-private.hh"
|
||||
#include "hb-ot.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
|
|
@ -32,17 +32,10 @@
|
|||
|
||||
#include "hb-ot-layout.h"
|
||||
#include "hb-ot-tag.h"
|
||||
#include "hb-ot-shape.h"
|
||||
|
||||
HB_BEGIN_DECLS
|
||||
|
||||
/* TODO remove */
|
||||
void
|
||||
hb_ot_shape_glyphs_closure (hb_font_t *font,
|
||||
hb_buffer_t *buffer,
|
||||
const hb_feature_t *features,
|
||||
unsigned int num_features,
|
||||
hb_set_t *glyphs);
|
||||
|
||||
HB_END_DECLS
|
||||
|
||||
#undef HB_OT_H_IN
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче