From 798f3a1a554f0d753e5a61689f8bfdd222c7411d Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Tue, 26 Mar 2013 15:14:28 -0700 Subject: [PATCH 01/73] Bug 851965 - Remove this crashtest due to hanging. r=gwagner --HG-- extra : rebase_source : 4e111710c04c7325d739c07372c76423e84a443d --- .../tests/crashtests/460706-1.xhtml | 463 ------------------ .../tests/crashtests/crashtests.list | 1 - 2 files changed, 464 deletions(-) delete mode 100644 parser/htmlparser/tests/crashtests/460706-1.xhtml diff --git a/parser/htmlparser/tests/crashtests/460706-1.xhtml b/parser/htmlparser/tests/crashtests/460706-1.xhtml deleted file mode 100644 index 70383af65641..000000000000 --- a/parser/htmlparser/tests/crashtests/460706-1.xhtml +++ /dev/null @@ -1,463 +0,0 @@ - - - - - - - - - - - diff --git a/parser/htmlparser/tests/crashtests/crashtests.list b/parser/htmlparser/tests/crashtests/crashtests.list index 50eb99f19bf9..ecd845ddc22d 100644 --- a/parser/htmlparser/tests/crashtests/crashtests.list +++ b/parser/htmlparser/tests/crashtests/crashtests.list @@ -34,7 +34,6 @@ asserts-if(Android,1) skip-if((OSX==10.7||OSX==10.8)&&browserIsRemote) load 3287 load 408939-1.html load 423373-1.html load 445171-1.html -skip-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) load 460706-1.xhtml # skip on WinXP - bug 851965 load 468538-1.xhtml load 515278-1.html load 515533-1.html From e9235ac9fea419247e87058bdf23e54896e3bcb1 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Wed, 3 Apr 2013 15:45:35 -0700 Subject: [PATCH 02/73] Bug 855036 - Don't treat a string as an object. r=gwagner --HG-- extra : rebase_source : 98e7ed9d1252847ece46548882d5654f6492ee19 --- dom/system/gonk/GonkGPSGeolocationProvider.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dom/system/gonk/GonkGPSGeolocationProvider.cpp b/dom/system/gonk/GonkGPSGeolocationProvider.cpp index c3b99a63cd98..0f6376c5af09 100644 --- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp +++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp @@ -673,9 +673,10 @@ GonkGPSGeolocationProvider::Handle(const nsAString& aName, JSContext *cx = nsContentUtils::GetCurrentJSContext(); NS_ENSURE_TRUE(cx, NS_OK); JSAutoRequest ar(cx); - JSAutoCompartment ac(cx, JSVAL_TO_OBJECT(aResult)); + // When we get the APN, we attempt to call data_call_open of AGPS. if (aResult.isString()) { + // NB: No need to enter a compartment to read the contents of a string. nsDependentJSString apn; apn.init(cx, aResult.toString()); if (!apn.IsEmpty()) { From d7c5934da8b052fe21885189c52528a9a83cf179 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Wed, 3 Apr 2013 17:28:49 -0400 Subject: [PATCH 03/73] Bug 857790 - Convert AudioParam time values to ticks relative to the source stream, not the destination stream; r=roc --- content/media/webaudio/WebAudioUtils.cpp | 10 +++++----- mfbt/PodOperations.h | 6 ++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/content/media/webaudio/WebAudioUtils.cpp b/content/media/webaudio/WebAudioUtils.cpp index c49edbb3ffae..f7af0256499f 100644 --- a/content/media/webaudio/WebAudioUtils.cpp +++ b/content/media/webaudio/WebAudioUtils.cpp @@ -21,11 +21,11 @@ struct ConvertTimeToTickHelper TrackRate sampleRate = IdealAudioRate(); ConvertTimeToTickHelper* This = static_cast (aClosure); - TrackTicks tick = This->mSourceStream->GetCurrentPosition(); - StreamTime streamTime = TicksToTimeRoundDown(sampleRate, tick); - GraphTime graphTime = This->mSourceStream->StreamTimeToGraphTime(streamTime); - StreamTime destinationStreamTime = This->mDestinationStream->GraphTimeToStreamTime(graphTime); - return TimeToTicksRoundDown(sampleRate, destinationStreamTime + SecondsToMediaTime(aTime)); + TrackTicks tick = This->mDestinationStream->GetCurrentPosition(); + StreamTime destinationStreamTime = TicksToTimeRoundDown(sampleRate, tick); + GraphTime graphTime = This->mDestinationStream->StreamTimeToGraphTime(destinationStreamTime); + StreamTime streamTime = This->mSourceStream->GraphTimeToStreamTime(graphTime); + return TimeToTicksRoundDown(sampleRate, streamTime + SecondsToMediaTime(aTime)); } }; diff --git a/mfbt/PodOperations.h b/mfbt/PodOperations.h index 62480d47980f..fd341650bca7 100644 --- a/mfbt/PodOperations.h +++ b/mfbt/PodOperations.h @@ -14,8 +14,10 @@ #ifndef mozilla_PodOperations_h #define mozilla_PodOperations_h -#include "mozilla/Attributes.h" -#include "mozilla/Util.h" +#define MOZ_DELETE delete +#define MOZ_ASSERT +#define MOZ_ASSERT_IF +#define MOZ_ALWAYS_INLINE __attribute__((always_inline)) __inline__ #include From 1afb935f3376abe1fbecc5afbe97856f8b70c32d Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Wed, 3 Apr 2013 17:31:42 -0400 Subject: [PATCH 04/73] Bug 857793 - Always return the defaultValue before the first scheduled event on an AudioParam; r=roc --- content/media/AudioEventTimeline.h | 17 +-------- .../compiledtest/TestAudioEventTimeline.cpp | 37 ++++++++++++++++++- 2 files changed, 36 insertions(+), 18 deletions(-) diff --git a/content/media/AudioEventTimeline.h b/content/media/AudioEventTimeline.h index dff1871c1477..e42b670ecff1 100644 --- a/content/media/AudioEventTimeline.h +++ b/content/media/AudioEventTimeline.h @@ -249,22 +249,7 @@ public: // If the requested time is before all of the existing events if (!previous) { - switch (next->mType) { - case AudioTimelineEvent::SetValue: - case AudioTimelineEvent::SetTarget: - // The requested time is before the first event - return mValue; - case AudioTimelineEvent::LinearRamp: - // Use t=0 as T0 and v=defaultValue as V0 - return LinearInterpolate(0.0, mValue, next->template Time(), next->mValue, aTime); - case AudioTimelineEvent::ExponentialRamp: - // Use t=0 as T0 and v=defaultValue as V0 - return ExponentialInterpolate(0.0, mValue, next->template Time(), next->mValue, aTime); - case AudioTimelineEvent::SetValueCurve: - // TODO: implement - return 0.0f; - } - MOZ_ASSERT(false, "unreached"); + return mValue; } // SetTarget nodes can be handled no matter what their next node is (if they have one) diff --git a/content/media/webaudio/compiledtest/TestAudioEventTimeline.cpp b/content/media/webaudio/compiledtest/TestAudioEventTimeline.cpp index 6e0cabe17f99..7ff0bed83619 100644 --- a/content/media/webaudio/compiledtest/TestAudioEventTimeline.cpp +++ b/content/media/webaudio/compiledtest/TestAudioEventTimeline.cpp @@ -215,7 +215,7 @@ void TestEventRemoval() is(timeline.GetEventCount(), 1u, "Should successfully delete two events"); } -void TestBeforeFirstEvent() +void TestBeforeFirstEventSetValue() { Timeline timeline(10.0f); @@ -225,6 +225,36 @@ void TestBeforeFirstEvent() is(timeline.GetValueAtTime(0.5), 10.0f, "Retrun the default value before the first event"); } +void TestBeforeFirstEventSetTarget() +{ + Timeline timeline(10.0f); + + ErrorResultMock rv; + + timeline.SetTargetAtTime(20.0f, 1.0, 5.0, rv); + is(timeline.GetValueAtTime(0.5), 10.0f, "Retrun the default value before the first event"); +} + +void TestBeforeFirstEventLinearRamp() +{ + Timeline timeline(10.0f); + + ErrorResultMock rv; + + timeline.LinearRampToValueAtTime(20.0f, 1.0, rv); + is(timeline.GetValueAtTime(0.5), 10.0f, "Retrun the default value before the first event"); +} + +void TestBeforeFirstEventExponentialRamp() +{ + Timeline timeline(10.0f); + + ErrorResultMock rv; + + timeline.ExponentialRampToValueAtTime(20.0f, 1.0, rv); + is(timeline.GetValueAtTime(0.5), 10.0f, "Retrun the default value before the first event"); +} + void TestAfterLastValueEvent() { Timeline timeline(10.0f); @@ -361,7 +391,10 @@ int main() TestInvalidEvents(); TestEventReplacement(); TestEventRemoval(); - TestBeforeFirstEvent(); + TestBeforeFirstEventSetValue(); + TestBeforeFirstEventSetTarget(); + TestBeforeFirstEventLinearRamp(); + TestBeforeFirstEventExponentialRamp(); TestAfterLastValueEvent(); TestAfterLastTargetValueEvent(); TestAfterLastTargetValueEventWithValueSet(); From 35de15c4b710e0be69b810377d304144f872be41 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Wed, 3 Apr 2013 18:59:24 -0400 Subject: [PATCH 05/73] Follow-up to bug 857790 - Undo the unintentional change to PodOperations.h --- mfbt/PodOperations.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mfbt/PodOperations.h b/mfbt/PodOperations.h index fd341650bca7..62480d47980f 100644 --- a/mfbt/PodOperations.h +++ b/mfbt/PodOperations.h @@ -14,10 +14,8 @@ #ifndef mozilla_PodOperations_h #define mozilla_PodOperations_h -#define MOZ_DELETE delete -#define MOZ_ASSERT -#define MOZ_ASSERT_IF -#define MOZ_ALWAYS_INLINE __attribute__((always_inline)) __inline__ +#include "mozilla/Attributes.h" +#include "mozilla/Util.h" #include From 0d62ef2574988111bd15951beb0a8f85ebcfdded Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Wed, 3 Apr 2013 19:12:27 -0400 Subject: [PATCH 06/73] Bug 727697. Add facility to dump audio output as WAV files. r=kinetik When MOZ_DUMP_AUDIO is set in the environment (to anything), we'll drop a series of files in the current working directory named dumped-audio-.wav, one per nsBufferedAudioStream created, containing the audio for the stream including any skips due to underruns. --- content/media/AudioStream.cpp | 97 ++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/content/media/AudioStream.cpp b/content/media/AudioStream.cpp index 512efdf77c55..1b8402b07324 100644 --- a/content/media/AudioStream.cpp +++ b/content/media/AudioStream.cpp @@ -41,6 +41,14 @@ static Mutex* gAudioPrefsLock = nullptr; static double gVolumeScale; static uint32_t gCubebLatency; +/** + * When MOZ_DUMP_AUDIO is set in the environment (to anything), + * we'll drop a series of files in the current working directory named + * dumped-audio-.wav, one per nsBufferedAudioStream created, containing + * the audio for the stream including any skips due to underruns. + */ +static int gDumpedAudioCount = 0; + static int PrefChanged(const char* aPref, void* aClosure) { if (strcmp(aPref, PREF_VOLUME_SCALE) == 0) { @@ -355,6 +363,9 @@ private: // than are available in mBuffer. uint64_t mLostFrames; + // Output file for dumping audio + FILE* mDumpFile; + // Temporary audio buffer. Filled by Write() and consumed by // DataCallback(). Once mBuffer is full, Write() blocks until sufficient // space becomes available in mBuffer. mBuffer is sized in bytes, not @@ -405,16 +416,88 @@ AudioStream* AudioStream::AllocateStream() return nullptr; } +static void SetUint16LE(PRUint8* aDest, PRUint16 aValue) +{ + aDest[0] = aValue & 0xFF; + aDest[1] = aValue >> 8; +} + +static void SetUint32LE(PRUint8* aDest, PRUint32 aValue) +{ + SetUint16LE(aDest, aValue & 0xFFFF); + SetUint16LE(aDest + 2, aValue >> 16); +} + +static FILE* +OpenDumpFile(AudioStream* aStream) +{ + if (!getenv("MOZ_DUMP_AUDIO")) + return nullptr; + char buf[100]; + sprintf(buf, "dumped-audio-%d.wav", gDumpedAudioCount); + FILE* f = fopen(buf, "wb"); + if (!f) + return nullptr; + ++gDumpedAudioCount; + + PRUint8 header[] = { + // RIFF header + 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, + // fmt chunk. We always write 16-bit samples. + 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x10, 0x00, + // data chunk + 0x64, 0x61, 0x74, 0x61, 0xFE, 0xFF, 0xFF, 0x7F + }; + static const int CHANNEL_OFFSET = 22; + static const int SAMPLE_RATE_OFFSET = 24; + static const int BLOCK_ALIGN_OFFSET = 32; + SetUint16LE(header + CHANNEL_OFFSET, aStream->GetChannels()); + SetUint32LE(header + SAMPLE_RATE_OFFSET, aStream->GetRate()); + SetUint16LE(header + BLOCK_ALIGN_OFFSET, aStream->GetChannels()*2); + fwrite(header, sizeof(header), 1, f); + + return f; +} + +static void +WriteDumpFile(FILE* aDumpFile, AudioStream* aStream, PRUint32 aFrames, + void* aBuffer) +{ + if (!aDumpFile) + return; + + PRUint32 samples = aStream->GetChannels()*aFrames; + if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) { + fwrite(aBuffer, 2, samples, aDumpFile); + return; + } + + NS_ASSERTION(AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_FLOAT32, "bad format"); + nsAutoTArray buf; + buf.SetLength(samples*2); + float* input = static_cast(aBuffer); + PRUint8* output = buf.Elements(); + for (PRUint32 i = 0; i < samples; ++i) { + SetUint16LE(output + i*2, PRInt16(input[i]*32767.0f)); + } + fwrite(output, 2, samples, aDumpFile); + fflush(aDumpFile); +} + #if defined(MOZ_CUBEB) BufferedAudioStream::BufferedAudioStream() - : mMonitor("BufferedAudioStream"), mLostFrames(0), mVolume(1.0), - mBytesPerFrame(0), mState(INITIALIZED) + : mMonitor("BufferedAudioStream"), mLostFrames(0), mDumpFile(nullptr), + mVolume(1.0), mBytesPerFrame(0), mState(INITIALIZED) { } BufferedAudioStream::~BufferedAudioStream() { Shutdown(); + if (mDumpFile) { + fclose(mDumpFile); + } } nsresult @@ -437,6 +520,8 @@ BufferedAudioStream::Init(int32_t aNumChannels, int32_t aRate, mInRate = mOutRate = aRate; mChannels = aNumChannels; + mDumpFile = OpenDumpFile(this); + cubeb_stream_params params; params.rate = aRate; params.channels = aNumChannels; @@ -777,10 +862,18 @@ BufferedAudioStream::DataCallback(void* aBuffer, long aFrames) if (mState != DRAINING) { uint8_t* rpos = static_cast(aBuffer) + FramesToBytes(aFrames - underrunFrames); memset(rpos, 0, FramesToBytes(underrunFrames)); +#ifdef PR_LOGGING + if (underrunFrames) { + PR_LOG(gAudioStreamLog, PR_LOG_WARNING, + ("AudioStream %p lost %d frames", this, underrunFrames)); + } +#endif mLostFrames += underrunFrames; servicedFrames += underrunFrames; } + WriteDumpFile(mDumpFile, this, aFrames, aBuffer); + mAudioClock.UpdateWritePosition(servicedFrames); return servicedFrames; } From dd2894d770c66b6fd9e796e28a6efa58726a0fd9 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 25 Mar 2013 08:34:59 -0400 Subject: [PATCH 07/73] Bug 853721 - Part 2: Hook up DelayNode to the media streams graph and implement delaying of incoming audio; r=roc --- content/media/AudioNodeStream.cpp | 3 - content/media/MediaStreamGraphImpl.h | 4 - content/media/webaudio/DelayNode.cpp | 194 ++++++++++++++++++++++++- content/media/webaudio/DelayNode.h | 8 + content/media/webaudio/WebAudioUtils.h | 9 ++ 5 files changed, 210 insertions(+), 8 deletions(-) diff --git a/content/media/AudioNodeStream.cpp b/content/media/AudioNodeStream.cpp index 5a02b714c4be..e0ec04afad68 100644 --- a/content/media/AudioNodeStream.cpp +++ b/content/media/AudioNodeStream.cpp @@ -191,9 +191,6 @@ AudioNodeStream::ObtainInputBlock(AudioChunk* aTmpChunk) continue; } AudioChunk* chunk = &a->mLastChunk; - // XXX when we implement DelayNode, this will no longer be true and we'll - // need to treat a null chunk (when the DelayNode hasn't had a chance - // to produce data yet) as silence here. MOZ_ASSERT(chunk); if (chunk->IsNull()) { continue; diff --git a/content/media/MediaStreamGraphImpl.h b/content/media/MediaStreamGraphImpl.h index cc0ed6441e46..1491aea9628e 100644 --- a/content/media/MediaStreamGraphImpl.h +++ b/content/media/MediaStreamGraphImpl.h @@ -253,10 +253,6 @@ public: * Produce data for all streams >= aStreamIndex for the given time interval. * Advances block by block, each iteration producing data for all streams * for a single block. - * This is needed if there are WebAudio delay nodes, whose output for a block - * may depend on the output of any other node (including itself) for the - * previous block. This is probably also more performant due to better memory - * locality. * This is called whenever we have an AudioNodeStream in the graph. */ void ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex, diff --git a/content/media/webaudio/DelayNode.cpp b/content/media/webaudio/DelayNode.cpp index 81f7150a8d99..47d98d9678d4 100644 --- a/content/media/webaudio/DelayNode.cpp +++ b/content/media/webaudio/DelayNode.cpp @@ -6,6 +6,10 @@ #include "DelayNode.h" #include "mozilla/dom/DelayNodeBinding.h" +#include "AudioNodeEngine.h" +#include "AudioNodeStream.h" +#include "AudioDestinationNode.h" +#include "WebAudioUtils.h" namespace mozilla { namespace dom { @@ -19,10 +23,191 @@ NS_INTERFACE_MAP_END_INHERITING(AudioNode) NS_IMPL_ADDREF_INHERITED(DelayNode, AudioNode) NS_IMPL_RELEASE_INHERITED(DelayNode, AudioNode) +class DelayNodeEngine : public AudioNodeEngine +{ +public: + explicit DelayNodeEngine(AudioDestinationNode* aDestination) + : mSource(nullptr) + , mDestination(static_cast (aDestination->Stream())) + // Keep the default value in sync with the default value in DelayNode::DelayNode. + , mDelay(0.f) + , mMaxDelay(0.) + , mWriteIndex(0) + , mCurrentDelayTime(0.) + { + } + + void SetSourceStream(AudioNodeStream* aSource) + { + mSource = aSource; + } + + enum Parameters { + DELAY, + MAX_DELAY + }; + void SetTimelineParameter(uint32_t aIndex, const AudioParamTimeline& aValue) MOZ_OVERRIDE + { + switch (aIndex) { + case DELAY: + MOZ_ASSERT(mSource && mDestination); + mDelay = aValue; + WebAudioUtils::ConvertAudioParamToTicks(mDelay, mSource, mDestination); + break; + default: + NS_ERROR("Bad DelayNodeEngine TimelineParameter"); + } + } + void SetDoubleParameter(uint32_t aIndex, double aValue) MOZ_OVERRIDE + { + switch (aIndex) { + case MAX_DELAY: mMaxDelay = aValue; break; + default: + NS_ERROR("Bad DelayNodeEngine DoubleParameter"); + } + } + + bool EnsureBuffer(uint32_t aNumberOfChannels) + { + if (aNumberOfChannels == 0) { + return false; + } + if (mBuffer.Length() == 0) { + if (!mBuffer.SetLength(aNumberOfChannels)) { + return false; + } + const int32_t numFrames = NS_lround(mMaxDelay) * IdealAudioRate(); + for (uint32_t channel = 0; channel < aNumberOfChannels; ++channel) { + if (!mBuffer[channel].SetLength(numFrames)) { + return false; + } + memset(mBuffer[channel].Elements(), 0, numFrames * sizeof(float)); + } + } else if (mBuffer.Length() != aNumberOfChannels) { + // TODO: Handle changes in the channel count + return false; + } + return true; + } + + virtual void ProduceAudioBlock(AudioNodeStream* aStream, + const AudioChunk& aInput, + AudioChunk* aOutput, + bool* aFinished) + { + MOZ_ASSERT(mSource == aStream, "Invalid source stream"); + + const bool firstTime = !!!mBuffer.Length(); + const uint32_t numChannels = aInput.mChannelData.Length(); + + if (!EnsureBuffer(numChannels)) { + aOutput->SetNull(0); + return; + } + + AllocateAudioBlock(numChannels, aOutput); + + double delayTime = 0; + float computedDelay[WEBAUDIO_BLOCK_SIZE]; + // Use a smoothing range of 20ms + const double smoothingRate = WebAudioUtils::ComputeSmoothingRate(0.02, IdealAudioRate()); + + if (mDelay.HasSimpleValue()) { + delayTime = std::max(0.0, std::min(mMaxDelay, double(mDelay.GetValue()))); + if (firstTime) { + // Initialize this only the first time to make sure that mCurrentDelayTime + // has a valid value when we try to change the delay time further below. + mCurrentDelayTime = delayTime; + } + } else { + // Compute the delay values for the duration of the input AudioChunk + TrackTicks tick = aStream->GetCurrentPosition(); + for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) { + computedDelay[counter] = std::max(0.0, std::min(mMaxDelay, + double(mDelay.GetValueAtTime(tick + counter)))); + } + } + + for (uint32_t channel = 0; channel < numChannels; ++channel) { + double currentDelayTime = mCurrentDelayTime; + uint32_t writeIndex = mWriteIndex; + + float* buffer = mBuffer[channel].Elements(); + const uint32_t bufferLength = mBuffer[channel].Length(); + const float* input = static_cast(aInput.mChannelData[channel]); + float* output = static_cast(const_cast(aOutput->mChannelData[channel])); + + for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) { + if (mDelay.HasSimpleValue()) { + // If the simple value has changed, smoothly approach it + currentDelayTime += (delayTime - currentDelayTime) * smoothingRate; + } else { + currentDelayTime = computedDelay[i]; + } + + // Write the input sample to the correct location in our buffer + buffer[writeIndex] = input[i]; + + // Now, determine the correct read position. We adjust the read position to be + // from currentDelayTime seconds in the past. We also interpolate the two input + // frames in case the read position does not match an integer index. + double readPosition = writeIndex + bufferLength - + (currentDelayTime * IdealAudioRate()); + if (readPosition >= bufferLength) { + readPosition -= bufferLength; + } + MOZ_ASSERT(readPosition >= 0.0, "Why are we reading before the beginning of the buffer?"); + + // Here is a the reason why readIndex1 and readIndex will never be out + // of bounds. The maximum value for bufferLength is 180 * 48000 (see + // AudioContext::CreateDelay). The maximum value for mCurrentDelay is + // 180.0, so initially readPosition cannot be more than bufferLength + + // a fraction less than 1. Then we take care of that case by + // subtracting bufferLength from it if needed. So, if + // |bufferLength-readPosition<1.0|, readIndex1 will end up being zero. + // If |1.0<=bufferLength-readPosition<2.0|, readIndex1 will be + // bufferLength-1 and readIndex2 will be 0. + int readIndex1 = int(readPosition); + int readIndex2 = (readIndex1 + 1) % bufferLength; + double interpolationFactor = readPosition - readIndex1; + + output[i] = (1.0 - interpolationFactor) * buffer[readIndex1] + + interpolationFactor * buffer[readIndex2]; + writeIndex = (writeIndex + 1) % bufferLength; + } + + // Remember currentDelayTime and writeIndex for the next ProduceAudioBlock + // call when processing the last channel. + if (channel == numChannels - 1) { + mCurrentDelayTime = currentDelayTime; + mWriteIndex = writeIndex; + } + } + } + + AudioNodeStream* mSource; + AudioNodeStream* mDestination; + AudioParamTimeline mDelay; + // Maximum delay time in seconds + double mMaxDelay; + // Circular buffer for capturing delayed samples. + AutoFallibleTArray, 2> mBuffer; + // Write index for the buffer, to write the frames to the correct index of the buffer + // given the current delay. + uint32_t mWriteIndex; + // Current delay time, in seconds + double mCurrentDelayTime; +}; + DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay) : AudioNode(aContext) - , mDelay(new AudioParam(this, Callback, 0.0f, 0.0f, float(aMaxDelay))) + , mDelay(new AudioParam(this, SendDelayToStream, 0.0f, 0.0f, float(aMaxDelay))) { + DelayNodeEngine* engine = new DelayNodeEngine(aContext->Destination()); + mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM); + engine->SetSourceStream(static_cast (mStream.get())); + AudioNodeStream* ns = static_cast(mStream.get()); + ns->SetDoubleParameter(DelayNodeEngine::MAX_DELAY, aMaxDelay); } JSObject* @@ -31,6 +216,13 @@ DelayNode::WrapObject(JSContext* aCx, JSObject* aScope) return DelayNodeBinding::Wrap(aCx, aScope, this); } +void +DelayNode::SendDelayToStream(AudioNode* aNode) +{ + DelayNode* This = static_cast(aNode); + SendTimelineParameterToStream(This, DelayNodeEngine::DELAY, *This->mDelay); +} + } } diff --git a/content/media/webaudio/DelayNode.h b/content/media/webaudio/DelayNode.h index 7c5b7a711e9f..cef827212b28 100644 --- a/content/media/webaudio/DelayNode.h +++ b/content/media/webaudio/DelayNode.h @@ -30,6 +30,14 @@ public: return mDelay; } + virtual bool SupportsMediaStreams() const MOZ_OVERRIDE + { + return true; + } + +private: + static void SendDelayToStream(AudioNode* aNode); + private: nsRefPtr mDelay; }; diff --git a/content/media/webaudio/WebAudioUtils.h b/content/media/webaudio/WebAudioUtils.h index 8960b744093f..f47c912c5cde 100644 --- a/content/media/webaudio/WebAudioUtils.h +++ b/content/media/webaudio/WebAudioUtils.h @@ -28,6 +28,15 @@ struct WebAudioUtils { return fabs(v1 - v2) < 1e-7; } + /** + * Computes an exponential smoothing rate for a time based variable + * over aDuration seconds. + */ + static double ComputeSmoothingRate(double aDuration, double aSampleRate) + { + return 1.0 - std::exp(-1.0 / (aDuration * aSampleRate)); + } + /** * Converts AudioParamTimeline floating point time values to tick values * with respect to a source and a destination AudioNodeStream. From 678b74f2e63c8b66adabcade23303c4c86f28fe4 Mon Sep 17 00:00:00 2001 From: Mats Palmgren Date: Thu, 4 Apr 2013 01:35:07 +0200 Subject: [PATCH 08/73] Bug 856243 - Add missing do_QueryFrame support and fix typo that caused most MathML frames to not QueryFrame to nsIMathMLFrame. Add static assertions to prevent those kinds of errors in the future. r=dholbert --- layout/generic/nsFrameSetFrame.cpp | 6 +++++ layout/generic/nsHTMLCanvasFrame.cpp | 4 ++++ layout/generic/nsHTMLCanvasFrame.h | 2 ++ layout/generic/nsQueryFrame.h | 28 ++++++++++++++++++++---- layout/mathml/nsMathMLContainerFrame.cpp | 2 +- 5 files changed, 37 insertions(+), 5 deletions(-) diff --git a/layout/generic/nsFrameSetFrame.cpp b/layout/generic/nsFrameSetFrame.cpp index 3c6059fff225..baefeb73ce48 100644 --- a/layout/generic/nsFrameSetFrame.cpp +++ b/layout/generic/nsFrameSetFrame.cpp @@ -142,6 +142,8 @@ protected: class nsHTMLFramesetBlankFrame : public nsLeafFrame { public: + NS_DECL_QUERYFRAME_TARGET(nsHTMLFramesetBlankFrame) + NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS #ifdef DEBUG @@ -1642,6 +1644,10 @@ NS_IMETHODIMP nsHTMLFramesetBorderFrame::GetFrameName(nsAString& aResult) const * nsHTMLFramesetBlankFrame ******************************************************************************/ +NS_QUERYFRAME_HEAD(nsHTMLFramesetBlankFrame) + NS_QUERYFRAME_ENTRY(nsHTMLFramesetBlankFrame) +NS_QUERYFRAME_TAIL_INHERITING(nsLeafFrame) + NS_IMPL_FRAMEARENA_HELPERS(nsHTMLFramesetBlankFrame) nsHTMLFramesetBlankFrame::~nsHTMLFramesetBlankFrame() diff --git a/layout/generic/nsHTMLCanvasFrame.cpp b/layout/generic/nsHTMLCanvasFrame.cpp index 42da89570eab..4247f0e420d0 100644 --- a/layout/generic/nsHTMLCanvasFrame.cpp +++ b/layout/generic/nsHTMLCanvasFrame.cpp @@ -88,6 +88,10 @@ NS_NewHTMLCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) return new (aPresShell) nsHTMLCanvasFrame(aContext); } +NS_QUERYFRAME_HEAD(nsHTMLCanvasFrame) + NS_QUERYFRAME_ENTRY(nsHTMLCanvasFrame) +NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) + NS_IMPL_FRAMEARENA_HELPERS(nsHTMLCanvasFrame) void diff --git a/layout/generic/nsHTMLCanvasFrame.h b/layout/generic/nsHTMLCanvasFrame.h index e67072808816..15a68870e396 100644 --- a/layout/generic/nsHTMLCanvasFrame.h +++ b/layout/generic/nsHTMLCanvasFrame.h @@ -34,6 +34,8 @@ public: typedef mozilla::layers::LayerManager LayerManager; typedef mozilla::FrameLayerBuilder::ContainerParameters ContainerParameters; + NS_DECL_QUERYFRAME_TARGET(nsHTMLCanvasFrame) + NS_DECL_QUERYFRAME NS_DECL_FRAMEARENA_HELPERS nsHTMLCanvasFrame(nsStyleContext* aContext) : nsContainerFrame(aContext) {} diff --git a/layout/generic/nsQueryFrame.h b/layout/generic/nsQueryFrame.h index 589df3360811..db7847b4023b 100644 --- a/layout/generic/nsQueryFrame.h +++ b/layout/generic/nsQueryFrame.h @@ -6,9 +6,15 @@ #define nsQueryFrame_h #include "nscore.h" +#include "mozilla/Assertions.h" +#include "mozilla/TypeTraits.h" + +// NOTE: the long lines in this file are intentional to make compiler error +// messages more readable. #define NS_DECL_QUERYFRAME_TARGET(classname) \ - static const nsQueryFrame::FrameIID kFrameIID = nsQueryFrame::classname##_id; + static const nsQueryFrame::FrameIID kFrameIID = nsQueryFrame::classname##_id; \ + typedef classname Has_NS_DECL_QUERYFRAME_TARGET; #define NS_DECL_QUERYFRAME \ virtual void* QueryFrame(FrameIID id); @@ -17,11 +23,19 @@ void* class::QueryFrame(FrameIID id) { switch (id) { #define NS_QUERYFRAME_ENTRY(class) \ - case class::kFrameIID: return static_cast(this); + case class::kFrameIID: { \ + MOZ_STATIC_ASSERT((mozilla::IsSame::value), \ + #class " must declare itself as a queryframe target"); \ + return static_cast(this); \ + } #define NS_QUERYFRAME_ENTRY_CONDITIONAL(class, condition) \ case class::kFrameIID: \ - if (condition) return static_cast(this); \ + if (condition) { \ + MOZ_STATIC_ASSERT((mozilla::IsSame::value), \ + #class " must declare itself as a queryframe target"); \ + return static_cast(this); \ + } \ break; #define NS_QUERYFRAME_TAIL_INHERITING(class) \ @@ -33,7 +47,11 @@ #define NS_QUERYFRAME_TAIL_INHERITANCE_ROOT \ default: break; \ } \ - return nullptr; \ + MOZ_ASSERT(id != GetFrameId(), \ + "A frame failed to QueryFrame to its *own type*. " \ + "It may be missing NS_DECL_QUERYFRAME, or a " \ + "NS_QUERYFRAME_ENTRY() line with its own type name"); \ + return nullptr; \ } class nsQueryFrame @@ -63,6 +81,8 @@ public: template operator Dest*() { + MOZ_STATIC_ASSERT((mozilla::IsSame::value), + "Dest must declare itself as a queryframe target"); if (!mRawPtr) return nullptr; diff --git a/layout/mathml/nsMathMLContainerFrame.cpp b/layout/mathml/nsMathMLContainerFrame.cpp index 683f2f90f457..94a4d1e605f7 100644 --- a/layout/mathml/nsMathMLContainerFrame.cpp +++ b/layout/mathml/nsMathMLContainerFrame.cpp @@ -39,7 +39,7 @@ using namespace mozilla; NS_IMPL_FRAMEARENA_HELPERS(nsMathMLContainerFrame) NS_QUERYFRAME_HEAD(nsMathMLContainerFrame) - NS_QUERYFRAME_ENTRY(nsMathMLFrame) + NS_QUERYFRAME_ENTRY(nsIMathMLFrame) NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) // ============================================================================= From c49be76e03f45c51ffee0298ed2268f3c3f6b938 Mon Sep 17 00:00:00 2001 From: Robert Lickenbrock Date: Wed, 3 Apr 2013 16:34:56 -0700 Subject: [PATCH 09/73] Bug 854803 - Part 1: Account for more decoder state in RasterImage::IsDecodeFinished(). r=seth --HG-- extra : rebase_source : 7fa2c43c5146eca831d5775d1ba4d0c665404626 --- image/src/RasterImage.cpp | 46 ++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index 7626fc4da3b7..aef665940b42 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -3327,35 +3327,38 @@ bool RasterImage::IsDecodeFinished() { // Precondition + mDecodingMutex.AssertCurrentThreadOwns(); NS_ABORT_IF_FALSE(mDecoder, "Can't call IsDecodeFinished() without decoder!"); + MOZ_ASSERT(mDecodeRequest); - // Assume it's not finished - bool decodeFinished = false; - - // The decode is complete if we got what we wanted... + // The decode is complete if we got what we wanted. if (mDecoder->IsSizeDecode()) { - if (mHasSize) - decodeFinished = true; + if (mDecoder->HasSize()) { + return true; + } + } else if (mDecoder->GetDecodeDone()) { + return true; } - else { - if (mDecoded) - decodeFinished = true; + + // If the decoder returned because it needed a new frame and we haven't + // written to it since then, the decoder may be storing data that it hasn't + // decoded yet. + if (mDecoder->NeedsNewFrame() || mDecodeRequest->mAllocatedNewFrame) { + return false; } - // ... but if we're waiting for a new frame, we're not done. - if (mDecoder && mDecoder->NeedsNewFrame()) - decodeFinished = false; - // Otherwise, if we have all the source data and wrote all the source data, // we're done. // - // (NB - This can be distinct from the above case even for non-erroneous - // images because the decoder might not call DecodingComplete() until we - // call Close() in ShutdownDecoder()) - else if (mHasSourceData && (mBytesDecoded == mSourceData.Length())) - decodeFinished = true; + // (NB - This can be the case even for non-erroneous images because + // Decoder::GetDecodeDone() might not return true until after we call + // Decoder::Finish() in ShutdownDecoder()) + if (mHasSourceData && (mBytesDecoded == mSourceData.Length())) { + return true; + } - return decodeFinished; + // If we get here, assume it's not finished. + return false; } // Indempotent error flagging routine. If a decoder is open, shuts it down. @@ -3471,8 +3474,7 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D // If the decode finished, or we're specifically being told to shut down, // tell the image and shut down the decoder. - if (image->mDecoder->GetDecodeDone() || image->IsDecodeFinished() || - aIntent != eShutdownIntent_Done) { + if (image->IsDecodeFinished() || aIntent != eShutdownIntent_Done) { done = true; // Hold on to a reference to the decoder until we're done with it @@ -3683,7 +3685,7 @@ RasterImage::DecodePool::DecodeJob::Run() } // If someone came along and synchronously decoded us, there's nothing for us to do. - if (!mRequest->mAllocatedNewFrame && (!mImage->mDecoder || mImage->IsDecodeFinished())) { + if (!mImage->mDecoder || mImage->IsDecodeFinished()) { DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage, mRequest); return NS_OK; } From 3fac65389967c9a3a54e28af975a8ca9567ec909 Mon Sep 17 00:00:00 2001 From: Robert Lickenbrock Date: Wed, 3 Apr 2013 16:34:58 -0700 Subject: [PATCH 10/73] Bug 854803 - Part 2: Don't try to enqueue more decoding from DecodeDoneWorker. r=seth --HG-- extra : rebase_source : 0d46455743f02d58c5615c72afb90bba13d505bc --- image/src/RasterImage.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index aef665940b42..a5942f75730d 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -3908,11 +3908,6 @@ RasterImage::DecodeDoneWorker::Run() mImage->FinishedSomeDecoding(eShutdownIntent_Done, mRequest); - // If we didn't finish decoding yet, try again - if (mImage->mDecoder) { - DecodePool::Singleton()->RequestDecode(mImage); - } - return NS_OK; } From 49fe4e10739908a00930620a8a3fdd11383b0e04 Mon Sep 17 00:00:00 2001 From: Yura Zenevich Date: Wed, 3 Apr 2013 16:53:05 -0700 Subject: [PATCH 11/73] Bug 857749 - fixed an issue with utterance generator function for application. r=eeejay --- accessible/src/jsat/UtteranceGenerator.jsm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accessible/src/jsat/UtteranceGenerator.jsm b/accessible/src/jsat/UtteranceGenerator.jsm index 199bd7a23ca6..bc67ae9909d6 100644 --- a/accessible/src/jsat/UtteranceGenerator.jsm +++ b/accessible/src/jsat/UtteranceGenerator.jsm @@ -279,8 +279,8 @@ this.UtteranceGenerator = { application: function application(aAccessible, aRoleStr, aStates, aFlags) { // Don't utter location of applications, it gets tiring. if (aAccessible.name != aAccessible.DOMNode.location) - return this.objectUtteranceFunctions.defaultFunc( - aAccessible, aRoleStr, aStates, aFlags); + return this.objectUtteranceFunctions.defaultFunc.apply(this, + [aAccessible, aRoleStr, aStates, aFlags]); return []; } From c1301cf1e45100b90509704bcbe1eac12c3dd1fe Mon Sep 17 00:00:00 2001 From: Aryeh Gregor Date: Wed, 3 Apr 2013 18:51:56 +0300 Subject: [PATCH 12/73] Bug 857617 - Clean up nsEditor::InsertTextImpl; r=ehsan --HG-- extra : rebase_source : 2b11cc95d29f08af6e2b93541cdd2f714593fea9 --- editor/libeditor/base/nsEditor.cpp | 194 +++++++++------------- editor/libeditor/text/nsTextEditUtils.cpp | 10 +- editor/libeditor/text/nsTextEditUtils.h | 2 +- 3 files changed, 84 insertions(+), 122 deletions(-) diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index b610aeb42a60..0e51641ba901 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -103,6 +103,7 @@ #include "nsStyleStruct.h" // for nsStyleDisplay, nsStyleText, etc #include "nsStyleStructFwd.h" // for nsIFrame::StyleUIReset, etc #include "nsTextEditUtils.h" // for nsTextEditUtils +#include "nsTextNode.h" // for nsTextNode #include "nsThreadUtils.h" // for nsRunnable #include "nsTransactionManager.h" // for nsTransactionManager #include "prtime.h" // for PR_Now @@ -2294,141 +2295,100 @@ NS_IMETHODIMP nsEditor::ScrollSelectionIntoView(bool aScrollToAnchor) return NS_OK; } -NS_IMETHODIMP nsEditor::InsertTextImpl(const nsAString& aStringToInsert, - nsCOMPtr *aInOutNode, - int32_t *aInOutOffset, - nsIDOMDocument *aDoc) +NS_IMETHODIMP +nsEditor::InsertTextImpl(const nsAString& aStringToInsert, + nsCOMPtr* aInOutNode, + int32_t* aInOutOffset, + nsIDOMDocument* aDoc) { - // NOTE: caller *must* have already used nsAutoTxnsConserveSelection stack-based - // class to turn off txn selection updating. Caller also turned on rules sniffing - // if desired. - - nsresult res; - NS_ENSURE_TRUE(aInOutNode && *aInOutNode && aInOutOffset && aDoc, NS_ERROR_NULL_POINTER); - if (!mInIMEMode && aStringToInsert.IsEmpty()) return NS_OK; - nsCOMPtr nodeAsText = do_QueryInterface(*aInOutNode); - if (!nodeAsText && IsPlaintextEditor()) { - nsCOMPtr rootNode = do_QueryInterface(GetRoot()); - // In some cases, aInOutNode is the anonymous DIV, and aInOutOffset is 0. - // To avoid injecting unneeded text nodes, we first look to see if we have - // one available. In that case, we'll just adjust aInOutNode and aInOutOffset - // accordingly. - if (*aInOutNode == rootNode && *aInOutOffset == 0) { - nsCOMPtr possibleTextNode; - res = (*aInOutNode)->GetFirstChild(getter_AddRefs(possibleTextNode)); - if (NS_SUCCEEDED(res)) { - nodeAsText = do_QueryInterface(possibleTextNode); - if (nodeAsText) { - *aInOutNode = possibleTextNode; - } - } + // NOTE: caller *must* have already used nsAutoTxnsConserveSelection + // stack-based class to turn off txn selection updating. Caller also turned + // on rules sniffing if desired. + + NS_ENSURE_TRUE(aInOutNode && *aInOutNode && aInOutOffset && aDoc, + NS_ERROR_NULL_POINTER); + if (!mInIMEMode && aStringToInsert.IsEmpty()) { + return NS_OK; + } + + nsCOMPtr node = do_QueryInterface(*aInOutNode); + NS_ENSURE_STATE(node); + uint32_t offset = static_cast(*aInOutOffset); + + if (!node->IsNodeOfType(nsINode::eTEXT) && IsPlaintextEditor()) { + nsCOMPtr root = GetRoot(); + // In some cases, node is the anonymous DIV, and offset is 0. To avoid + // injecting unneeded text nodes, we first look to see if we have one + // available. In that case, we'll just adjust node and offset accordingly. + if (node == root && offset == 0 && node->HasChildren() && + node->GetFirstChild()->IsNodeOfType(nsINode::eTEXT)) { + node = node->GetFirstChild(); } - // In some other cases, aInOutNode is the anonymous DIV, and aInOutOffset points - // to the terminating mozBR. In that case, we'll adjust aInOutNode and aInOutOffset - // to the preceding text node, if any. - if (!nodeAsText && *aInOutNode == rootNode && *aInOutOffset > 0) { - nsCOMPtr children; - res = (*aInOutNode)->GetChildNodes(getter_AddRefs(children)); - if (NS_SUCCEEDED(res)) { - nsCOMPtr possibleMozBRNode; - children->Item(*aInOutOffset, getter_AddRefs(possibleMozBRNode)); - if (possibleMozBRNode && nsTextEditUtils::IsMozBR(possibleMozBRNode)) { - nsCOMPtr possibleTextNode; - res = children->Item(*aInOutOffset - 1, getter_AddRefs(possibleTextNode)); - if (NS_SUCCEEDED(res)) { - nodeAsText = do_QueryInterface(possibleTextNode); - if (nodeAsText) { - uint32_t length; - res = nodeAsText->GetLength(&length); - if (NS_SUCCEEDED(res)) { - *aInOutOffset = int32_t(length); - *aInOutNode = possibleTextNode; - } - } - } - } else { - // The selection might be at the end of the last textnode child, - // in which case we can just append to the textnode in question. - nsCOMPtr possibleTextNode; - res = children->Item(*aInOutOffset - 1, getter_AddRefs(possibleTextNode)); - nodeAsText = do_QueryInterface(possibleTextNode); - if (nodeAsText) { - uint32_t length; - res = nodeAsText->GetLength(&length); - if (NS_SUCCEEDED(res)) { - *aInOutOffset = int32_t(length); - *aInOutNode = possibleTextNode; - } - } - } - } + // In some other cases, node is the anonymous DIV, and offset points to the + // terminating mozBR. In that case, we'll adjust aInOutNode and + // aInOutOffset to the preceding text node, if any. + if (node == root && offset > 0 && node->GetChildAt(offset - 1) && + node->GetChildAt(offset - 1)->IsNodeOfType(nsINode::eTEXT)) { + node = node->GetChildAt(offset - 1); + offset = node->Length(); } - // Sometimes, aInOutNode is the mozBR element itself. In that case, we'll - // adjust the insertion point to the previous text node, if one exists, or - // to the parent anonymous DIV. - if (nsTextEditUtils::IsMozBR(*aInOutNode) && *aInOutOffset == 0) { - nsCOMPtr previous; - (*aInOutNode)->GetPreviousSibling(getter_AddRefs(previous)); - nodeAsText = do_QueryInterface(previous); - if (nodeAsText) { - uint32_t length; - res = nodeAsText->GetLength(&length); - if (NS_SUCCEEDED(res)) { - *aInOutOffset = int32_t(length); - *aInOutNode = previous; - } - } else { - nsCOMPtr parent; - (*aInOutNode)->GetParentNode(getter_AddRefs(parent)); - if (parent == rootNode) { - *aInOutNode = parent; - } + // Sometimes, node is the mozBR element itself. In that case, we'll adjust + // the insertion point to the previous text node, if one exists, or to the + // parent anonymous DIV. + if (nsTextEditUtils::IsMozBR(node) && offset == 0) { + if (node->GetPreviousSibling() && + node->GetPreviousSibling()->IsNodeOfType(nsINode::eTEXT)) { + node = node->GetPreviousSibling(); + offset = node->Length(); + } else if (node->GetParentNode() && node->GetParentNode() == root) { + node = node->GetParentNode(); } } } - int32_t offset = *aInOutOffset; - if (mInIMEMode) - { - if (!nodeAsText) - { + + nsresult res; + if (mInIMEMode) { + if (!node->IsNodeOfType(nsINode::eTEXT)) { // create a text node - res = aDoc->CreateTextNode(EmptyString(), getter_AddRefs(nodeAsText)); - NS_ENSURE_SUCCESS(res, res); - NS_ENSURE_TRUE(nodeAsText, NS_ERROR_NULL_POINTER); - nsCOMPtr newNode = do_QueryInterface(nodeAsText); + nsCOMPtr doc = do_QueryInterface(aDoc); + NS_ENSURE_STATE(doc); + nsRefPtr newNode = doc->CreateTextNode(EmptyString()); // then we insert it into the dom tree - res = InsertNode(newNode, *aInOutNode, offset); + res = InsertNode(newNode->AsDOMNode(), node->AsDOMNode(), offset); NS_ENSURE_SUCCESS(res, res); + node = newNode; offset = 0; } - res = InsertTextIntoTextNodeImpl(aStringToInsert, nodeAsText, offset); + nsCOMPtr charDataNode = do_QueryInterface(node); + NS_ENSURE_STATE(charDataNode); + res = InsertTextIntoTextNodeImpl(aStringToInsert, charDataNode, offset); NS_ENSURE_SUCCESS(res, res); - } - else - { - if (nodeAsText) - { + offset += aStringToInsert.Length(); + } else { + if (node->IsNodeOfType(nsINode::eTEXT)) { // we are inserting text into an existing text node. - res = InsertTextIntoTextNodeImpl(aStringToInsert, nodeAsText, offset); + nsCOMPtr charDataNode = do_QueryInterface(node); + NS_ENSURE_STATE(charDataNode); + res = InsertTextIntoTextNodeImpl(aStringToInsert, charDataNode, offset); NS_ENSURE_SUCCESS(res, res); - *aInOutOffset += aStringToInsert.Length(); - } - else - { - // we are inserting text into a non-text node - // first we have to create a textnode (this also populates it with the text) - res = aDoc->CreateTextNode(aStringToInsert, getter_AddRefs(nodeAsText)); - NS_ENSURE_SUCCESS(res, res); - NS_ENSURE_TRUE(nodeAsText, NS_ERROR_NULL_POINTER); - nsCOMPtr newNode = do_QueryInterface(nodeAsText); + offset += aStringToInsert.Length(); + } else { + // we are inserting text into a non-text node. first we have to create a + // textnode (this also populates it with the text) + nsCOMPtr doc = do_QueryInterface(aDoc); + NS_ENSURE_STATE(doc); + nsRefPtr newNode = doc->CreateTextNode(aStringToInsert); // then we insert it into the dom tree - res = InsertNode(newNode, *aInOutNode, offset); + res = InsertNode(newNode->AsDOMNode(), node->AsDOMNode(), offset); NS_ENSURE_SUCCESS(res, res); - *aInOutNode = newNode; - *aInOutOffset = aStringToInsert.Length(); + node = newNode; + offset = aStringToInsert.Length(); } } - return res; + + *aInOutNode = node->AsDOMNode(); + *aInOutOffset = static_cast(offset); + return NS_OK; } diff --git a/editor/libeditor/text/nsTextEditUtils.cpp b/editor/libeditor/text/nsTextEditUtils.cpp index 408de26d2810..eb9895a4e070 100644 --- a/editor/libeditor/text/nsTextEditUtils.cpp +++ b/editor/libeditor/text/nsTextEditUtils.cpp @@ -52,12 +52,14 @@ nsTextEditUtils::IsMozBR(nsIDOMNode *node) bool -nsTextEditUtils::IsMozBR(dom::Element* aNode) +nsTextEditUtils::IsMozBR(nsINode* aNode) { MOZ_ASSERT(aNode); - return aNode->IsHTML(nsGkAtoms::br) && - aNode->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, - NS_LITERAL_STRING("_moz"), eIgnoreCase); + return aNode->IsElement() && + aNode->AsElement()->IsHTML(nsGkAtoms::br) && + aNode->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, + NS_LITERAL_STRING("_moz"), + eIgnoreCase); } /////////////////////////////////////////////////////////////////////////// diff --git a/editor/libeditor/text/nsTextEditUtils.h b/editor/libeditor/text/nsTextEditUtils.h index 54d842375b1b..30e7aa9cdc5f 100644 --- a/editor/libeditor/text/nsTextEditUtils.h +++ b/editor/libeditor/text/nsTextEditUtils.h @@ -24,7 +24,7 @@ public: static bool IsBody(nsIDOMNode* aNode); static bool IsBreak(nsIDOMNode* aNode); static bool IsMozBR(nsIDOMNode* aNode); - static bool IsMozBR(mozilla::dom::Element* aNode); + static bool IsMozBR(nsINode* aNode); static bool HasMozAttr(nsIDOMNode* aNode); }; From 6713a93963657e4224fbb721c860f165eadb3cd4 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Tue, 2 Apr 2013 11:49:26 -0400 Subject: [PATCH 13/73] Bug 853646 - part 0 - add mozilla::Remove{Const,Volatile,CV} to TypeTraits.h; r=Waldo --- mfbt/TypeTraits.h | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/mfbt/TypeTraits.h b/mfbt/TypeTraits.h index 661912e11638..3a00db94f919 100644 --- a/mfbt/TypeTraits.h +++ b/mfbt/TypeTraits.h @@ -1,3 +1,4 @@ +/* -*- Mode: C++; 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/. */ @@ -227,6 +228,60 @@ struct IsConvertible /* 20.9.7.1 Const-volatile modifications [meta.trans.cv] */ +/** + * RemoveConst removes top-level const qualifications on a type. + * + * mozilla::RemoveConst::type is int; + * mozilla::RemoveConst::type is int; + * mozilla::RemoveConst::type is const int*; + * mozilla::RemoveConst::type is int*. + */ +template +struct RemoveConst +{ + typedef T Type; +}; + +template +struct RemoveConst +{ + typedef T Type; +}; + +/** + * RemoveVolatile removes top-level volatile qualifications on a type. + * + * mozilla::RemoveVolatile::type is int; + * mozilla::RemoveVolatile::type is int; + * mozilla::RemoveVolatile::type is volatile int*; + * mozilla::RemoveVolatile::type is int*. + */ +template +struct RemoveVolatile +{ + typedef T Type; +}; + +template +struct RemoveVolatile +{ + typedef T Type; +}; + +/** + * RemoveCV removes top-level const and volatile qualifications on a type. + * + * mozilla::RemoveCV::type is int; + * mozilla::RemoveCV::type is int; + * mozilla::RemoveCV::type is int; + * mozilla::RemoveCV::type is int*. + */ +template +struct RemoveCV +{ + typedef typename RemoveConst::Type>::Type Type; +}; + /* 20.9.7.2 Reference modifications [meta.trans.ref] */ /* 20.9.7.3 Sign modifications [meta.trans.sign] */ From 27039b4cb4c033271b6468e0cfe619f530cd1c38 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 3 Apr 2013 11:24:11 -0400 Subject: [PATCH 14/73] Bug 853646 - part 0a - remove js::StripConst and use mozilla::RemoveConst instead; r=Waldo --- js/public/HashTable.h | 4 ++-- js/public/TemplateLib.h | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/js/public/HashTable.h b/js/public/HashTable.h index 1cffbd1d62ed..5cc7ea4e8b9e 100644 --- a/js/public/HashTable.h +++ b/js/public/HashTable.h @@ -610,7 +610,7 @@ template class HashTableEntry { template friend class HashTable; - typedef typename tl::StripConst::result NonConstT; + typedef typename mozilla::RemoveConst::Type NonConstT; HashNumber keyHash; mozilla::AlignedStorage2 mem; @@ -677,7 +677,7 @@ class HashTableEntry template class HashTable : private AllocPolicy { - typedef typename tl::StripConst::result NonConstT; + typedef typename mozilla::RemoveConst::Type NonConstT; typedef typename HashPolicy::KeyType Key; typedef typename HashPolicy::Lookup Lookup; diff --git a/js/public/TemplateLib.h b/js/public/TemplateLib.h index 254c7fe752c7..58966b166f9f 100644 --- a/js/public/TemplateLib.h +++ b/js/public/TemplateLib.h @@ -104,10 +104,6 @@ template struct UnsafeRangeSizeMask { static const size_t result = MulOverflowMask<2 * sizeof(T)>::result; }; -/* Return T stripped of any const-ness. */ -template struct StripConst { typedef T result; }; -template struct StripConst { typedef T result; }; - template struct If { static const T result = v1; }; template struct If { static const T result = v2; }; From 94ac509a99e94d158578181fa8b1e1f02c361068 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Tue, 2 Apr 2013 11:49:58 -0400 Subject: [PATCH 15/73] Bug 853646 - part 1 - add mozilla::IsIntegral to TypeTraits.h; r=Waldo --- mfbt/TypeTraits.h | 65 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/mfbt/TypeTraits.h b/mfbt/TypeTraits.h index 3a00db94f919..0c1a9a67ddb3 100644 --- a/mfbt/TypeTraits.h +++ b/mfbt/TypeTraits.h @@ -18,6 +18,10 @@ namespace mozilla { +/* Forward declarations. */ + +template struct RemoveCV; + /* 20.9.3 Helper classes [meta.help] */ /** @@ -40,6 +44,43 @@ typedef IntegralConstant FalseType; /* 20.9.4.1 Primary type categories [meta.unary.cat] */ +namespace detail { + +template +struct IsIntegralHelper : FalseType {}; + +template<> struct IsIntegralHelper : TrueType {}; +template<> struct IsIntegralHelper : TrueType {}; +template<> struct IsIntegralHelper : TrueType {}; +template<> struct IsIntegralHelper : TrueType {}; +template<> struct IsIntegralHelper : TrueType {}; +template<> struct IsIntegralHelper : TrueType {}; +template<> struct IsIntegralHelper : TrueType {}; +template<> struct IsIntegralHelper : TrueType {}; +template<> struct IsIntegralHelper : TrueType {}; +template<> struct IsIntegralHelper : TrueType {}; +template<> struct IsIntegralHelper : TrueType {}; +template<> struct IsIntegralHelper : TrueType {}; +template<> struct IsIntegralHelper : TrueType {}; + +} /* namespace detail */ + +/** + * IsIntegral determines whether a type is an integral type. + * + * mozilla::IsIntegral::value is true; + * mozilla::IsIntegral::value is true; + * mozilla::IsIntegral::value is true; + * mozilla::IsIntegral::value is false; + * mozilla::IsIntegral::value is false; + * + * Note that the behavior of IsIntegral on char16_t and char32_t is + * unspecified. + */ +template +struct IsIntegral : detail::IsIntegralHelper::Type> +{}; + /** * IsPointer determines whether a type is a pointer type (but not a pointer-to- * member type). @@ -231,10 +272,10 @@ struct IsConvertible /** * RemoveConst removes top-level const qualifications on a type. * - * mozilla::RemoveConst::type is int; - * mozilla::RemoveConst::type is int; - * mozilla::RemoveConst::type is const int*; - * mozilla::RemoveConst::type is int*. + * mozilla::RemoveConst::Type is int; + * mozilla::RemoveConst::Type is int; + * mozilla::RemoveConst::Type is const int*; + * mozilla::RemoveConst::Type is int*. */ template struct RemoveConst @@ -251,10 +292,10 @@ struct RemoveConst /** * RemoveVolatile removes top-level volatile qualifications on a type. * - * mozilla::RemoveVolatile::type is int; - * mozilla::RemoveVolatile::type is int; - * mozilla::RemoveVolatile::type is volatile int*; - * mozilla::RemoveVolatile::type is int*. + * mozilla::RemoveVolatile::Type is int; + * mozilla::RemoveVolatile::Type is int; + * mozilla::RemoveVolatile::Type is volatile int*; + * mozilla::RemoveVolatile::Type is int*. */ template struct RemoveVolatile @@ -271,10 +312,10 @@ struct RemoveVolatile /** * RemoveCV removes top-level const and volatile qualifications on a type. * - * mozilla::RemoveCV::type is int; - * mozilla::RemoveCV::type is int; - * mozilla::RemoveCV::type is int; - * mozilla::RemoveCV::type is int*. + * mozilla::RemoveCV::Type is int; + * mozilla::RemoveCV::Type is int; + * mozilla::RemoveCV::Type is int; + * mozilla::RemoveCV::Type is int*. */ template struct RemoveCV From bcb463b2ef5f4a88a653d5eb3b514d39a7c15862 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 30 Jan 2013 16:45:39 -0500 Subject: [PATCH 16/73] Bug 798172 - part 1 - add mfbt/Endian.h; r=Waldo --- mfbt/Endian.h | 631 +++++++++++++++++++++++++++++++++++++++ mfbt/exported_headers.mk | 1 + 2 files changed, 632 insertions(+) create mode 100644 mfbt/Endian.h diff --git a/mfbt/Endian.h b/mfbt/Endian.h new file mode 100644 index 000000000000..445b45286de6 --- /dev/null +++ b/mfbt/Endian.h @@ -0,0 +1,631 @@ +/* -*- Mode: C++; 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/. */ + +/* Functions for reading and writing integers in various endiannesses. */ + +/* + * The classes LittleEndian and BigEndian expose static methods for + * reading and writing 16-, 32-, and 64-bit signed and unsigned integers + * in their respective endianness. The naming scheme is: + * + * {Little,Big}Endian::{read,write}{Uint,Int} + * + * For instance, LittleEndian::readInt32 will read a 32-bit signed + * integer from memory in little endian format. Similarly, + * BigEndian::writeUint16 will write a 16-bit unsigned integer to memory + * in big-endian format. + * + * The class NativeEndian exposes methods for conversion of existing + * data to and from the native endianness. These methods are intended + * for cases where data needs to be transferred, serialized, etc. + * swap{To,From}{Little,Big}Endian byteswap a single value if necessary. + * Bulk conversion functions are also provided which optimize the + * no-conversion-needed case: + * + * - copyAndSwap{To,From}{Little,Big}Endian; + * - swap{To,From}{Little,Big}EndianInPlace. + * + * The *From* variants are intended to be used for reading data and the + * *To* variants for writing data. + * + * Methods on NativeEndian work with integer data of any type. + * Floating-point data is not supported. + * + * For clarity in networking code, "Network" may be used as a synonym + * for "Big" in any of the above methods or class names. + * + * As an example, reading a file format header whose fields are stored + * in big-endian format might look like: + * + * class ExampleHeader + * { + * private: + * uint32_t magic; + * uint32_t length; + * uint32_t totalRecords; + * uint64_t checksum; + * + * public: + * ExampleHeader(const void* data) { + * const uint8_t* ptr = static_cast(data); + * magic = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t); + * length = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t); + * totalRecords = BigEndian::readUint32(ptr); ptr += sizeof(uint32_t); + * checksum = BigEndian::readUint64(ptr); + * } + * ... + * }; + */ + +#ifndef mozilla_Endian_h_ +#define mozilla_Endian_h_ + +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" +#include "mozilla/Compiler.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/StandardInteger.h" +#include "mozilla/TypeTraits.h" + +#include + +#if defined(_MSC_VER) && _MSC_VER >= 1300 +# include +# pragma intrinsic(_byteswap_ushort) +# pragma intrinsic(_byteswap_ulong) +# pragma intrinsic(_byteswap_uint64) +#endif + +#if defined(_WIN64) +# if defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) +# define MOZ_LITTLE_ENDIAN 1 +# else +# error "CPU type is unknown" +# endif +#elif defined(_WIN32) +# if defined(_M_IX86) +# define MOZ_LITTLE_ENDIAN 1 +# else +# error "CPU type is unknown" +# endif +#elif defined(__APPLE__) +# if __LITTLE_ENDIAN__ +# define MOZ_LITTLE_ENDIAN 1 +# elif __BIG_ENDIAN__ +# define MOZ_BIG_ENDIAN 1 +# endif +#elif defined(__GNUC__) && \ + defined(__BYTE_ORDER__) && \ + defined(__ORDER_LITTLE_ENDIAN__) && \ + defined(__ORDER_BIG_ENDIAN__) + /* + * Some versions of GCC provide architecture-independent macros for + * this. Yes, there are more than two values for __BYTE_ORDER__. + */ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define MOZ_LITTLE_ENDIAN 1 +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define MOZ_BIG_ENDIAN 1 +# else +# error "Can't handle mixed-endian architectures" +# endif +/* + * We can't include useful headers like or + * here because they're not present on all platforms. Instead we have + * this big conditional that ideally will catch all the interesting + * cases. + */ +#elif defined(__sparc) || defined(__sparc__) || \ + defined(_POWER) || defined(__powerpc__) || \ + defined(__ppc__) || defined(__hppa) || \ + defined(_MIPSEB) || defined(__ARMEB__) || \ + defined(__s390__) || \ + (defined(__sh__) && defined(__LITTLE_ENDIAN__)) || \ + (defined(__ia64) && defined(__BIG_ENDIAN__)) +# define MOZ_BIG_ENDIAN 1 +#elif defined(__i386) || defined(__i386__) || \ + defined(__x86_64) || defined(__x86_64__) || \ + defined(_MIPSEL) || defined(__ARMEL__) || \ + defined(__alpha__) || \ + (defined(__sh__) && defined(__BIG_ENDIAN__)) || \ + (defined(__ia64) && !defined(__BIG_ENDIAN__)) +# define MOZ_LITTLE_ENDIAN 1 +#endif + +#if MOZ_BIG_ENDIAN +# define MOZ_LITTLE_ENDIAN 0 +#elif MOZ_LITTLE_ENDIAN +# define MOZ_BIG_ENDIAN 0 +#else +# error "Cannot determine endianness" +#endif + +namespace mozilla { + +namespace detail { + +/* + * We need wrappers here because free functions with default template + * arguments and/or partial specialization of function templates are not + * supported by all the compilers we use. + */ +template +struct Swapper; + +template +struct Swapper +{ + static T swap(T value) + { +#if defined(__clang__) + return T(__builtin_bswap16(value)); +#elif defined(__GNUC__) && MOZ_GCC_VERSION_AT_LEAST(4, 8, 0) + return T(__builtin_bswap16(value)); +#elif defined(_MSC_VER) + return T(_byteswap_ushort(value)); +#else + return T(((value & 0x00ff) << 8) | ((value & 0xff00) >> 8)); +#endif + } +}; + +template +struct Swapper +{ + static T swap(T value) + { +#if defined(__clang__) || defined(__GNUC__) + return T(__builtin_bswap32(value)); +#elif defined(_MSC_VER) + return T(_byteswap_ulong(value)); +#else + return T(((value & 0x000000ffU) << 24) | + ((value & 0x0000ff00U) << 8) | + ((value & 0x00ff0000U) >> 8) | + ((value & 0xff000000U) >> 24)); +#endif + } +}; + +template +struct Swapper +{ + static inline T swap(T value) + { +#if defined(__clang__) || defined(__GNUC__) + return T(__builtin_bswap64(value)); +#elif defined(_MSC_VER) + return T(_byteswap_uint64(value)); +#else + return T(((value & 0x00000000000000ffULL) << 56) | + ((value & 0x000000000000ff00ULL) << 40) | + ((value & 0x0000000000ff0000ULL) << 24) | + ((value & 0x00000000ff000000ULL) << 8) | + ((value & 0x000000ff00000000ULL) >> 8) | + ((value & 0x0000ff0000000000ULL) >> 24) | + ((value & 0x00ff000000000000ULL) >> 40) | + ((value & 0xff00000000000000ULL) >> 56)); +#endif + } +}; + +enum Endianness { Little, Big }; + +#if MOZ_BIG_ENDIAN +# define MOZ_NATIVE_ENDIANNESS detail::Big +#else +# define MOZ_NATIVE_ENDIANNESS detail::Little +#endif + +class EndianUtils +{ + /** + * Assert that the memory regions [dest, dest+count) and [src, src+count] + * do not overlap. count is given in bytes. + */ + static void assertNoOverlap(const void* dest, const void* src, size_t count) + { + DebugOnly byteDestPtr = static_cast(dest); + DebugOnly byteSrcPtr = static_cast(src); + MOZ_ASSERT((byteDestPtr < byteSrcPtr && + byteDestPtr + count <= byteSrcPtr) || + (byteSrcPtr < byteDestPtr && + byteSrcPtr + count <= byteDestPtr)); + } + + template + static void assertAligned(T* ptr) + { + MOZ_ASSERT((uintptr_t(ptr) % sizeof(T)) == 0, "Unaligned pointer!"); + } + + protected: + /** + * Return |value| converted from SourceEndian encoding to DestEndian + * encoding. + */ + template + static inline T maybeSwap(T value) + { + if (SourceEndian == DestEndian) + return value; + + return Swapper::swap(value); + } + + /** + * Convert |count| elements at |ptr| from SourceEndian encoding to + * DestEndian encoding. + */ + template + static inline void maybeSwapInPlace(T* ptr, size_t count) + { + assertAligned(ptr); + + if (SourceEndian == DestEndian) + return; + + for (size_t i = 0; i < count; i++) + ptr[i] = Swapper::swap(ptr[i]); + } + + /** + * Write |count| elements to the unaligned address |dest| in DestEndian + * format, using elements found at |src| in SourceEndian format. + */ + template + static void copyAndSwapTo(void* dest, const T* src, size_t count) + { + assertNoOverlap(dest, src, count * sizeof(T)); + assertAligned(src); + + if (SourceEndian == DestEndian) { + memcpy(dest, src, count * sizeof(T)); + return; + } + + uint8_t* byteDestPtr = static_cast(dest); + for (size_t i = 0; i < count; ++i) { + union { + T val; + uint8_t buffer[sizeof(T)]; + } u; + u.val = maybeSwap(src[i]); + memcpy(byteDestPtr, u.buffer, sizeof(T)); + byteDestPtr += sizeof(T); + } + } + + /** + * Write |count| elements to |dest| in DestEndian format, using elements + * found at the unaligned address |src| in SourceEndian format. + */ + template + static void copyAndSwapFrom(T* dest, const void* src, size_t count) + { + assertNoOverlap(dest, src, count * sizeof(T)); + assertAligned(dest); + + if (SourceEndian == DestEndian) { + memcpy(dest, src, count * sizeof(T)); + return; + } + + const uint8_t* byteSrcPtr = static_cast(src); + for (size_t i = 0; i < count; ++i) { + union { + T val; + uint8_t buffer[sizeof(T)]; + } u; + memcpy(u.buffer, byteSrcPtr, sizeof(T)); + dest[i] = maybeSwap(u.val); + byteSrcPtr += sizeof(T); + } + } +}; + +template +class Endian : private EndianUtils +{ + protected: + /** Read a uint16_t in ThisEndian endianness from |p| and return it. */ + static MOZ_WARN_UNUSED_RESULT uint16_t readUint16(const void* p) { + return read(p); + } + + /** Read a uint32_t in ThisEndian endianness from |p| and return it. */ + static MOZ_WARN_UNUSED_RESULT uint32_t readUint32(const void* p) { + return read(p); + } + + /** Read a uint64_t in ThisEndian endianness from |p| and return it. */ + static MOZ_WARN_UNUSED_RESULT uint64_t readUint64(const void* p) { + return read(p); + } + + /** Read an int16_t in ThisEndian endianness from |p| and return it. */ + static MOZ_WARN_UNUSED_RESULT int16_t readInt16(const void* p) { + return read(p); + } + + /** Read an int32_t in ThisEndian endianness from |p| and return it. */ + static MOZ_WARN_UNUSED_RESULT int32_t readInt32(const void* p) { + return read(p); + } + + /** Read an int64_t in ThisEndian endianness from |p| and return it. */ + static MOZ_WARN_UNUSED_RESULT int64_t readInt64(const void* p) { + return read(p); + } + + /** Write |val| to |p| using ThisEndian endianness. */ + static void writeUint16(void* p, uint16_t val) { + write(p, val); + } + /** Write |val| to |p| using ThisEndian endianness. */ + static void writeUint32(void* p, uint32_t val) { + write(p, val); + } + /** Write |val| to |p| using ThisEndian endianness. */ + static void writeUint64(void* p, uint64_t val) { + write(p, val); + } + + /** Write |val| to |p| using ThisEndian endianness. */ + static void writeInt16(void* p, int16_t val) { + write(p, val); + } + /** Write |val| to |p| using ThisEndian endianness. */ + static void writeInt32(void* p, int32_t val) { + write(p, val); + } + /** Write |val| to |p| using ThisEndian endianness. */ + static void writeInt64(void* p, int64_t val) { + write(p, val); + } + + /* + * Converts a value of type T to little-endian format. + * + * This function is intended for cases where you have data in your + * native-endian format and you need it to appear in little-endian + * format for transmission. + */ + template + MOZ_WARN_UNUSED_RESULT static T swapToLittleEndian(T value) { + return maybeSwap(value); + } + /* + * Copies count values of type T starting at src to dest, converting + * them to little-endian format if ThisEndian is Big. + * As with memcpy, dest and src must not overlap. + */ + template + static void copyAndSwapToLittleEndian(void* dest, const T* src, + size_t count) { + copyAndSwapTo(dest, src, count); + } + /* + * Likewise, but converts values in place. + */ + template + static void swapToLittleEndianInPlace(T* p, size_t count) { + maybeSwapInPlace(p, count); + } + + /* + * Converts a value of type T to big-endian format. + */ + template + MOZ_WARN_UNUSED_RESULT static T swapToBigEndian(T value) { + return maybeSwap(value); + } + /* + * Copies count values of type T starting at src to dest, converting + * them to big-endian format if ThisEndian is Little. + * As with memcpy, dest and src must not overlap. + */ + template + static void copyAndSwapToBigEndian(void* dest, const T* src, size_t count) { + copyAndSwapTo(dest, src, count); + } + /* + * Likewise, but converts values in place. + */ + template + static void swapToBigEndianInPlace(T* p, size_t count) { + maybeSwapInPlace(p, count); + } + + /* + * Synonyms for the big-endian functions, for better readability + * in network code. + */ + template + MOZ_WARN_UNUSED_RESULT static T swapToNetworkOrder(T value) { + return swapToBigEndian(value); + } + template + static void + copyAndSwapToNetworkOrder(void* dest, const T* src, size_t count) { + copyAndSwapToBigEndian(dest, src, count); + } + template + static void + swapToNetworkOrderInPlace(T* p, size_t count) { + swapToBigEndianInPlace(p, count); + } + + /* + * Converts a value of type T from little-endian format. + */ + template + MOZ_WARN_UNUSED_RESULT static T swapFromLittleEndian(T value) { + return maybeSwap(value); + } + /* + * Copies count values of type T starting at src to dest, converting + * them to little-endian format if ThisEndian is Big. + * As with memcpy, dest and src must not overlap. + */ + template + static void copyAndSwapFromLittleEndian(T* dest, const void* src, + size_t count) { + copyAndSwapFrom(dest, src, count); + } + /* + * Likewise, but converts values in place. + */ + template + static void swapFromLittleEndianInPlace(T* p, size_t count) { + maybeSwapInPlace(p, count); + } + + /* + * Converts a value of type T from big-endian format. + */ + template + MOZ_WARN_UNUSED_RESULT static T swapFromBigEndian(T value) { + return maybeSwap(value); + } + /* + * Copies count values of type T starting at src to dest, converting + * them to big-endian format if ThisEndian is Little. + * As with memcpy, dest and src must not overlap. + */ + template + static void copyAndSwapFromBigEndian(T* dest, const void* src, + size_t count) { + copyAndSwapFrom(dest, src, count); + } + /* + * Likewise, but converts values in place. + */ + template + static void swapFromBigEndianInPlace(T* p, size_t count) { + maybeSwapInPlace(p, count); + } + + /* + * Synonyms for the big-endian functions, for better readability + * in network code. + */ + template + MOZ_WARN_UNUSED_RESULT static T swapFromNetworkOrder(T value) { + return swapFromBigEndian(value); + } + template + static void copyAndSwapFromNetworkOrder(T* dest, const void* src, + size_t count) { + copyAndSwapFromBigEndian(dest, src, count); + } + template + static void swapFromNetworkOrderInPlace(T* p, size_t count) { + swapFromBigEndianInPlace(p, count); + } + + private: + /** + * Read a value of type T, encoded in endianness ThisEndian from |p|. + * Return that value encoded in native endianness. + */ + template + static T read(const void* p) { + union { + T val; + uint8_t buffer[sizeof(T)]; + } u; + memcpy(u.buffer, p, sizeof(T)); + return maybeSwap(u.val); + } + + /** + * Write a value of type T, in native endianness, to |p|, in ThisEndian + * endianness. + */ + template + static void write(void* p, T value) { + T tmp = maybeSwap(value); + memcpy(p, &tmp, sizeof(T)); + } + + Endian() MOZ_DELETE; + Endian(const Endian& other) MOZ_DELETE; + void operator=(const Endian& other) MOZ_DELETE; +}; + +template +class EndianReadWrite : public Endian +{ + private: + typedef Endian super; + + public: + using super::readUint16; + using super::readUint32; + using super::readUint64; + using super::readInt16; + using super::readInt32; + using super::readInt64; + using super::writeUint16; + using super::writeUint32; + using super::writeUint64; + using super::writeInt16; + using super::writeInt32; + using super::writeInt64; +}; + +} /* namespace detail */ + +class LittleEndian MOZ_FINAL : public detail::EndianReadWrite +{}; + +class BigEndian MOZ_FINAL : public detail::EndianReadWrite +{}; + +typedef BigEndian NetworkEndian; + +class NativeEndian MOZ_FINAL : public detail::Endian +{ + private: + typedef detail::Endian super; + + public: + /* + * These functions are intended for cases where you have data in your + * native-endian format and you need the data to appear in the appropriate + * endianness for transmission, serialization, etc. + */ + using super::swapToLittleEndian; + using super::copyAndSwapToLittleEndian; + using super::swapToLittleEndianInPlace; + using super::swapToBigEndian; + using super::copyAndSwapToBigEndian; + using super::swapToBigEndianInPlace; + using super::swapToNetworkOrder; + using super::copyAndSwapToNetworkOrder; + using super::swapToNetworkOrderInPlace; + + /* + * These functions are intended for cases where you have data in the + * given endianness (e.g. reading from disk or a file-format) and you + * need the data to appear in native-endian format for processing. + */ + using super::swapFromLittleEndian; + using super::copyAndSwapFromLittleEndian; + using super::swapFromLittleEndianInPlace; + using super::swapFromBigEndian; + using super::copyAndSwapFromBigEndian; + using super::swapFromBigEndianInPlace; + using super::swapFromNetworkOrder; + using super::copyAndSwapFromNetworkOrder; + using super::swapFromNetworkOrderInPlace; +}; + +#undef MOZ_NATIVE_ENDIANNESS + +} /* namespace mozilla */ + +#endif /* mozilla_Endian_h_ */ diff --git a/mfbt/exported_headers.mk b/mfbt/exported_headers.mk index c660b9b36206..a1f8137e73bc 100644 --- a/mfbt/exported_headers.mk +++ b/mfbt/exported_headers.mk @@ -17,6 +17,7 @@ EXPORTS_mozilla += \ Compiler.h \ Constants.h \ DebugOnly.h \ + Endian.h \ EnumSet.h \ FloatingPoint.h \ GuardObjects.h \ From 7ef4ee80d8257ddd663425f950b38ad59f8a1b24 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 30 Jan 2013 16:46:45 -0500 Subject: [PATCH 17/73] Bug 798172 - part 2 - add tests for mfbt/Endian.h; r=Waldo --- mfbt/tests/Makefile.in | 1 + mfbt/tests/TestEndian.cpp | 398 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 399 insertions(+) create mode 100644 mfbt/tests/TestEndian.cpp diff --git a/mfbt/tests/Makefile.in b/mfbt/tests/Makefile.in index 6b46d1105d10..8767dabbc966 100644 --- a/mfbt/tests/Makefile.in +++ b/mfbt/tests/Makefile.in @@ -14,6 +14,7 @@ STL_FLAGS = CPP_UNIT_TESTS = \ TestBloomFilter.cpp \ TestCheckedInt.cpp \ + TestEndian.cpp \ TestEnumSet.cpp \ TestSHA1.cpp \ TestTypeTraits.cpp \ diff --git a/mfbt/tests/TestEndian.cpp b/mfbt/tests/TestEndian.cpp new file mode 100644 index 000000000000..1ec520de3cea --- /dev/null +++ b/mfbt/tests/TestEndian.cpp @@ -0,0 +1,398 @@ +/* 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 "mozilla/Assertions.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Endian.h" + +using mozilla::BigEndian; +using mozilla::DebugOnly; +using mozilla::LittleEndian; +using mozilla::NativeEndian; + +template +void +TestSingleSwap(T value, T swappedValue) +{ +#if MOZ_LITTLE_ENDIAN + MOZ_ASSERT(NativeEndian::swapToBigEndian(value) == swappedValue); + MOZ_ASSERT(NativeEndian::swapFromBigEndian(value) == swappedValue); + MOZ_ASSERT(NativeEndian::swapToNetworkOrder(value) == swappedValue); + MOZ_ASSERT(NativeEndian::swapFromNetworkOrder(value) == swappedValue); +#else + MOZ_ASSERT(NativeEndian::swapToLittleEndian(value) == swappedValue); + MOZ_ASSERT(NativeEndian::swapFromLittleEndian(value) == swappedValue); +#endif +} + +template +void +TestSingleNoSwap(T value, T notSwappedValue) +{ +#if MOZ_LITTLE_ENDIAN + MOZ_ASSERT(NativeEndian::swapToLittleEndian(value) == notSwappedValue); + MOZ_ASSERT(NativeEndian::swapFromLittleEndian(value) == notSwappedValue); +#else + MOZ_ASSERT(NativeEndian::swapToBigEndian(value) == notSwappedValue); + MOZ_ASSERT(NativeEndian::swapFromBigEndian(value) == notSwappedValue); + MOZ_ASSERT(NativeEndian::swapToNetworkOrder(value) == notSwappedValue); + MOZ_ASSERT(NativeEndian::swapFromNetworkOrder(value) == notSwappedValue); +#endif +} + +// Endian.h functions are declared as protected in an base class and +// then re-exported as public in public derived classes. The +// standardese around explicit instantiation of templates is not clear +// in such cases. Provide these wrappers to make things more explicit. +// For your own enlightenment, you may wish to peruse: +// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56152 and subsequently +// http://j.mp/XosS6S . +#define WRAP_COPYTO(NAME) \ + template \ + void \ + NAME(void* dst, const T* src, unsigned int count) \ + { \ + NativeEndian::NAME(dst, src, count); \ + } + +WRAP_COPYTO(copyAndSwapToLittleEndian) +WRAP_COPYTO(copyAndSwapToBigEndian) +WRAP_COPYTO(copyAndSwapToNetworkOrder) + +#define WRAP_COPYFROM(NAME) \ + template \ + void \ + NAME(T* dst, const void* src, unsigned int count) \ + { \ + NativeEndian::NAME(dst, src, count); \ + } + +WRAP_COPYFROM(copyAndSwapFromLittleEndian) +WRAP_COPYFROM(copyAndSwapFromBigEndian) +WRAP_COPYFROM(copyAndSwapFromNetworkOrder) + +#define WRAP_IN_PLACE(NAME) \ + template \ + void \ + NAME(T* p, unsigned int count) \ + { \ + NativeEndian::NAME(p, count); \ + } +WRAP_IN_PLACE(swapToLittleEndianInPlace) +WRAP_IN_PLACE(swapFromLittleEndianInPlace) +WRAP_IN_PLACE(swapToBigEndianInPlace) +WRAP_IN_PLACE(swapFromBigEndianInPlace) +WRAP_IN_PLACE(swapToNetworkOrderInPlace) +WRAP_IN_PLACE(swapFromNetworkOrderInPlace) + +enum SwapExpectation { + Swap, + NoSwap +}; + +template +void +TestBulkSwapToSub(enum SwapExpectation expectSwap, + const T (&values)[count], + void (*swapperFunc)(void*, const T*, unsigned int), + T (*readerFunc)(const void*)) +{ + const size_t arraySize = 2 * count; + const size_t bufferSize = arraySize * sizeof(T); + static uint8_t buffer[bufferSize]; + const uint8_t fillValue = 0xa5; + static uint8_t checkBuffer[bufferSize]; + + MOZ_ASSERT(bufferSize > 2 * sizeof(T)); + + memset(checkBuffer, fillValue, bufferSize); + + for (size_t startPosition = 0; startPosition < sizeof(T); ++startPosition) { + for (size_t nValues = 0; nValues < count; ++nValues) { + memset(buffer, fillValue, bufferSize); + swapperFunc(buffer + startPosition, values, nValues); + + MOZ_ASSERT(memcmp(buffer, checkBuffer, startPosition) == 0); + DebugOnly valuesEndPosition = startPosition + sizeof(T) * nValues; + MOZ_ASSERT(memcmp(buffer + valuesEndPosition, + checkBuffer + valuesEndPosition, + bufferSize - valuesEndPosition) == 0); + if (expectSwap == NoSwap) { + MOZ_ASSERT(memcmp(buffer + startPosition, values, + nValues * sizeof(T)) == 0); + } + for (size_t i = 0; i < nValues; ++i) { + MOZ_ASSERT(readerFunc(buffer + startPosition + sizeof(T) * i) == + values[i]); + } + } + } +} + +template +void +TestBulkSwapFromSub(enum SwapExpectation expectSwap, + const T (&values)[count], + void (*swapperFunc)(T*, const void*, unsigned int), + T (*readerFunc)(const void*)) +{ + const size_t arraySize = 2 * count; + const size_t bufferSize = arraySize * sizeof(T); + static T buffer[arraySize]; + const uint8_t fillValue = 0xa5; + static T checkBuffer[arraySize]; + + memset(checkBuffer, fillValue, bufferSize); + + for (size_t startPosition = 0; startPosition < count; ++startPosition) { + for (size_t nValues = 0; nValues < (count - startPosition); ++nValues) { + memset(buffer, fillValue, bufferSize); + swapperFunc(buffer + startPosition, values, nValues); + + MOZ_ASSERT(memcmp(buffer, checkBuffer, startPosition * sizeof(T)) == 0); + DebugOnly valuesEndPosition = startPosition + nValues; + MOZ_ASSERT(memcmp(buffer + valuesEndPosition, + checkBuffer + valuesEndPosition, + (arraySize - valuesEndPosition) * sizeof(T)) == 0); + if (expectSwap == NoSwap) { + MOZ_ASSERT(memcmp(buffer + startPosition, values, + nValues * sizeof(T)) == 0); + } + for (size_t i = 0; i < nValues; ++i) + MOZ_ASSERT(readerFunc(buffer + startPosition + i) == values[i]); + } + } +} + + +template +void +TestBulkInPlaceSub(enum SwapExpectation expectSwap, + const T (&values)[count], + void (*swapperFunc)(T* p, unsigned int), + T (*readerFunc)(const void*)) +{ + const size_t bufferCount = 4 * count; + const size_t bufferSize = bufferCount * sizeof(T); + static T buffer[bufferCount]; + const T fillValue = 0xa5; + static T checkBuffer[bufferCount]; + + MOZ_ASSERT(bufferSize > 2 * sizeof(T)); + + memset(checkBuffer, fillValue, bufferSize); + + for (size_t startPosition = 0; startPosition < count; ++startPosition) { + for (size_t nValues = 0; nValues < count; ++nValues) { + memset(buffer, fillValue, bufferSize); + memcpy(buffer + startPosition, values, nValues * sizeof(T)); + swapperFunc(buffer + startPosition, nValues); + + MOZ_ASSERT(memcmp(buffer, checkBuffer, startPosition * sizeof(T)) == 0); + DebugOnly valuesEndPosition = startPosition + nValues; + MOZ_ASSERT(memcmp(buffer + valuesEndPosition, + checkBuffer + valuesEndPosition, + bufferSize - valuesEndPosition * sizeof(T)) == 0); + if (expectSwap == NoSwap) { + MOZ_ASSERT(memcmp(buffer + startPosition, values, + nValues * sizeof(T)) == 0); + } + for (size_t i = 0; i < nValues; ++i) + MOZ_ASSERT(readerFunc(buffer + startPosition + i) == values[i]); + } + } +} + +template +struct Reader +{ +}; + +#define SPECIALIZE_READER(TYPE, READ_FUNC) \ + template<> \ + struct Reader \ + { \ + static TYPE readLE(const void* p) { return LittleEndian::READ_FUNC(p); } \ + static TYPE readBE(const void* p) { return BigEndian::READ_FUNC(p); } \ + }; + +SPECIALIZE_READER(uint16_t, readUint16) +SPECIALIZE_READER(uint32_t, readUint32) +SPECIALIZE_READER(uint64_t, readUint64) +SPECIALIZE_READER(int16_t, readInt16) +SPECIALIZE_READER(int32_t, readInt32) +SPECIALIZE_READER(int64_t, readInt64) + +template +void +TestBulkSwap(const T (&bytes)[count]) +{ +#if MOZ_LITTLE_ENDIAN + TestBulkSwapToSub(Swap, bytes, copyAndSwapToBigEndian, Reader::readBE); + TestBulkSwapFromSub(Swap, bytes, copyAndSwapFromBigEndian, Reader::readBE); + TestBulkSwapToSub(Swap, bytes, copyAndSwapToNetworkOrder, Reader::readBE); + TestBulkSwapFromSub(Swap, bytes, copyAndSwapFromNetworkOrder, Reader::readBE); +#else + TestBulkSwapToSub(Swap, bytes, copyAndSwapToLittleEndian, Reader::readLE); + TestBulkSwapFromSub(Swap, bytes, copyAndSwapFromLittleEndian, Reader::readLE); +#endif +} + +template +void +TestBulkNoSwap(const T (&bytes)[count]) +{ +#if MOZ_LITTLE_ENDIAN + TestBulkSwapToSub(NoSwap, bytes, copyAndSwapToLittleEndian, Reader::readLE); + TestBulkSwapFromSub(NoSwap, bytes, copyAndSwapFromLittleEndian, Reader::readLE); +#else + TestBulkSwapToSub(NoSwap, bytes, copyAndSwapToBigEndian, Reader::readBE); + TestBulkSwapFromSub(NoSwap, bytes, copyAndSwapFromBigEndian, Reader::readBE); + TestBulkSwapToSub(NoSwap, bytes, copyAndSwapToNetworkOrder, Reader::readBE); + TestBulkSwapFromSub(NoSwap, bytes, copyAndSwapFromNetworkOrder, Reader::readBE); +#endif +} + +template +void +TestBulkInPlaceSwap(const T (&bytes)[count]) +{ +#if MOZ_LITTLE_ENDIAN + TestBulkInPlaceSub(Swap, bytes, swapToBigEndianInPlace, Reader::readBE); + TestBulkInPlaceSub(Swap, bytes, swapFromBigEndianInPlace, Reader::readBE); + TestBulkInPlaceSub(Swap, bytes, swapToNetworkOrderInPlace, Reader::readBE); + TestBulkInPlaceSub(Swap, bytes, swapFromNetworkOrderInPlace, Reader::readBE); +#else + TestBulkInPlaceSub(Swap, bytes, swapToLittleEndianInPlace, Reader::readLE); + TestBulkInPlaceSub(Swap, bytes, swapFromLittleEndianInPlace, Reader::readLE); +#endif +} + +template +void +TestBulkInPlaceNoSwap(const T (&bytes)[count]) +{ +#if MOZ_LITTLE_ENDIAN + TestBulkInPlaceSub(NoSwap, bytes, swapToLittleEndianInPlace, Reader::readLE); + TestBulkInPlaceSub(NoSwap, bytes, swapFromLittleEndianInPlace, Reader::readLE); +#else + TestBulkInPlaceSub(NoSwap, bytes, swapToBigEndianInPlace, Reader::readBE); + TestBulkInPlaceSub(NoSwap, bytes, swapFromBigEndianInPlace, Reader::readBE); + TestBulkInPlaceSub(NoSwap, bytes, swapToNetworkOrderInPlace, Reader::readBE); + TestBulkInPlaceSub(NoSwap, bytes, swapFromNetworkOrderInPlace, Reader::readBE); +#endif +} + +int +main() +{ + static const uint8_t unsigned_bytes[16] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8 }; + static const int8_t signed_bytes[16] = { -0x0f, -0x0e, -0x0d, -0x0c, -0x0b, -0x0a, -0x09, -0x08, + -0x0f, -0x0e, -0x0d, -0x0c, -0x0b, -0x0a, -0x09, -0x08 }; + static const uint16_t uint16_values[8] = { 0x102, 0x304, 0x506, 0x708, 0x102, 0x304, 0x506, 0x708 }; + static const int16_t int16_values[8] = { int16_t(0xf1f2), int16_t(0xf3f4), int16_t(0xf5f6), int16_t(0xf7f8), + int16_t(0xf1f2), int16_t(0xf3f4), int16_t(0xf5f6), int16_t(0xf7f8) }; + static const uint32_t uint32_values[4] = { 0x1020304, 0x5060708, 0x1020304, 0x5060708 }; + static const int32_t int32_values[4] = { int32_t(0xf1f2f3f4), int32_t(0xf5f6f7f8), + int32_t(0xf1f2f3f4), int32_t(0xf5f6f7f8) }; + static const uint64_t uint64_values[2] = { 0x102030405060708, 0x102030405060708 }; + static const int64_t int64_values[2] = { int64_t(0xf1f2f3f4f5f6f7f8), + int64_t(0xf1f2f3f4f5f6f7f8) }; + uint8_t buffer[8]; + + MOZ_ASSERT(LittleEndian::readUint16(&unsigned_bytes[0]) == 0x201); + MOZ_ASSERT(BigEndian::readUint16(&unsigned_bytes[0]) == 0x102); + + MOZ_ASSERT(LittleEndian::readUint32(&unsigned_bytes[0]) == 0x4030201U); + MOZ_ASSERT(BigEndian::readUint32(&unsigned_bytes[0]) == 0x1020304U); + + MOZ_ASSERT(LittleEndian::readUint64(&unsigned_bytes[0]) == 0x807060504030201ULL); + MOZ_ASSERT(BigEndian::readUint64(&unsigned_bytes[0]) == 0x102030405060708ULL); + + LittleEndian::writeUint16(&buffer[0], 0x201); + MOZ_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint16_t)) == 0); + BigEndian::writeUint16(&buffer[0], 0x102); + MOZ_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint16_t)) == 0); + + LittleEndian::writeUint32(&buffer[0], 0x4030201U); + MOZ_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint32_t)) == 0); + BigEndian::writeUint32(&buffer[0], 0x1020304U); + MOZ_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint32_t)) == 0); + + LittleEndian::writeUint64(&buffer[0], 0x807060504030201ULL); + MOZ_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint64_t)) == 0); + BigEndian::writeUint64(&buffer[0], 0x102030405060708ULL); + MOZ_ASSERT(memcmp(&unsigned_bytes[0], &buffer[0], sizeof(uint64_t)) == 0); + + MOZ_ASSERT(LittleEndian::readInt16(&signed_bytes[0]) == int16_t(0xf2f1)); + MOZ_ASSERT(BigEndian::readInt16(&signed_bytes[0]) == int16_t(0xf1f2)); + + MOZ_ASSERT(LittleEndian::readInt32(&signed_bytes[0]) == int32_t(0xf4f3f2f1)); + MOZ_ASSERT(BigEndian::readInt32(&signed_bytes[0]) == int32_t(0xf1f2f3f4)); + + MOZ_ASSERT(LittleEndian::readInt64(&signed_bytes[0]) == int64_t(0xf8f7f6f5f4f3f2f1LL)); + MOZ_ASSERT(BigEndian::readInt64(&signed_bytes[0]) == int64_t(0xf1f2f3f4f5f6f7f8LL)); + + LittleEndian::writeInt16(&buffer[0], 0xf2f1); + MOZ_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(int16_t)) == 0); + BigEndian::writeInt16(&buffer[0], 0xf1f2); + MOZ_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(int16_t)) == 0); + + LittleEndian::writeInt32(&buffer[0], 0xf4f3f2f1); + MOZ_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(int32_t)) == 0); + BigEndian::writeInt32(&buffer[0], 0xf1f2f3f4); + MOZ_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(int32_t)) == 0); + + LittleEndian::writeInt64(&buffer[0], 0xf8f7f6f5f4f3f2f1LL); + MOZ_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(int64_t)) == 0); + BigEndian::writeInt64(&buffer[0], 0xf1f2f3f4f5f6f7f8LL); + MOZ_ASSERT(memcmp(&signed_bytes[0], &buffer[0], sizeof(int64_t)) == 0); + + TestSingleSwap(uint16_t(0xf2f1), uint16_t(0xf1f2)); + TestSingleSwap(uint32_t(0xf4f3f2f1), uint32_t(0xf1f2f3f4)); + TestSingleSwap(uint64_t(0xf8f7f6f5f4f3f2f1), uint64_t(0xf1f2f3f4f5f6f7f8)); + + TestSingleSwap(int16_t(0xf2f1), int16_t(0xf1f2)); + TestSingleSwap(int32_t(0xf4f3f2f1), int32_t(0xf1f2f3f4)); + TestSingleSwap(int64_t(0xf8f7f6f5f4f3f2f1), int64_t(0xf1f2f3f4f5f6f7f8)); + + TestSingleNoSwap(uint16_t(0xf2f1), uint16_t(0xf2f1)); + TestSingleNoSwap(uint32_t(0xf4f3f2f1), uint32_t(0xf4f3f2f1)); + TestSingleNoSwap(uint64_t(0xf8f7f6f5f4f3f2f1), uint64_t(0xf8f7f6f5f4f3f2f1)); + + TestSingleNoSwap(int16_t(0xf2f1), int16_t(0xf2f1)); + TestSingleNoSwap(int32_t(0xf4f3f2f1), int32_t(0xf4f3f2f1)); + TestSingleNoSwap(int64_t(0xf8f7f6f5f4f3f2f1), int64_t(0xf8f7f6f5f4f3f2f1)); + + TestBulkSwap(uint16_values); + TestBulkSwap(int16_values); + TestBulkSwap(uint32_values); + TestBulkSwap(int32_values); + TestBulkSwap(uint64_values); + TestBulkSwap(int64_values); + + TestBulkNoSwap(uint16_values); + TestBulkNoSwap(int16_values); + TestBulkNoSwap(uint32_values); + TestBulkNoSwap(int32_values); + TestBulkNoSwap(uint64_values); + TestBulkNoSwap(int64_values); + + TestBulkInPlaceSwap(uint16_values); + TestBulkInPlaceSwap(int16_values); + TestBulkInPlaceSwap(uint32_values); + TestBulkInPlaceSwap(int32_values); + TestBulkInPlaceSwap(uint64_values); + TestBulkInPlaceSwap(int64_values); + + TestBulkInPlaceNoSwap(uint16_values); + TestBulkInPlaceNoSwap(int16_values); + TestBulkInPlaceNoSwap(uint32_values); + TestBulkInPlaceNoSwap(int32_values); + TestBulkInPlaceNoSwap(uint64_values); + TestBulkInPlaceNoSwap(int64_values); + + return 0; +} From e15f39cb8c6a39e38eb1ae4c9e235d22d3dbe237 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 30 Jan 2013 16:47:00 -0500 Subject: [PATCH 18/73] Bug 798172 - part 3 - convert SHA1.cpp to use Endian.h; r=Waldo --- mfbt/SHA1.cpp | 40 ++++++++++------------------------------ 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/mfbt/SHA1.cpp b/mfbt/SHA1.cpp index 5791bf27e041..f11b064ec7cb 100644 --- a/mfbt/SHA1.cpp +++ b/mfbt/SHA1.cpp @@ -3,20 +3,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/Assertions.h" +#include "mozilla/Endian.h" #include "mozilla/SHA1.h" #include -// FIXME: We should probably create a more complete mfbt/Endian.h. This assumes -// that any compiler that doesn't define these macros is little endian. -#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) -# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -# define MOZ_IS_LITTLE_ENDIAN -# endif -#else -# define MOZ_IS_LITTLE_ENDIAN -#endif - +using mozilla::NativeEndian; using mozilla::SHA1Sum; static inline uint32_t @@ -26,18 +18,6 @@ SHA_ROTL(uint32_t t, uint32_t n) return (t << n) | (t >> (32 - n)); } -static inline unsigned -SHA_HTONL(unsigned x) -{ -#ifdef MOZ_IS_LITTLE_ENDIAN - const unsigned int mask = 0x00FF00FF; - x = (x << 16) | (x >> 16); - return ((x & mask) << 8) | ((x >> 8) & mask); -#else - return x; -#endif -} - static void shaCompress(volatile unsigned* X, const uint32_t* datain); @@ -160,16 +140,16 @@ SHA1Sum::finish(SHA1Sum::Hash& hashOut) /* Convert size from bytes to bits. */ size2 <<= 3; - u.w[14] = SHA_HTONL(uint32_t(size2 >> 32)); - u.w[15] = SHA_HTONL(uint32_t(size2)); + u.w[14] = NativeEndian::swapToBigEndian(uint32_t(size2 >> 32)); + u.w[15] = NativeEndian::swapToBigEndian(uint32_t(size2)); shaCompress(&H[H2X], u.w); /* Output hash. */ - u.w[0] = SHA_HTONL(H[0]); - u.w[1] = SHA_HTONL(H[1]); - u.w[2] = SHA_HTONL(H[2]); - u.w[3] = SHA_HTONL(H[3]); - u.w[4] = SHA_HTONL(H[4]); + u.w[0] = NativeEndian::swapToBigEndian(H[0]); + u.w[1] = NativeEndian::swapToBigEndian(H[1]); + u.w[2] = NativeEndian::swapToBigEndian(H[2]); + u.w[3] = NativeEndian::swapToBigEndian(H[3]); + u.w[4] = NativeEndian::swapToBigEndian(H[4]); memcpy(hashOut, u.w, 20); mDone = true; } @@ -243,7 +223,7 @@ shaCompress(volatile unsigned *X, const uint32_t *inbuf) #define SHA_RND4(a, b, c, d, e, n) \ a = SHA_ROTL(b ,5) + SHA_F4(c, d, e) + a + XW(n) + K3; c = SHA_ROTL(c, 30) -#define LOAD(n) XW(n) = SHA_HTONL(inbuf[n]) +#define LOAD(n) XW(n) = NativeEndian::swapToBigEndian(inbuf[n]) A = XH(0); B = XH(1); From c4e1eb0113f8167bba5f623d9ef0cfcb14921b73 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 30 Jan 2013 16:47:52 -0500 Subject: [PATCH 19/73] Bug 798172 - part 4 - convert the jsclone bits to use Endian.h; r=Waldo --- js/src/jsclone.cpp | 110 +++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 69 deletions(-) diff --git a/js/src/jsclone.cpp b/js/src/jsclone.cpp index d20294e315b0..92e1990d2c8a 100644 --- a/js/src/jsclone.cpp +++ b/js/src/jsclone.cpp @@ -3,6 +3,7 @@ * 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 "mozilla/Endian.h" #include "mozilla/FloatingPoint.h" #include "jsclone.h" @@ -17,6 +18,8 @@ #include "vm/StringObject-inl.h" using namespace js; +using mozilla::LittleEndian; +using mozilla::NativeEndian; enum StructuredDataType { /* Structured data types provided by the engine */ @@ -74,52 +77,6 @@ JS_STATIC_ASSERT(SCTAG_END_OF_BUILTIN_TYPES <= JS_SCTAG_USER_MIN); JS_STATIC_ASSERT(JS_SCTAG_USER_MIN <= JS_SCTAG_USER_MAX); JS_STATIC_ASSERT(TypedArray::TYPE_INT8 == 0); -static uint8_t -SwapBytes(uint8_t u) -{ - return u; -} - -static uint16_t -SwapBytes(uint16_t u) -{ -#ifdef IS_BIG_ENDIAN - return ((u & 0x00ff) << 8) | ((u & 0xff00) >> 8); -#else - return u; -#endif -} - -static uint32_t -SwapBytes(uint32_t u) -{ -#ifdef IS_BIG_ENDIAN - return ((u & 0x000000ffU) << 24) | - ((u & 0x0000ff00U) << 8) | - ((u & 0x00ff0000U) >> 8) | - ((u & 0xff000000U) >> 24); -#else - return u; -#endif -} - -static uint64_t -SwapBytes(uint64_t u) -{ -#ifdef IS_BIG_ENDIAN - return ((u & 0x00000000000000ffLLU) << 56) | - ((u & 0x000000000000ff00LLU) << 40) | - ((u & 0x0000000000ff0000LLU) << 24) | - ((u & 0x00000000ff000000LLU) << 8) | - ((u & 0x000000ff00000000LLU) >> 8) | - ((u & 0x0000ff0000000000LLU) >> 24) | - ((u & 0x00ff000000000000LLU) >> 40) | - ((u & 0xff00000000000000LLU) >> 56); -#else - return u; -#endif -} - bool js::WriteStructuredClone(JSContext *cx, HandleValue v, uint64_t **bufp, size_t *nbytesp, const JSStructuredCloneCallbacks *cb, void *cbClosure, @@ -149,15 +106,15 @@ js::ClearStructuredClone(const uint64_t *data, size_t nbytes) const uint64_t *point = data; const uint64_t *end = data + nbytes / 8; - uint64_t u = SwapBytes(*point++); + uint64_t u = LittleEndian::readUint64(point++); uint32_t tag = uint32_t(u >> 32); if (tag == SCTAG_TRANSFER_MAP_HEADER) { if ((TransferableMapHeader)uint32_t(u) == SCTAG_TM_NOT_MARKED) { while (point != end) { - uint64_t u = SwapBytes(*point++); + uint64_t u = LittleEndian::readUint64(point++); uint32_t tag = uint32_t(u >> 32); if (tag == SCTAG_TRANSFER_MAP) { - u = SwapBytes(*point++); + u = LittleEndian::readUint64(point++); js_free(reinterpret_cast(u)); } } @@ -174,7 +131,7 @@ js::StructuredCloneHasTransferObjects(const uint64_t *data, size_t nbytes, bool *hasTransferable = false; if (data) { - uint64_t u = SwapBytes(*data); + uint64_t u = LittleEndian::readUint64(data); uint32_t tag = uint32_t(u >> 32); if (tag == SCTAG_TRANSFER_MAP_HEADER) { *hasTransferable = true; @@ -211,7 +168,7 @@ SCInput::read(uint64_t *p) *p = 0; /* initialize to shut GCC up */ return eof(); } - *p = SwapBytes(*point++); + *p = LittleEndian::readUint64(point++); return true; } @@ -232,7 +189,7 @@ SCInput::get(uint64_t *p) { if (point == end) return eof(); - *p = SwapBytes(*point); + *p = LittleEndian::readUint64(point); return true; } @@ -253,7 +210,7 @@ SCInput::replace(uint64_t u) { if (point == end) return eof(); - *point = SwapBytes(u); + LittleEndian::writeUint64(point, u); return true; } @@ -286,6 +243,20 @@ SCInput::readDouble(double *p) return true; } +template +static void +copyAndSwapFromLittleEndian(T *dest, const void *src, size_t nelems) +{ + NativeEndian::copyAndSwapFromLittleEndian(dest, src, nelems); +} + +template <> +void +copyAndSwapFromLittleEndian(uint8_t *dest, const void *src, size_t nelems) +{ + memcpy(dest, src, nelems); +} + template bool SCInput::readArray(T *p, size_t nelems) @@ -300,14 +271,7 @@ SCInput::readArray(T *p, size_t nelems) if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems || nwords > size_t(end - point)) return eof(); - if (sizeof(T) == 1) { - js_memcpy(p, point, nelems); - } else { - const T *q = (const T *) point; - const T *qend = q + nelems; - while (q != qend) - *p++ = ::SwapBytes(*q++); - } + copyAndSwapFromLittleEndian(p, point, nelems); point += nwords; return true; } @@ -341,7 +305,7 @@ SCOutput::SCOutput(JSContext *cx) : cx(cx), buf(cx) {} bool SCOutput::write(uint64_t u) { - return buf.append(SwapBytes(u)); + return buf.append(NativeEndian::swapToLittleEndian(u)); } bool @@ -393,6 +357,20 @@ SCOutput::writeDouble(double d) return write(ReinterpretDoubleAsUInt64(CanonicalizeNan(d))); } +template +static void +copyAndSwapToLittleEndian(void *dest, const T *src, size_t nelems) +{ + NativeEndian::copyAndSwapToLittleEndian(dest, src, nelems); +} + +template <> +void +copyAndSwapToLittleEndian(void *dest, const uint8_t *src, size_t nelems) +{ + memcpy(dest, src, nelems); +} + template bool SCOutput::writeArray(const T *p, size_t nelems) @@ -415,13 +393,7 @@ SCOutput::writeArray(const T *p, size_t nelems) buf.back() = 0; /* zero-pad to an 8-byte boundary */ T *q = (T *) &buf[start]; - if (sizeof(T) == 1) { - js_memcpy(q, p, nelems); - } else { - const T *pend = p + nelems; - while (p != pend) - *q++ = ::SwapBytes(*p++); - } + copyAndSwapToLittleEndian(q, p, nelems); return true; } From d7c78d45aac3f02e62e37e25a301eb6717ea8c12 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Wed, 30 Jan 2013 16:48:17 -0500 Subject: [PATCH 20/73] Bug 798172 - part 5 - convert xdr bits to use Endian.h; r=Waldo --- js/src/vm/Xdr.cpp | 21 ++----------- js/src/vm/Xdr.h | 77 +++++++++-------------------------------------- 2 files changed, 17 insertions(+), 81 deletions(-) diff --git a/js/src/vm/Xdr.cpp b/js/src/vm/Xdr.cpp index f3b0869758de..34fc4b6b0668 100644 --- a/js/src/vm/Xdr.cpp +++ b/js/src/vm/Xdr.cpp @@ -70,27 +70,10 @@ XDRState::codeChars(jschar *chars, size_t nchars) uint8_t *ptr = buf.write(nbytes); if (!ptr) return false; -#ifdef IS_LITTLE_ENDIAN - memcpy(ptr, chars, nbytes); -#else - for (size_t i = 0; i != nchars; i++) { - uint16_t tmp = NormalizeByteOrder16(chars[i]); - memcpy(ptr, &tmp, sizeof tmp); - ptr += sizeof tmp; - } -#endif + mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr, chars, nchars); } else { const uint8_t *ptr = buf.read(nbytes); -#ifdef IS_LITTLE_ENDIAN - memcpy(chars, ptr, nbytes); -#else - for (size_t i = 0; i != nchars; i++) { - uint16_t tmp; - memcpy(&tmp, ptr, sizeof tmp); - chars[i] = NormalizeByteOrder16(tmp); - ptr += sizeof tmp; - } -#endif + mozilla::NativeEndian::copyAndSwapFromLittleEndian(chars, ptr, nchars); } return true; } diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 9d007d367124..3ad573a6de0f 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -8,6 +8,8 @@ #ifndef Xdr_h___ #define Xdr_h___ +#include "mozilla/Endian.h" + #include "jsapi.h" #include "jsprvtd.h" #include "jsnum.h" @@ -88,40 +90,9 @@ class XDRBuffer { uint8_t *limit; }; -/* We use little-endian byteorder for all encoded data */ - -#if defined IS_LITTLE_ENDIAN - -inline uint32_t -NormalizeByteOrder32(uint32_t x) -{ - return x; -} - -inline uint16_t -NormalizeByteOrder16(uint16_t x) -{ - return x; -} - -#elif defined IS_BIG_ENDIAN - -inline uint32_t -NormalizeByteOrder32(uint32_t x) -{ - return (x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24); -} - -inline uint16_t -NormalizeByteOrder16(uint16_t x) -{ - return (x >> 8) | (x << 8); -} - -#else -#error "unknown byte order" -#endif - +/* + * XDR serialization state. All data is encoded in little endian. + */ template class XDRState { public: @@ -153,31 +124,27 @@ class XDRState { } bool codeUint16(uint16_t *n) { - uint16_t tmp; if (mode == XDR_ENCODE) { - uint8_t *ptr = buf.write(sizeof tmp); + uint8_t *ptr = buf.write(sizeof *n); if (!ptr) return false; - tmp = NormalizeByteOrder16(*n); - memcpy(ptr, &tmp, sizeof tmp); + mozilla::LittleEndian::writeUint16(ptr, *n); } else { - memcpy(&tmp, buf.read(sizeof tmp), sizeof tmp); - *n = NormalizeByteOrder16(tmp); + const uint8_t *ptr = buf.read(sizeof *n); + *n = mozilla::LittleEndian::readUint16(ptr); } return true; } bool codeUint32(uint32_t *n) { - uint32_t tmp; if (mode == XDR_ENCODE) { - uint8_t *ptr = buf.write(sizeof tmp); + uint8_t *ptr = buf.write(sizeof *n); if (!ptr) return false; - tmp = NormalizeByteOrder32(*n); - memcpy(ptr, &tmp, sizeof tmp); + mozilla::LittleEndian::writeUint32(ptr, *n); } else { - memcpy(&tmp, buf.read(sizeof tmp), sizeof tmp); - *n = NormalizeByteOrder32(tmp); + const uint8_t *ptr = buf.read(sizeof *n); + *n = mozilla::LittleEndian::readUint32(ptr); } return true; } @@ -187,24 +154,10 @@ class XDRState { uint8_t *ptr = buf.write(sizeof(*n)); if (!ptr) return false; - ptr[0] = (*n >> 0) & 0xFF; - ptr[1] = (*n >> 8) & 0xFF; - ptr[2] = (*n >> 16) & 0xFF; - ptr[3] = (*n >> 24) & 0xFF; - ptr[4] = (*n >> 32) & 0xFF; - ptr[5] = (*n >> 40) & 0xFF; - ptr[6] = (*n >> 48) & 0xFF; - ptr[7] = (*n >> 56) & 0xFF; + mozilla::LittleEndian::writeUint64(ptr, *n); } else { const uint8_t *ptr = buf.read(sizeof(*n)); - *n = (uint64_t(ptr[0]) << 0) | - (uint64_t(ptr[1]) << 8) | - (uint64_t(ptr[2]) << 16) | - (uint64_t(ptr[3]) << 24) | - (uint64_t(ptr[4]) << 32) | - (uint64_t(ptr[5]) << 40) | - (uint64_t(ptr[6]) << 48) | - (uint64_t(ptr[7]) << 56); + *n = mozilla::LittleEndian::readUint64(ptr); } return true; } From 793fdb86cbd136b485163523c83e92ddab5f96c2 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Wed, 3 Apr 2013 20:29:16 -0400 Subject: [PATCH 21/73] Backed out changeset e03a9a31e9c4 (bug 857617) because of build bustage Landed on a CLOSED TREE --- editor/libeditor/base/nsEditor.cpp | 196 +++++++++++++--------- editor/libeditor/text/nsTextEditUtils.cpp | 10 +- editor/libeditor/text/nsTextEditUtils.h | 2 +- 3 files changed, 123 insertions(+), 85 deletions(-) diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index 0e51641ba901..b610aeb42a60 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -103,7 +103,6 @@ #include "nsStyleStruct.h" // for nsStyleDisplay, nsStyleText, etc #include "nsStyleStructFwd.h" // for nsIFrame::StyleUIReset, etc #include "nsTextEditUtils.h" // for nsTextEditUtils -#include "nsTextNode.h" // for nsTextNode #include "nsThreadUtils.h" // for nsRunnable #include "nsTransactionManager.h" // for nsTransactionManager #include "prtime.h" // for PR_Now @@ -2295,100 +2294,141 @@ NS_IMETHODIMP nsEditor::ScrollSelectionIntoView(bool aScrollToAnchor) return NS_OK; } -NS_IMETHODIMP -nsEditor::InsertTextImpl(const nsAString& aStringToInsert, - nsCOMPtr* aInOutNode, - int32_t* aInOutOffset, - nsIDOMDocument* aDoc) +NS_IMETHODIMP nsEditor::InsertTextImpl(const nsAString& aStringToInsert, + nsCOMPtr *aInOutNode, + int32_t *aInOutOffset, + nsIDOMDocument *aDoc) { - // NOTE: caller *must* have already used nsAutoTxnsConserveSelection - // stack-based class to turn off txn selection updating. Caller also turned - // on rules sniffing if desired. - - NS_ENSURE_TRUE(aInOutNode && *aInOutNode && aInOutOffset && aDoc, - NS_ERROR_NULL_POINTER); - if (!mInIMEMode && aStringToInsert.IsEmpty()) { - return NS_OK; - } - - nsCOMPtr node = do_QueryInterface(*aInOutNode); - NS_ENSURE_STATE(node); - uint32_t offset = static_cast(*aInOutOffset); - - if (!node->IsNodeOfType(nsINode::eTEXT) && IsPlaintextEditor()) { - nsCOMPtr root = GetRoot(); - // In some cases, node is the anonymous DIV, and offset is 0. To avoid - // injecting unneeded text nodes, we first look to see if we have one - // available. In that case, we'll just adjust node and offset accordingly. - if (node == root && offset == 0 && node->HasChildren() && - node->GetFirstChild()->IsNodeOfType(nsINode::eTEXT)) { - node = node->GetFirstChild(); + // NOTE: caller *must* have already used nsAutoTxnsConserveSelection stack-based + // class to turn off txn selection updating. Caller also turned on rules sniffing + // if desired. + + nsresult res; + NS_ENSURE_TRUE(aInOutNode && *aInOutNode && aInOutOffset && aDoc, NS_ERROR_NULL_POINTER); + if (!mInIMEMode && aStringToInsert.IsEmpty()) return NS_OK; + nsCOMPtr nodeAsText = do_QueryInterface(*aInOutNode); + if (!nodeAsText && IsPlaintextEditor()) { + nsCOMPtr rootNode = do_QueryInterface(GetRoot()); + // In some cases, aInOutNode is the anonymous DIV, and aInOutOffset is 0. + // To avoid injecting unneeded text nodes, we first look to see if we have + // one available. In that case, we'll just adjust aInOutNode and aInOutOffset + // accordingly. + if (*aInOutNode == rootNode && *aInOutOffset == 0) { + nsCOMPtr possibleTextNode; + res = (*aInOutNode)->GetFirstChild(getter_AddRefs(possibleTextNode)); + if (NS_SUCCEEDED(res)) { + nodeAsText = do_QueryInterface(possibleTextNode); + if (nodeAsText) { + *aInOutNode = possibleTextNode; + } + } } - // In some other cases, node is the anonymous DIV, and offset points to the - // terminating mozBR. In that case, we'll adjust aInOutNode and - // aInOutOffset to the preceding text node, if any. - if (node == root && offset > 0 && node->GetChildAt(offset - 1) && - node->GetChildAt(offset - 1)->IsNodeOfType(nsINode::eTEXT)) { - node = node->GetChildAt(offset - 1); - offset = node->Length(); + // In some other cases, aInOutNode is the anonymous DIV, and aInOutOffset points + // to the terminating mozBR. In that case, we'll adjust aInOutNode and aInOutOffset + // to the preceding text node, if any. + if (!nodeAsText && *aInOutNode == rootNode && *aInOutOffset > 0) { + nsCOMPtr children; + res = (*aInOutNode)->GetChildNodes(getter_AddRefs(children)); + if (NS_SUCCEEDED(res)) { + nsCOMPtr possibleMozBRNode; + children->Item(*aInOutOffset, getter_AddRefs(possibleMozBRNode)); + if (possibleMozBRNode && nsTextEditUtils::IsMozBR(possibleMozBRNode)) { + nsCOMPtr possibleTextNode; + res = children->Item(*aInOutOffset - 1, getter_AddRefs(possibleTextNode)); + if (NS_SUCCEEDED(res)) { + nodeAsText = do_QueryInterface(possibleTextNode); + if (nodeAsText) { + uint32_t length; + res = nodeAsText->GetLength(&length); + if (NS_SUCCEEDED(res)) { + *aInOutOffset = int32_t(length); + *aInOutNode = possibleTextNode; + } + } + } + } else { + // The selection might be at the end of the last textnode child, + // in which case we can just append to the textnode in question. + nsCOMPtr possibleTextNode; + res = children->Item(*aInOutOffset - 1, getter_AddRefs(possibleTextNode)); + nodeAsText = do_QueryInterface(possibleTextNode); + if (nodeAsText) { + uint32_t length; + res = nodeAsText->GetLength(&length); + if (NS_SUCCEEDED(res)) { + *aInOutOffset = int32_t(length); + *aInOutNode = possibleTextNode; + } + } + } + } } - // Sometimes, node is the mozBR element itself. In that case, we'll adjust - // the insertion point to the previous text node, if one exists, or to the - // parent anonymous DIV. - if (nsTextEditUtils::IsMozBR(node) && offset == 0) { - if (node->GetPreviousSibling() && - node->GetPreviousSibling()->IsNodeOfType(nsINode::eTEXT)) { - node = node->GetPreviousSibling(); - offset = node->Length(); - } else if (node->GetParentNode() && node->GetParentNode() == root) { - node = node->GetParentNode(); + // Sometimes, aInOutNode is the mozBR element itself. In that case, we'll + // adjust the insertion point to the previous text node, if one exists, or + // to the parent anonymous DIV. + if (nsTextEditUtils::IsMozBR(*aInOutNode) && *aInOutOffset == 0) { + nsCOMPtr previous; + (*aInOutNode)->GetPreviousSibling(getter_AddRefs(previous)); + nodeAsText = do_QueryInterface(previous); + if (nodeAsText) { + uint32_t length; + res = nodeAsText->GetLength(&length); + if (NS_SUCCEEDED(res)) { + *aInOutOffset = int32_t(length); + *aInOutNode = previous; + } + } else { + nsCOMPtr parent; + (*aInOutNode)->GetParentNode(getter_AddRefs(parent)); + if (parent == rootNode) { + *aInOutNode = parent; + } } } } - - nsresult res; - if (mInIMEMode) { - if (!node->IsNodeOfType(nsINode::eTEXT)) { + int32_t offset = *aInOutOffset; + if (mInIMEMode) + { + if (!nodeAsText) + { // create a text node - nsCOMPtr doc = do_QueryInterface(aDoc); - NS_ENSURE_STATE(doc); - nsRefPtr newNode = doc->CreateTextNode(EmptyString()); - // then we insert it into the dom tree - res = InsertNode(newNode->AsDOMNode(), node->AsDOMNode(), offset); + res = aDoc->CreateTextNode(EmptyString(), getter_AddRefs(nodeAsText)); + NS_ENSURE_SUCCESS(res, res); + NS_ENSURE_TRUE(nodeAsText, NS_ERROR_NULL_POINTER); + nsCOMPtr newNode = do_QueryInterface(nodeAsText); + // then we insert it into the dom tree + res = InsertNode(newNode, *aInOutNode, offset); NS_ENSURE_SUCCESS(res, res); - node = newNode; offset = 0; } - nsCOMPtr charDataNode = do_QueryInterface(node); - NS_ENSURE_STATE(charDataNode); - res = InsertTextIntoTextNodeImpl(aStringToInsert, charDataNode, offset); + res = InsertTextIntoTextNodeImpl(aStringToInsert, nodeAsText, offset); NS_ENSURE_SUCCESS(res, res); - offset += aStringToInsert.Length(); - } else { - if (node->IsNodeOfType(nsINode::eTEXT)) { + } + else + { + if (nodeAsText) + { // we are inserting text into an existing text node. - nsCOMPtr charDataNode = do_QueryInterface(node); - NS_ENSURE_STATE(charDataNode); - res = InsertTextIntoTextNodeImpl(aStringToInsert, charDataNode, offset); + res = InsertTextIntoTextNodeImpl(aStringToInsert, nodeAsText, offset); NS_ENSURE_SUCCESS(res, res); - offset += aStringToInsert.Length(); - } else { - // we are inserting text into a non-text node. first we have to create a - // textnode (this also populates it with the text) - nsCOMPtr doc = do_QueryInterface(aDoc); - NS_ENSURE_STATE(doc); - nsRefPtr newNode = doc->CreateTextNode(aStringToInsert); + *aInOutOffset += aStringToInsert.Length(); + } + else + { + // we are inserting text into a non-text node + // first we have to create a textnode (this also populates it with the text) + res = aDoc->CreateTextNode(aStringToInsert, getter_AddRefs(nodeAsText)); + NS_ENSURE_SUCCESS(res, res); + NS_ENSURE_TRUE(nodeAsText, NS_ERROR_NULL_POINTER); + nsCOMPtr newNode = do_QueryInterface(nodeAsText); // then we insert it into the dom tree - res = InsertNode(newNode->AsDOMNode(), node->AsDOMNode(), offset); + res = InsertNode(newNode, *aInOutNode, offset); NS_ENSURE_SUCCESS(res, res); - node = newNode; - offset = aStringToInsert.Length(); + *aInOutNode = newNode; + *aInOutOffset = aStringToInsert.Length(); } } - - *aInOutNode = node->AsDOMNode(); - *aInOutOffset = static_cast(offset); - return NS_OK; + return res; } diff --git a/editor/libeditor/text/nsTextEditUtils.cpp b/editor/libeditor/text/nsTextEditUtils.cpp index eb9895a4e070..408de26d2810 100644 --- a/editor/libeditor/text/nsTextEditUtils.cpp +++ b/editor/libeditor/text/nsTextEditUtils.cpp @@ -52,14 +52,12 @@ nsTextEditUtils::IsMozBR(nsIDOMNode *node) bool -nsTextEditUtils::IsMozBR(nsINode* aNode) +nsTextEditUtils::IsMozBR(dom::Element* aNode) { MOZ_ASSERT(aNode); - return aNode->IsElement() && - aNode->AsElement()->IsHTML(nsGkAtoms::br) && - aNode->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, - NS_LITERAL_STRING("_moz"), - eIgnoreCase); + return aNode->IsHTML(nsGkAtoms::br) && + aNode->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, + NS_LITERAL_STRING("_moz"), eIgnoreCase); } /////////////////////////////////////////////////////////////////////////// diff --git a/editor/libeditor/text/nsTextEditUtils.h b/editor/libeditor/text/nsTextEditUtils.h index 30e7aa9cdc5f..54d842375b1b 100644 --- a/editor/libeditor/text/nsTextEditUtils.h +++ b/editor/libeditor/text/nsTextEditUtils.h @@ -24,7 +24,7 @@ public: static bool IsBody(nsIDOMNode* aNode); static bool IsBreak(nsIDOMNode* aNode); static bool IsMozBR(nsIDOMNode* aNode); - static bool IsMozBR(nsINode* aNode); + static bool IsMozBR(mozilla::dom::Element* aNode); static bool HasMozAttr(nsIDOMNode* aNode); }; From e9c81fccf8fbc9fbb93aafcace6b42d7f78bd39d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guilherme=20Gon=C3=A7alves?= Date: Wed, 3 Apr 2013 17:43:14 -0700 Subject: [PATCH 22/73] Bug 855704 - Log error messages in webspeech. r=smaug --- content/media/webspeech/recognition/test/head.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/content/media/webspeech/recognition/test/head.js b/content/media/webspeech/recognition/test/head.js index 9dcbc992d1df..465c6a04e6b7 100644 --- a/content/media/webspeech/recognition/test/head.js +++ b/content/media/webspeech/recognition/test/head.js @@ -55,7 +55,12 @@ function EventManager(sr) { for (var i = 0; i < allEvents.length; i++) { (function (eventName) { sr["on" + eventName] = function (evt) { - ok(false, "unexpected event: " + eventName); + var message = "unexpected event: " + eventName; + if (eventName == "error") { + message += " -- " + evt.message; + } + + ok(false, message); if (self.done) self.done(); }; })(allEvents[i]); From 1ebd0fa550a22dc40886c338b0a537020f8e6e4d Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Wed, 3 Apr 2013 17:43:02 -0700 Subject: [PATCH 23/73] Bug 855536 - Add initExtendedSlot (r=njn) --- js/src/jsfun.cpp | 2 +- js/src/jsfun.h | 7 ++++++- js/src/jsfuninlines.h | 7 +++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 56b0eaf8929f..96d9eb2a4057 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1567,7 +1567,7 @@ js::CloneFunctionObject(JSContext *cx, HandleFunction fun, HandleObject parent, clone->flags |= JSFunction::EXTENDED; if (fun->isExtended() && fun->compartment() == cx->compartment) { for (unsigned i = 0; i < FunctionExtended::NUM_EXTENDED_SLOTS; i++) - clone->setExtendedSlot(i, fun->getExtendedSlot(i)); + clone->initExtendedSlot(i, fun->getExtendedSlot(i)); } else { clone->initializeExtended(); } diff --git a/js/src/jsfun.h b/js/src/jsfun.h index da56858ae0fb..157da8a1d877 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -301,8 +301,13 @@ class JSFunction : public JSObject return !!(flags & EXTENDED); } - /* Accessors for data stored in extended functions. */ + /* + * Accessors for data stored in extended functions. Use setExtendedSlot if + * the function has already been initialized. Otherwise use + * initExtendedSlot. + */ inline void initializeExtended(); + inline void initExtendedSlot(size_t which, const js::Value &val); inline void setExtendedSlot(size_t which, const js::Value &val); inline const js::Value &getExtendedSlot(size_t which) const; diff --git a/js/src/jsfuninlines.h b/js/src/jsfuninlines.h index 10bad1727d11..0a0b49ad79c4 100644 --- a/js/src/jsfuninlines.h +++ b/js/src/jsfuninlines.h @@ -91,6 +91,13 @@ JSFunction::initializeExtended() toExtended()->extendedSlots[1].init(js::UndefinedValue()); } +inline void +JSFunction::initExtendedSlot(size_t which, const js::Value &val) +{ + JS_ASSERT(which < mozilla::ArrayLength(toExtended()->extendedSlots)); + toExtended()->extendedSlots[which].init(val); +} + inline void JSFunction::setExtendedSlot(size_t which, const js::Value &val) { From f0f5a3a37d2e87aacc1afa812a22aa1c601b5013 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 3 Apr 2013 20:46:06 -0400 Subject: [PATCH 24/73] Bug 847983 - Skip script text nodes for content events; r=masayuki --- content/events/src/nsContentEventHandler.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/content/events/src/nsContentEventHandler.cpp b/content/events/src/nsContentEventHandler.cpp index de978925bf4c..523b0e75453e 100644 --- a/content/events/src/nsContentEventHandler.cpp +++ b/content/events/src/nsContentEventHandler.cpp @@ -230,6 +230,10 @@ static uint32_t CountNewlinesInNativeLength(nsIContent* aContent, nsContentEventHandler::GetNativeTextLength(nsIContent* aContent, uint32_t aMaxLength) { if (aContent->IsNodeOfType(nsINode::eTEXT)) { + // Skip text nodes without frames, e.g. inside script elements + if (!aContent->GetPrimaryFrame()) { + return 0; + } uint32_t textLengthDifference = #if defined(XP_MACOSX) // On Mac, the length of a native newline ("\r") is equal to the length of @@ -309,6 +313,10 @@ static nsresult GenerateFlatTextContent(nsRange* aRange, nsIContent* content = static_cast(node); if (content->IsNodeOfType(nsINode::eTEXT)) { + // Skip text nodes without frames, e.g. inside script elements + if (!content->GetPrimaryFrame()) { + continue; + } if (content == startNode) AppendSubString(aString, content, aRange->StartOffset(), content->TextLength() - aRange->StartOffset()); From 48106edeba6b8c3f5b3a4f7bf4309f5e363d1b78 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 3 Apr 2013 20:46:06 -0400 Subject: [PATCH 25/73] Bug 847983 - Don't include empty elements at end of range; r=masayuki --- content/events/src/nsContentEventHandler.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/content/events/src/nsContentEventHandler.cpp b/content/events/src/nsContentEventHandler.cpp index 523b0e75453e..1d21384dd46c 100644 --- a/content/events/src/nsContentEventHandler.cpp +++ b/content/events/src/nsContentEventHandler.cpp @@ -388,6 +388,11 @@ nsContentEventHandler::SetRangeFromFlatTextOffset( uint32_t nativeOffset = 0; uint32_t nativeEndOffset = aNativeOffset + aNativeLength; + // When we clip the end offset to the end of the root element, set it to after + // the last valid node. This way we don't include any extraneous nodes, such + // as the bogus editor BR node, in our range. + nsINode *lastValidNode = mRootContent; + uint32_t lastValidOffset = 0; bool startSet = false; for (; !iter->IsDone(); iter->Next()) { nsINode* node = iter->GetCurrentNode(); @@ -402,6 +407,8 @@ nsContentEventHandler::SetRangeFromFlatTextOffset( if (nativeTextLength == 0) continue; + lastValidNode = node->GetParent(); + lastValidOffset = lastValidNode->IndexOf(node) + 1; if (nativeOffset <= aNativeOffset && aNativeOffset < nativeOffset + nativeTextLength) { nsCOMPtr domNode(do_QueryInterface(content)); @@ -455,17 +462,16 @@ nsContentEventHandler::SetRangeFromFlatTextOffset( nativeOffset += nativeTextLength; } - if (nativeOffset < aNativeOffset) - return NS_ERROR_FAILURE; + NS_ENSURE_TRUE(aNativeOffset <= nativeOffset, NS_ERROR_FAILURE); - nsCOMPtr domNode(do_QueryInterface(mRootContent)); + nsCOMPtr domNode(do_QueryInterface(lastValidNode)); NS_ASSERTION(domNode, "lastContent doesn't have nsIDOMNode!"); if (!startSet) { - MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT)); - rv = aRange->SetStart(domNode, int32_t(mRootContent->GetChildCount())); + MOZ_ASSERT(!domNode->IsNodeOfType(nsINode::eTEXT)); + rv = aRange->SetStart(domNode, int32_t(lastValidOffset)); NS_ENSURE_SUCCESS(rv, rv); } - rv = aRange->SetEnd(domNode, int32_t(mRootContent->GetChildCount())); + rv = aRange->SetEnd(domNode, int32_t(lastValidOffset)); NS_ASSERTION(NS_SUCCEEDED(rv), "nsIDOMRange::SetEnd failed"); return rv; } From c4af316f9fbff79b220d35943f334535e00e2894 Mon Sep 17 00:00:00 2001 From: Jim Chen Date: Wed, 3 Apr 2013 20:46:06 -0400 Subject: [PATCH 26/73] Bug 847983 - Try harder to get a valid frame; r=masayuki --- content/events/src/nsContentEventHandler.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/content/events/src/nsContentEventHandler.cpp b/content/events/src/nsContentEventHandler.cpp index 1d21384dd46c..a8ed17f841c6 100644 --- a/content/events/src/nsContentEventHandler.cpp +++ b/content/events/src/nsContentEventHandler.cpp @@ -106,6 +106,9 @@ nsContentEventHandler::Init(nsQueryContentEvent* aEvent) nsRect r; nsIFrame* frame = caret->GetGeometry(mSelection, &r); + if (!frame) { + frame = mRootContent->GetPrimaryFrame(); + } NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); aEvent->mReply.mFocusedWidget = frame->GetNearestWidget(); From 235a253ffee0ba22ae5ac484e93cfc62ad8d956e Mon Sep 17 00:00:00 2001 From: Rodrigo Silveira Date: Wed, 3 Apr 2013 13:19:34 -0700 Subject: [PATCH 27/73] bug 856241 - Remove closing multiple tabs dialog r=mbrubeck --HG-- extra : rebase_source : e53aa69ccac48e784c1947f8544c68c313ccbf2c --- browser/metro/base/content/browser.js | 39 ------------------- browser/metro/components/PromptService.js | 2 +- .../locales/en-US/chrome/browser.properties | 10 ----- browser/metro/profile/metro.js | 1 - 4 files changed, 1 insertion(+), 51 deletions(-) diff --git a/browser/metro/base/content/browser.js b/browser/metro/base/content/browser.js index c7f707371f56..85d1a243e461 100644 --- a/browser/metro/base/content/browser.js +++ b/browser/metro/base/content/browser.js @@ -214,46 +214,7 @@ var Browser = { } }, - _waitingToClose: false, closing: function closing() { - // If we are already waiting for the close prompt, don't show another - if (this._waitingToClose) - return false; - - // Prompt if we have multiple tabs before closing window - let numTabs = this._tabs.length; - if (numTabs > 1) { - let shouldPrompt = Services.prefs.getBoolPref("browser.tabs.warnOnClose"); - if (shouldPrompt) { - let prompt = Services.prompt; - - // Default to true: if it were false, we wouldn't get this far - let warnOnClose = { value: true }; - - let messageBase = Strings.browser.GetStringFromName("tabs.closeWarning"); - let message = PluralForm.get(numTabs, messageBase).replace("#1", numTabs); - - let title = Strings.browser.GetStringFromName("tabs.closeWarningTitle"); - let closeText = Strings.browser.GetStringFromName("tabs.closeButton"); - let checkText = Strings.browser.GetStringFromName("tabs.closeWarningPromptMe"); - let buttons = (prompt.BUTTON_TITLE_IS_STRING * prompt.BUTTON_POS_0) + - (prompt.BUTTON_TITLE_CANCEL * prompt.BUTTON_POS_1); - - this._waitingToClose = true; - let pressed = prompt.confirmEx(window, title, message, buttons, closeText, null, null, checkText, warnOnClose); - this._waitingToClose = false; - - // Don't set the pref unless they press OK and it's false - let reallyClose = (pressed == 0); - if (reallyClose && !warnOnClose.value) - Services.prefs.setBoolPref("browser.tabs.warnOnClose", false); - - // If we don't want to close, return now. If we are closing, continue with other housekeeping. - if (!reallyClose) - return false; - } - } - // Figure out if there's at least one other browser window around. let lastBrowser = true; let e = Services.wm.getEnumerator("navigator:browser"); diff --git a/browser/metro/components/PromptService.js b/browser/metro/components/PromptService.js index f2f7cdcb5c22..06c7e402774f 100644 --- a/browser/metro/components/PromptService.js +++ b/browser/metro/components/PromptService.js @@ -218,7 +218,7 @@ Prompt.prototype = { if (aCheck && aCheck.msg) { aDoc.getElementById("prompt-" + aType + "-checkbox").checked = aCheck.value; - this.setLabelForNode(aDoc.getElementById("prompt-" + aType + "-checkbox-label"), aCheck.msg); + this.setLabelForNode(aDoc.getElementById("prompt-" + aType + "-checkbox"), aCheck.msg); aDoc.getElementById("prompt-" + aType + "-checkbox").removeAttribute("collapsed"); } }, diff --git a/browser/metro/locales/en-US/chrome/browser.properties b/browser/metro/locales/en-US/chrome/browser.properties index ee08579ac994..4c9594b71458 100644 --- a/browser/metro/locales/en-US/chrome/browser.properties +++ b/browser/metro/locales/en-US/chrome/browser.properties @@ -73,16 +73,6 @@ offlineApps.wantsTo=%S wants to store data on your device for offline use. indexedDBQuota.allow=Allow indexedDBQuota.wantsTo=%S wants to store a lot of data on your device for offline use. -# Closing Tabs -tabs.closeWarningTitle=Confirm close - -# LOCALIZATION NOTE (tabs.closeWarning): Semi-colon list of plural forms. -# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals -# #1 number of tabs (must be greater than 1) -tabs.closeWarning=NOT USED;You are about to close #1 tabs. Continue? - -tabs.closeButton=Close tabs -tabs.closeWarningPromptMe=Warn me when I attempt to close multiple tabs tabs.emptyTabTitle=New Tab # Open Search diff --git a/browser/metro/profile/metro.js b/browser/metro/profile/metro.js index df9722fb1a8b..69eafae61a1b 100644 --- a/browser/metro/profile/metro.js +++ b/browser/metro/profile/metro.js @@ -67,7 +67,6 @@ pref("browser.chromeURL", "chrome://browser/content/"); // When true, always show the tab strip and use desktop-style tabs (no thumbnails) pref("browser.tabs.tabsOnly", false); -pref("browser.tabs.warnOnClose", true); pref("browser.tabs.remote", false); // Telemetry From 707d7b011aa6983c2f9693df12f14a353611b577 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Wed, 3 Apr 2013 21:24:24 -0400 Subject: [PATCH 28/73] Backed out changesets 35ba787bc3db, e95379bbcfd5, and fff4886f812c (bug 847983) for build bustage on a CLOSED TREE. --- content/events/src/nsContentEventHandler.cpp | 29 ++++---------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/content/events/src/nsContentEventHandler.cpp b/content/events/src/nsContentEventHandler.cpp index a8ed17f841c6..de978925bf4c 100644 --- a/content/events/src/nsContentEventHandler.cpp +++ b/content/events/src/nsContentEventHandler.cpp @@ -106,9 +106,6 @@ nsContentEventHandler::Init(nsQueryContentEvent* aEvent) nsRect r; nsIFrame* frame = caret->GetGeometry(mSelection, &r); - if (!frame) { - frame = mRootContent->GetPrimaryFrame(); - } NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); aEvent->mReply.mFocusedWidget = frame->GetNearestWidget(); @@ -233,10 +230,6 @@ static uint32_t CountNewlinesInNativeLength(nsIContent* aContent, nsContentEventHandler::GetNativeTextLength(nsIContent* aContent, uint32_t aMaxLength) { if (aContent->IsNodeOfType(nsINode::eTEXT)) { - // Skip text nodes without frames, e.g. inside script elements - if (!aContent->GetPrimaryFrame()) { - return 0; - } uint32_t textLengthDifference = #if defined(XP_MACOSX) // On Mac, the length of a native newline ("\r") is equal to the length of @@ -316,10 +309,6 @@ static nsresult GenerateFlatTextContent(nsRange* aRange, nsIContent* content = static_cast(node); if (content->IsNodeOfType(nsINode::eTEXT)) { - // Skip text nodes without frames, e.g. inside script elements - if (!content->GetPrimaryFrame()) { - continue; - } if (content == startNode) AppendSubString(aString, content, aRange->StartOffset(), content->TextLength() - aRange->StartOffset()); @@ -391,11 +380,6 @@ nsContentEventHandler::SetRangeFromFlatTextOffset( uint32_t nativeOffset = 0; uint32_t nativeEndOffset = aNativeOffset + aNativeLength; - // When we clip the end offset to the end of the root element, set it to after - // the last valid node. This way we don't include any extraneous nodes, such - // as the bogus editor BR node, in our range. - nsINode *lastValidNode = mRootContent; - uint32_t lastValidOffset = 0; bool startSet = false; for (; !iter->IsDone(); iter->Next()) { nsINode* node = iter->GetCurrentNode(); @@ -410,8 +394,6 @@ nsContentEventHandler::SetRangeFromFlatTextOffset( if (nativeTextLength == 0) continue; - lastValidNode = node->GetParent(); - lastValidOffset = lastValidNode->IndexOf(node) + 1; if (nativeOffset <= aNativeOffset && aNativeOffset < nativeOffset + nativeTextLength) { nsCOMPtr domNode(do_QueryInterface(content)); @@ -465,16 +447,17 @@ nsContentEventHandler::SetRangeFromFlatTextOffset( nativeOffset += nativeTextLength; } - NS_ENSURE_TRUE(aNativeOffset <= nativeOffset, NS_ERROR_FAILURE); + if (nativeOffset < aNativeOffset) + return NS_ERROR_FAILURE; - nsCOMPtr domNode(do_QueryInterface(lastValidNode)); + nsCOMPtr domNode(do_QueryInterface(mRootContent)); NS_ASSERTION(domNode, "lastContent doesn't have nsIDOMNode!"); if (!startSet) { - MOZ_ASSERT(!domNode->IsNodeOfType(nsINode::eTEXT)); - rv = aRange->SetStart(domNode, int32_t(lastValidOffset)); + MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT)); + rv = aRange->SetStart(domNode, int32_t(mRootContent->GetChildCount())); NS_ENSURE_SUCCESS(rv, rv); } - rv = aRange->SetEnd(domNode, int32_t(lastValidOffset)); + rv = aRange->SetEnd(domNode, int32_t(mRootContent->GetChildCount())); NS_ASSERTION(NS_SUCCEEDED(rv), "nsIDOMRange::SetEnd failed"); return rv; } From 028424632981d455b322c581de7c84d1555bf62a Mon Sep 17 00:00:00 2001 From: Anthony Jones Date: Wed, 27 Mar 2013 11:25:12 +1300 Subject: [PATCH 29/73] Bug 852251 - Check for null on CompositorChild::Get() --- dom/ipc/TabChild.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 9ec4a1f46318..50e0877ad327 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -2104,10 +2104,15 @@ TabChild::InitRenderingState() if (id != 0) { // Pushing layers transactions directly to a separate // compositor context. + PCompositorChild* compositorChild = CompositorChild::Get(); + if (!compositorChild) { + NS_WARNING("failed to get CompositorChild instance"); + return false; + } shadowManager = - CompositorChild::Get()->SendPLayersConstructor(be, id, - &be, - &maxTextureSize); + compositorChild->SendPLayersConstructor(be, id, + &be, + &maxTextureSize); } else { // Pushing transactions to the parent content. shadowManager = remoteFrame->SendPLayersConstructor(); From 4d5dd53b5ea5dcf531751fce301b367c8f622c24 Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Wed, 3 Apr 2013 19:18:42 -0700 Subject: [PATCH 30/73] Bug 856486 (Part 1) - Avoid asserts triggered by rapid off-main-thread decoding. r=jrmuizel --- image/src/RasterImage.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index a5942f75730d..c1cc9f3109c2 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -2817,7 +2817,7 @@ RasterImage::RequestDecodeCore(RequestDecodeType aDecodeType) // data, so signal that we want a full decode and give up for now. if (!mHasSize) { mWantFullDecode = true; - return NS_ERROR_NOT_AVAILABLE; + return NS_OK; } } @@ -3525,7 +3525,8 @@ RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_D } // If we were a size decode and a full decode was requested, now's the time. - if (NS_SUCCEEDED(rv) && done && wasSize && image->mWantFullDecode) { + if (NS_SUCCEEDED(rv) && aIntent != eShutdownIntent_Error && done && + wasSize && image->mWantFullDecode) { image->mWantFullDecode = false; // If we're not meant to be storing source data and we just got the size, From 9b33d58640963496ac4b35e55bb0c044d523d7c7 Mon Sep 17 00:00:00 2001 From: Seth Fowler Date: Wed, 3 Apr 2013 19:19:38 -0700 Subject: [PATCH 31/73] Bug 856486 (Part 2) - Buffer the last fully-decoded frame for multipart images. r=jrmuizel --- image/src/RasterImage.cpp | 42 ++++++++++++++++++++++++++++++++++++--- image/src/RasterImage.h | 5 ++++- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index c1cc9f3109c2..4d37faf0023f 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -385,6 +385,7 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker, ImageResource(aStatusTracker, aURI), // invoke superclass's constructor mSize(0,0), mFrameDecodeFlags(DECODE_FLAGS_DEFAULT), + mMultipartDecodedFrame(nullptr), mAnim(nullptr), mLoopCount(-1), mLockCount(0), @@ -458,6 +459,8 @@ RasterImage::~RasterImage() for (unsigned int i = 0; i < mFrames.Length(); ++i) delete mFrames[i]; + delete mMultipartDecodedFrame; + // Total statistics num_containers--; total_source_bytes -= mSourceData.Length(); @@ -1571,6 +1574,25 @@ RasterImage::DecodingComplete() NS_ENSURE_SUCCESS(rv, rv); } + // Double-buffer our frame in the multipart case, since we'll start decoding + // into mFrames again immediately and this produces severe tearing. + if (mMultipart) { + if (mFrames.Length() == 1) { + imgFrame* swapFrame = mMultipartDecodedFrame; + mMultipartDecodedFrame = GetImgFrameNoDecode(GetCurrentFrameIndex()); + mFrames.Clear(); + if (swapFrame) { + mFrames.AppendElement(swapFrame); + } + } else { + // Don't double buffer for animated multipart images. It entails more + // complexity and it's not really needed since we already are smart about + // not displaying the still-decoding frame of an animated image. We may + // have already stored an extra frame, though, so we'll release it here. + delete mMultipartDecodedFrame; + } + } + return NS_OK; } @@ -2473,6 +2495,10 @@ RasterImage::Discard(bool force) mScaleResult.status = SCALE_INVALID; mScaleResult.frame = nullptr; + // Clear the last decoded multipart frame. + delete mMultipartDecodedFrame; + mMultipartDecodedFrame = nullptr; + // Flag that we no longer have decoded frames for this image mDecoded = false; @@ -3193,9 +3219,19 @@ RasterImage::Draw(gfxContext *aContext, NS_ENSURE_SUCCESS(rv, rv); } - uint32_t frameIndex = aWhichFrame == FRAME_FIRST ? 0 - : GetCurrentImgFrameIndex(); - imgFrame *frame = GetDrawableImgFrame(frameIndex); + imgFrame* frame = nullptr; + + if (mMultipart) { + // In the multipart case we prefer to use mMultipartDecodedFrame, which is + // the most recent one we completely decoded, rather than display the real + // current frame and risk severe tearing. + frame = mMultipartDecodedFrame; + } + if (!frame) { + uint32_t frameIndex = aWhichFrame == FRAME_FIRST ? 0 + : GetCurrentImgFrameIndex(); + frame = GetDrawableImgFrame(frameIndex); + } if (!frame) { return NS_OK; // Getting the frame (above) touches the image and kicks off decoding } diff --git a/image/src/RasterImage.h b/image/src/RasterImage.h index 0d591c422889..e1f907de7ae7 100644 --- a/image/src/RasterImage.h +++ b/image/src/RasterImage.h @@ -729,7 +729,10 @@ private: // data // IMPORTANT: if you use mFrames in a method, call EnsureImageIsDecoded() first // to ensure that the frames actually exist (they may have been discarded to save // memory, or we may be decoding on draw). - nsTArray mFrames; + nsTArray mFrames; + + // The last frame we decoded for multipart images. + imgFrame* mMultipartDecodedFrame; nsCOMPtr mProperties; From 142568ca7e4202cff9c58fa719ab92c78c3ebcbc Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 3 Apr 2013 22:22:14 -0400 Subject: [PATCH 32/73] Bug 854532. Make _asInstance codegen always produce code in the same order. r=khuey Also fixes whitespace in _hasInstance and makes sure the .pp files are always in the same order --- dom/bindings/BindingGen.py | 7 +++++-- dom/bindings/Codegen.py | 9 ++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/dom/bindings/BindingGen.py b/dom/bindings/BindingGen.py index 07cf5c9958a6..80a8a8ed473f 100644 --- a/dom/bindings/BindingGen.py +++ b/dom/bindings/BindingGen.py @@ -19,7 +19,9 @@ def generate_binding_header(config, outputprefix, srcprefix, webidlfile): with open(filename, 'wb') as f: f.write(root.declare()) with open(depsname, 'wb') as f: - f.write("\n".join(filename + ": " + os.path.join(srcprefix, x) for x in root.deps())) + # Sort so that our output is stable + f.write("\n".join(filename + ": " + os.path.join(srcprefix, x) for + x in sorted(root.deps()))) def generate_binding_cpp(config, outputprefix, srcprefix, webidlfile): """ @@ -33,7 +35,8 @@ def generate_binding_cpp(config, outputprefix, srcprefix, webidlfile): with open(filename, 'wb') as f: f.write(root.define()) with open(depsname, 'wb') as f: - f.write("\n".join(filename + ": " + os.path.join(srcprefix, x) for x in root.deps())) + f.write("\n".join(filename + ": " + os.path.join(srcprefix, x) for + x in sorted(root.deps()))) def main(): diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 9190bb04365c..7acd957a872a 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1105,13 +1105,16 @@ class CGClassHasInstanceHook(CGAbstractStaticMethod): const DOMClass* domClass = GetDOMClass(js::UnwrapObject(instance)); *bp = false; """ - for iface in self.descriptor.interface.interfacesImplementingSelf: + # Sort interaces implementing self by name so we get stable output. + for iface in sorted(self.descriptor.interface.interfacesImplementingSelf, + key=lambda iface: iface.identifier.name): hasInstanceCode += """ if (domClass->mInterfaceChain[PrototypeTraits::Depth] == prototypes::id::%s) { *bp = true; return true; - }""" % (iface.identifier.name, iface.identifier.name) - hasInstanceCode += "return true;" + } +""" % (iface.identifier.name, iface.identifier.name) + hasInstanceCode += " return true;" return header + hasInstanceCode; def isChromeOnly(m): From 38246c449e653f9bdc8651b5a47aae52b931a2c3 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 3 Apr 2013 22:22:15 -0400 Subject: [PATCH 33/73] Bug 852219 part 1. Don't mark JS-implemented interfaces with descendant interfaces as final. r=khuey,mccr8 --- dom/bindings/Codegen.py | 23 ++++++++++++++++++----- dom/bindings/parser/WebIDL.py | 6 ++++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 7acd957a872a..7a07e33a7d62 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -5546,7 +5546,7 @@ ${className}::${className}(${args})${initializationList} class ClassDestructor(ClassItem): """ - Used for adding a constructor to a CGClass. + Used for adding a destructor to a CGClass. inline should be True if the destructor should be marked inline. @@ -5556,17 +5556,22 @@ class ClassDestructor(ClassItem): visibility determines the visibility of the destructor (public, protected, private), defaults to private. - body contains a string with the code for the destructor, defaults to None. + body contains a string with the code for the destructor, defaults to empty. + + virtual determines whether the destructor is virtual, defaults to False. """ def __init__(self, inline=False, bodyInHeader=False, - visibility="private", body=None): + visibility="private", body='', virtual=False): self.inline = inline or bodyInHeader self.bodyInHeader = bodyInHeader self.body = body + self.virtual = virtual ClassItem.__init__(self, None, visibility) def getDecorators(self, declaring): decorators = [] + if self.virtual and declaring: + decorators.append('virtual') if self.inline and declaring: decorators.append('inline') if decorators: @@ -5574,7 +5579,6 @@ class ClassDestructor(ClassItem): return '' def getBody(self): - assert self.body is not None return self.body def declare(self, cgClass): @@ -8105,6 +8109,14 @@ class CGJSImplClass(CGBindingImplClass): " NS_INTERFACE_MAP_ENTRY(nsISupports)\n" "NS_INTERFACE_MAP_END\n").substitute({ "ifaceName": self.descriptor.name }) + if descriptor.interface.hasChildInterfaces(): + decorators = "" + # We need a public virtual destructor our subclasses can use + destructor = ClassDestructor(virtual=True, visibility="public") + else: + decorators = "MOZ_FINAL" + destructor = None + CGClass.__init__(self, descriptor.name, bases=[ClassBase("nsISupports"), ClassBase("nsWrapperCache")], @@ -8114,8 +8126,9 @@ class CGJSImplClass(CGBindingImplClass): baseConstructors=["mImpl(aImpl)", "mParent(aParent)"], body="SetIsDOMBinding();")], + destructor=destructor, methods=self.methodDecls, - decorators="MOZ_FINAL", + decorators=decorators, extradeclarations=extradeclarations, extradefinitions=extradefinitions) diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index 0e2177749630..c307116ab98c 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -506,6 +506,7 @@ class IDLInterface(IDLObjectWithScope): # self.interfacesImplementingSelf is the set of interfaces that directly # have self as a consequential interface self.interfacesImplementingSelf = set() + self._hasChildInterfaces = False IDLObjectWithScope.__init__(self, location, parentScope, name) @@ -567,6 +568,8 @@ class IDLInterface(IDLObjectWithScope): if self.parent: self.parent.finish(scope) + self.parent._hasChildInterfaces = True + # Callbacks must not inherit from non-callbacks or inherit from # anything that has consequential interfaces. # XXXbz Can non-callbacks inherit from callbacks? Spec issue pending. @@ -977,6 +980,9 @@ class IDLInterface(IDLObjectWithScope): def isJSImplemented(self): return bool(self.getJSImplementation()) + def hasChildInterfaces(self): + return self._hasChildInterfaces + def _getDependentObjects(self): deps = set(self.members) deps.union(self.implementedInterfaces) From d21bd00b481d0f6b9a67050dfd422f6979196828 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 3 Apr 2013 22:22:15 -0400 Subject: [PATCH 34/73] Bug 852219 part 2. Add an infallible constructor for CallbackObjects which are already in the right compartment and use this to simplify construction of the autogenerated implementation of a JS-implemented WebIDL binding. r=mccr8 --- dom/bindings/CallbackInterface.h | 10 ++++++ dom/bindings/CallbackObject.h | 28 ++++++++++++----- dom/bindings/Codegen.py | 52 +++++++++++++++++--------------- 3 files changed, 57 insertions(+), 33 deletions(-) diff --git a/dom/bindings/CallbackInterface.h b/dom/bindings/CallbackInterface.h index 9895eb1a4836..2cf51ecf9d2c 100644 --- a/dom/bindings/CallbackInterface.h +++ b/dom/bindings/CallbackInterface.h @@ -37,6 +37,16 @@ public: { } + /* + * Create a CallbackInterface without any sort of interesting games with + * compartments, for cases when you want to just use the existing object + * as-is. This constructor can never fail. + */ + explicit CallbackInterface(JSObject* aCallback) + : CallbackObject(aCallback) + { + } + protected: bool GetCallableProperty(JSContext* cx, const char* aPropName, JS::Value* aCallable); diff --git a/dom/bindings/CallbackObject.h b/dom/bindings/CallbackObject.h index 8607967ce4ba..feae2884f2c3 100644 --- a/dom/bindings/CallbackObject.h +++ b/dom/bindings/CallbackObject.h @@ -62,15 +62,20 @@ public: } } - // Set mCallback before we hold, on the off chance that a GC could somehow - // happen in there... (which would be pretty odd, granted). - mCallback = aCallback; - // Make sure we'll be able to drop as needed - nsLayoutStatics::AddRef(); - NS_HOLD_JS_OBJECTS(this, CallbackObject); + Init(aCallback); *aInited = true; } + /* + * Create a CallbackObject without any sort of interesting games with + * compartments, for cases when you want to just use the existing object + * as-is. This constructor can never fail. + */ + explicit CallbackObject(JSObject* aCallback) + { + Init(aCallback); + } + virtual ~CallbackObject() { DropCallback(); @@ -101,16 +106,23 @@ public: }; protected: - explicit CallbackObject(CallbackObject* aCallbackFunction) - : mCallback(aCallbackFunction->mCallback) + explicit CallbackObject(CallbackObject* aCallbackObject) + { + Init(aCallbackObject->mCallback); + } + +private: + inline void Init(JSObject* aCallback) { // Set mCallback before we hold, on the off chance that a GC could somehow // happen in there... (which would be pretty odd, granted). + mCallback = aCallback; // Make sure we'll be able to drop as needed nsLayoutStatics::AddRef(); NS_HOLD_JS_OBJECTS(this, CallbackObject); } +protected: void DropCallback() { if (mCallback) { diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 7a07e33a7d62..32f980b08173 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -5468,11 +5468,11 @@ class ClassConstructor(ClassItem): baseConstructors is a list of strings containing calls to base constructors, defaults to None. - body contains a string with the code for the constructor, defaults to None. + body contains a string with the code for the constructor, defaults to empty. """ def __init__(self, args, inline=False, bodyInHeader=False, visibility="private", explicit=False, baseConstructors=None, - body=None): + body=""): self.args = args self.inline = inline or bodyInHeader self.bodyInHeader = bodyInHeader @@ -5504,7 +5504,6 @@ class ClassConstructor(ClassItem): return '' def getBody(self): - assert self.body is not None return self.body def declare(self, cgClass): @@ -6085,8 +6084,7 @@ const DOMClass Class = """ + DOMClass(self.descriptor) + """; class CGDOMJSProxyHandler_CGDOMJSProxyHandler(ClassConstructor): def __init__(self): ClassConstructor.__init__(self, [], inline=True, visibility="private", - baseConstructors=["mozilla::dom::DOMProxyHandler(Class)"], - body="") + baseConstructors=["mozilla::dom::DOMProxyHandler(Class)"]) class CGDOMJSProxyHandler_getOwnPropertyDescriptor(ClassMethod): def __init__(self, descriptor): @@ -8035,17 +8033,9 @@ class CGJSImplMethod(CGNativeMember): aRv.Throw(NS_ERROR_FAILURE); return nullptr; } - // Construct the callback interface object. - bool initOk; - nsRefPtr<${callbackClass}> cbImpl = new ${callbackClass}(cx, nullptr, jsImplObj, &initOk); - if (!initOk) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - // Build the actual implementation. - nsRefPtr<${implClass}> impl = new ${implClass}(cbImpl, window); + // Build the C++ implementation. + nsRefPtr<${implClass}> impl = new ${implClass}(jsImplObj, window); return impl.forget();""").substitute({"implClass" : self.descriptor.name, - "callbackClass" : jsImplName(self.descriptor.name), "contractId" : self.descriptor.interface.getJSImplementation() }) @@ -8117,15 +8107,19 @@ class CGJSImplClass(CGBindingImplClass): decorators = "MOZ_FINAL" destructor = None + constructor = ClassConstructor( + [Argument("JSObject*", "aJSImplObject"), + Argument("nsISupports*", "aParent")], + visibility="public", + baseConstructors=["mImpl(new %s(aJSImplObject))" % + jsImplName(descriptor.name), + "mParent(aParent)"], + body="SetIsDOMBinding();") + CGClass.__init__(self, descriptor.name, bases=[ClassBase("nsISupports"), ClassBase("nsWrapperCache")], - constructors=[ClassConstructor([Argument(jsImplName(descriptor.name) + "*", "aImpl"), - Argument("nsISupports*", "aParent")], - visibility="public", - baseConstructors=["mImpl(aImpl)", - "mParent(aParent)"], - body="SetIsDOMBinding();")], + constructors=[constructor], destructor=destructor, methods=self.methodDecls, decorators=decorators, @@ -8178,8 +8172,7 @@ class CGCallback(CGClass): visibility="public", baseConstructors=[ "%s(cx, aOwner, aCallback, aInited)" % self.baseName - ], - body="")] + ])] def getMethodImpls(self, method): assert method.needThisHandling @@ -8255,8 +8248,7 @@ class CGCallbackFunction(CGCallback): explicit=True, baseConstructors=[ "CallbackFunction(aOther)" - ], - body="")] + ])] class CGCallbackInterface(CGCallback): def __init__(self, descriptor): @@ -8272,6 +8264,16 @@ class CGCallbackInterface(CGCallback): CGCallback.__init__(self, iface, descriptor, "CallbackInterface", methods, getters=getters, setters=setters) + def getConstructors(self): + return CGCallback.getConstructors(self) + [ + ClassConstructor( + [Argument("JSObject*", "aCallback")], + bodyInHeader=True, + visibility="public", + explicit=True, + baseConstructors=["CallbackInterface(aCallback)"]) + ] + class FakeMember(): def __init__(self): self.treatUndefinedAs = self.treatNullAs = "Default" From 793e37ca3f66ea78c2ce7c3a2eefb87ed89469ba Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 3 Apr 2013 22:22:15 -0400 Subject: [PATCH 35/73] Bug 852219 part 3. Handle JS-implemented interfaces having a parent interface. r=mccr8 --- dom/bindings/Codegen.py | 71 ++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 32f980b08173..b9ff47cd605a 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -8080,24 +8080,51 @@ class CGJSImplClass(CGBindingImplClass): def __init__(self, descriptor): CGBindingImplClass.__init__(self, descriptor, CGJSImplMethod, CGJSImplGetter, CGJSImplSetter) + if descriptor.interface.parent: + parentClass = descriptor.getDescriptor( + descriptor.interface.parent.identifier.name).nativeType + baseClasses = [ClassBase(parentClass)] + isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED" + ccDecl = ("NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)" % + (descriptor.name, parentClass)) + constructorBody = ( + "// Make sure we're an nsWrapperCache already\n" + "MOZ_ASSERT(static_cast(this));\n" + "// And that our ancestor has called SetIsDOMBinding()\n" + "MOZ_ASSERT(IsDOMBinding());") + extradefinitions= string.Template( + "NS_IMPL_CYCLE_COLLECTION_INHERITED_2(${ifaceName}, ${parentClass}, mImpl, mParent)\n" + "NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass})\n" + "NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass})\n" + "NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${ifaceName})\n" + "NS_INTERFACE_MAP_END_INHERITING(${parentClass})\n").substitute( + { "ifaceName": self.descriptor.name, + "parentClass": parentClass }) + else: + baseClasses = [ClassBase("nsISupports"), + ClassBase("nsWrapperCache")] + isupportsDecl = "NS_DECL_CYCLE_COLLECTING_ISUPPORTS" + ccDecl = ("NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)" % + descriptor.name) + constructorBody = "SetIsDOMBinding();" + extradefinitions= string.Template( + "NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(${ifaceName}, mImpl, mParent)\n" + "NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName})\n" + "NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName})\n" + "NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})\n" + " NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY\n" + " NS_INTERFACE_MAP_ENTRY(nsISupports)\n" + "NS_INTERFACE_MAP_END\n").substitute({ "ifaceName": self.descriptor.name }) + extradeclarations=( "public:\n" - " NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n" - " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n" + " %s\n" + " %s\n" "\n" "private:\n" " nsRefPtr<%s> mImpl;\n" " nsCOMPtr mParent;\n" - "\n" % (descriptor.name, jsImplName(descriptor.name))) - - extradefinitions= string.Template( - "NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(${ifaceName}, mImpl, mParent)\n" - "NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName})\n" - "NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName})\n" - "NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})\n" - " NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY\n" - " NS_INTERFACE_MAP_ENTRY(nsISupports)\n" - "NS_INTERFACE_MAP_END\n").substitute({ "ifaceName": self.descriptor.name }) + "\n" % (isupportsDecl, ccDecl, jsImplName(descriptor.name))) if descriptor.interface.hasChildInterfaces(): decorators = "" @@ -8107,18 +8134,26 @@ class CGJSImplClass(CGBindingImplClass): decorators = "MOZ_FINAL" destructor = None + baseConstructors=["mImpl(new %s(aJSImplObject))" % jsImplName(descriptor.name), + "mParent(aParent)"] + parentInterface = descriptor.interface.parent + while parentInterface: + if parentInterface.isJSImplemented(): + baseConstructors = ( + [ "%s(aJSImplObject, aParent)" % parentClass ] + + baseConstructors ) + break + parentInterface = parentInterface.parent + constructor = ClassConstructor( [Argument("JSObject*", "aJSImplObject"), Argument("nsISupports*", "aParent")], visibility="public", - baseConstructors=["mImpl(new %s(aJSImplObject))" % - jsImplName(descriptor.name), - "mParent(aParent)"], - body="SetIsDOMBinding();") + baseConstructors=baseConstructors, + body=constructorBody) CGClass.__init__(self, descriptor.name, - bases=[ClassBase("nsISupports"), - ClassBase("nsWrapperCache")], + bases=baseClasses, constructors=[constructor], destructor=destructor, methods=self.methodDecls, From 94f45fb924e1b15b8e24d29d24e8920ceaa12000 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 3 Apr 2013 22:22:15 -0400 Subject: [PATCH 36/73] Bug 852219 part 4. Fix forward-declarations and includes for JS-implemented interfaces. r=mccr8 --- dom/bindings/Codegen.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index b9ff47cd605a..9e7e49b93ec9 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -478,7 +478,7 @@ class CGHeaders(CGWrapper): def __init__(self, descriptors, dictionaries, callbacks, callbackDescriptors, declareIncludes, defineIncludes, child, - config=None, anyJSImplemented=False): + config=None, jsImplementedDescriptors=[]): """ Builds a set of includes to cover |descriptors|. @@ -594,12 +594,20 @@ class CGHeaders(CGWrapper): # And we need BindingUtils.h so we can wrap "this" objects declareIncludes.add("mozilla/dom/BindingUtils.h") - if len(callbackDescriptors) != 0 or anyJSImplemented: + if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0: # We need CallbackInterface to serve as our parent class declareIncludes.add("mozilla/dom/CallbackInterface.h") # And we need BindingUtils.h so we can wrap "this" objects declareIncludes.add("mozilla/dom/BindingUtils.h") + # Also need to include the headers for ancestors of + # JS-implemented interfaces. + for jsImplemented in jsImplementedDescriptors: + jsParent = jsImplemented.interface.parent + if jsParent: + parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name) + declareIncludes.add(parentDesc.headerFile) + # Let the machinery do its thing. def _includeString(includes): return ''.join(['#include "%s"\n' % i for i in includes]) + '\n' @@ -7161,7 +7169,7 @@ class CGBindingRoot(CGThing): for callback in workerCallbacks: workerIfaces.extend(getInterfacesFromCallback(callback)) - for callbackDescriptor in callbackDescriptors: + for callbackDescriptor in callbackDescriptors + jsImplemented: callbackDescriptorIfaces = [ t.unroll().inner for t in getTypesFromDescriptor(callbackDescriptor) @@ -7219,6 +7227,13 @@ class CGBindingRoot(CGThing): for t in getTypesFromDictionary(dictionary) if t.unroll().isCallback()) + # Forward declarations for callback functions used in interfaces + for desc in descriptors: + forwardDeclares.extend( + declareNativeType("mozilla::dom::" + str(t.unroll())) + for t in getTypesFromDescriptor(desc) + if t.unroll().isCallback()) + forwardDeclares = CGList(forwardDeclares) descriptorsWithPrototype = filter(lambda d: d.interface.hasInterfacePrototypeObject(), @@ -7335,7 +7350,7 @@ class CGBindingRoot(CGThing): ], curr, config, - anyJSImplemented = len(jsImplemented) != 0) + jsImplemented) # Add include guards. curr = CGIncludeGuard(prefix, curr) From f9536eed2bae49fa9fc410146e6c3d9d8376b356 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 3 Apr 2013 22:22:15 -0400 Subject: [PATCH 37/73] Bug 852219 part 5. Add tests for JS-implemented interfaces inheriting from other interfaces. r=mccr8 --- dom/bindings/Bindings.conf | 26 +++++++++++++ dom/bindings/test/TestCImplementedInterface.h | 39 +++++++++++++++++++ dom/bindings/test/TestJSImplGen.webidl | 6 +++ .../test/TestJSImplInheritanceGen.webidl | 13 +++++++ dom/webidl/WebIDL.mk | 1 + 5 files changed, 85 insertions(+) create mode 100644 dom/bindings/test/TestCImplementedInterface.h create mode 100644 dom/bindings/test/TestJSImplInheritanceGen.webidl diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index d8495ff13f71..b67b12c4072d 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1178,6 +1178,16 @@ DOMInterfaces = { 'register': False, }, +'TestCImplementedInterface' : { + 'headerFile': 'TestCImplementedInterface.h', + 'register': False, + }, + +'TestCImplementedInterface2' : { + 'headerFile': 'TestCImplementedInterface.h', + 'register': False, + }, + 'TestJSImplInterface' : { # Keep this in sync with TestExampleInterface 'headerFile': 'TestJSImplGenBinding.h', @@ -1196,6 +1206,22 @@ DOMInterfaces = { 'attributeRenamedFrom': 'attributeRenamedTo' } }, +'TestJSImplInterface' : { + # Keep this in sync with TestExampleInterface + 'headerFile': 'TestJSImplGenBinding.h', + 'register': False + }, + +'TestJSImplInterface2' : { + 'headerFile': 'TestJSImplGenBinding.h', + 'register': False + }, + +'TestJSImplInterface3' : { + 'headerFile': 'TestJSImplGenBinding.h', + 'register': False + }, + 'TestExternalInterface' : { 'nativeType': 'mozilla::dom::TestExternalInterface', 'headerFile': 'TestBindingHeader.h', diff --git a/dom/bindings/test/TestCImplementedInterface.h b/dom/bindings/test/TestCImplementedInterface.h new file mode 100644 index 000000000000..71ba98ab618b --- /dev/null +++ b/dom/bindings/test/TestCImplementedInterface.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; 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/. + */ + +#ifndef TestCImplementedInterface_h +#define TestCImplementedInterface_h + +#include "../TestJSImplGenBinding.h" + +namespace mozilla { +namespace dom { + +class TestCImplementedInterface : public TestJSImplInterface +{ +public: + TestCImplementedInterface(JSObject* aJSImpl, nsISupports* aParent) + : TestJSImplInterface(aJSImpl, aParent) + {} +}; + +class TestCImplementedInterface2 : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestCImplementedInterface2) + + // We need a GetParentObject to make binding codegen happy + nsISupports* GetParentObject(); +}; + + + +} // namespace dom +} // namespace mozilla + +#endif // TestCImplementedInterface_h diff --git a/dom/bindings/test/TestJSImplGen.webidl b/dom/bindings/test/TestJSImplGen.webidl index 3a33055cef0a..42edf973abd2 100644 --- a/dom/bindings/test/TestJSImplGen.webidl +++ b/dom/bindings/test/TestJSImplGen.webidl @@ -413,3 +413,9 @@ interface TestJSImplInterface { */ // If you add things here, add them to TestCodeGen as well }; + +interface TestCImplementedInterface : TestJSImplInterface { +}; + +interface TestCImplementedInterface2 { +}; diff --git a/dom/bindings/test/TestJSImplInheritanceGen.webidl b/dom/bindings/test/TestJSImplInheritanceGen.webidl new file mode 100644 index 000000000000..43c996f8454f --- /dev/null +++ b/dom/bindings/test/TestJSImplInheritanceGen.webidl @@ -0,0 +1,13 @@ +/* -*- 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/. + */ + +[Constructor, JSImplementation="@mozilla.org/test-js-impl-interface2;1"] +interface TestJSImplInterface2 : TestCImplementedInterface { +}; + +[Constructor, JSImplementation="@mozilla.org/test-js-impl-interface3;1"] +interface TestJSImplInterface3 : TestCImplementedInterface2 { +}; diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index 48d1625944fd..75a931be487b 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -339,6 +339,7 @@ test_webidl_files := \ TestDictionary.webidl \ TestExampleGen.webidl \ TestJSImplGen.webidl \ + TestJSImplInheritanceGen.webidl \ TestTypedef.webidl \ $(NULL) else From 48d68f9676d50b83af8e53be0690bd1562032279 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 3 Apr 2013 22:22:16 -0400 Subject: [PATCH 38/73] Bug 852219 part 6. Handle cases when the C++ class we want to actually inherit from is not the one that the WebIDL interface is mapped to. r=mccr8 --- dom/bindings/Bindings.conf | 6 ++++++ dom/bindings/Codegen.py | 4 ++-- dom/bindings/Configuration.py | 5 +++++ dom/bindings/test/TestJSImplInheritanceGen.webidl | 4 ++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index b67b12c4072d..1300e069b6d8 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -322,6 +322,7 @@ DOMInterfaces = { { 'hasXPConnectImpls': True, 'concrete': False, + 'jsImplParent': 'nsDOMEventTargetHelper' }, { 'workers': True, @@ -1222,6 +1223,11 @@ DOMInterfaces = { 'register': False }, +'TestJSImplInterface4' : { + 'headerFile': 'TestJSImplGenBinding.h', + 'register': False + }, + 'TestExternalInterface' : { 'nativeType': 'mozilla::dom::TestExternalInterface', 'headerFile': 'TestBindingHeader.h', diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 9e7e49b93ec9..74a52fd065de 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -606,7 +606,7 @@ class CGHeaders(CGWrapper): jsParent = jsImplemented.interface.parent if jsParent: parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name) - declareIncludes.add(parentDesc.headerFile) + declareIncludes.add(parentDesc.jsImplParentHeader) # Let the machinery do its thing. def _includeString(includes): @@ -8097,7 +8097,7 @@ class CGJSImplClass(CGBindingImplClass): if descriptor.interface.parent: parentClass = descriptor.getDescriptor( - descriptor.interface.parent.identifier.name).nativeType + descriptor.interface.parent.identifier.name).jsImplParent baseClasses = [ClassBase(parentClass)] isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED" ccDecl = ("NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)" % diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index c485a038c378..1d5d03f95868 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -211,6 +211,7 @@ class Descriptor(DescriptorProvider): nativeTypeDefault = "mozilla::dom::" + ifaceName self.nativeType = desc.get('nativeType', nativeTypeDefault) + self.jsImplParent = desc.get('jsImplParent', self.nativeType) # Do something sane for JSObject if self.nativeType == "JSObject": @@ -229,6 +230,10 @@ class Descriptor(DescriptorProvider): headerDefault = self.nativeType headerDefault = headerDefault.replace("::", "/") + ".h" self.headerFile = desc.get('headerFile', headerDefault) + if self.jsImplParent == self.nativeType: + self.jsImplParentHeader = self.headerFile + else: + self.jsImplParentHeader = self.jsImplParent.replace("::", "/") + ".h" self.skipGen = desc.get('skipGen', False) diff --git a/dom/bindings/test/TestJSImplInheritanceGen.webidl b/dom/bindings/test/TestJSImplInheritanceGen.webidl index 43c996f8454f..57e618a5d87f 100644 --- a/dom/bindings/test/TestJSImplInheritanceGen.webidl +++ b/dom/bindings/test/TestJSImplInheritanceGen.webidl @@ -11,3 +11,7 @@ interface TestJSImplInterface2 : TestCImplementedInterface { [Constructor, JSImplementation="@mozilla.org/test-js-impl-interface3;1"] interface TestJSImplInterface3 : TestCImplementedInterface2 { }; + +[Constructor, JSImplementation="@mozilla.org/test-js-impl-interface4;1"] +interface TestJSImplInterface4 : EventTarget { +}; From b6f5bb741b59574eba7588c8c81894c432c3ccef Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 3 Apr 2013 22:22:16 -0400 Subject: [PATCH 39/73] Bug 856841. Don't pass in an implicit JSContext based on argument or return value types for JS-implemented interfaces. r=mccr8 --- dom/bindings/Codegen.py | 14 +++++++++----- dom/bindings/test/TestJSImplGen.webidl | 10 ++++++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 74a52fd065de..b7e1b3392417 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -3921,8 +3921,9 @@ if (global.Failed()) { """ % globalObjectType)) argsPre.append("global") - needsCx = needCx(returnType, arguments, self.extendedAttributes, - descriptor) + needsCx = (not descriptor.interface.isJSImplemented() and + needCx(returnType, arguments, self.extendedAttributes, + descriptor)) if needsCx and not (static and descriptor.workers): argsPre.append("cx") @@ -8005,7 +8006,8 @@ class CGJSImplMethod(CGNativeMember): signature, descriptor.getExtendedAttributes(method), breakAfter=breakAfter, - variadicIsSequence=True) + variadicIsSequence=True, + passCxAsNeeded=False) self.signature = signature if isConstructor: self.body = self.getConstructorImpl() @@ -8068,7 +8070,8 @@ class CGJSImplGetter(CGNativeMember): attr), (attr.type, []), descriptor.getExtendedAttributes(attr, - getter=True)) + getter=True), + passCxAsNeeded=False) self.body = self.getImpl() def getImpl(self): @@ -8083,7 +8086,8 @@ class CGJSImplSetter(CGNativeMember): (BuiltinTypes[IDLBuiltinType.Types.void], [FakeArgument(attr.type, attr)]), descriptor.getExtendedAttributes(attr, - setter=True)) + setter=True), + passCxAsNeeded=False) self.body = self.getImpl() def getImpl(self): diff --git a/dom/bindings/test/TestJSImplGen.webidl b/dom/bindings/test/TestJSImplGen.webidl index 42edf973abd2..977b9016b7ce 100644 --- a/dom/bindings/test/TestJSImplGen.webidl +++ b/dom/bindings/test/TestJSImplGen.webidl @@ -311,22 +311,24 @@ interface TestJSImplInterface { //void passOptionalNullableTreatAsNullCallback(optional TestTreatAsNullCallback? arg); void passOptionalNullableTreatAsNullCallbackWithDefaultValue(optional TestTreatAsNullCallback? arg = null); -/* The rest of these are untested. // Any types void passAny(any arg); void passOptionalAny(optional any arg); void passAnyDefaultNull(optional any arg = null); any receiveAny(); - // object types - void passObject(object arg); + // object types. Unfortunately, non-nullable object is inconsistently + // represented as either JSObject* (for callbacks) or JSObject& (for + // non-callbacks), so we can't handle those yet. See bug 856911. + //(BUG 856911) void passObject(object arg); void passNullableObject(object? arg); - void passOptionalObject(optional object arg); + //(BUG 856911) void passOptionalObject(optional object arg); void passOptionalNullableObject(optional object? arg); void passOptionalNullableObjectWithDefaultValue(optional object? arg = null); object receiveObject(); object? receiveNullableObject(); +/* The rest of these are untested. // Union types void passUnion((object or long) arg); void passUnionWithNullable((object? or long) arg); From 7879480d35f58115c8b83831f5caba178b2f6336 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 3 Apr 2013 22:22:16 -0400 Subject: [PATCH 40/73] Bug 856819 part 1. Refactor the dependency-sorting we do for dictionaries so that we can also use it for JS-implemented interfaces. r=mccr8 --- dom/bindings/Codegen.py | 61 ++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 22 deletions(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index b7e1b3392417..686224920bf8 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -7126,6 +7126,37 @@ class CGRegisterProtos(CGAbstractMethod): def definition_body(self): return self._defineMacro() + self._registerProtos() + self._undefineMacro() +def dependencySortObjects(objects, dependencyGetter, nameGetter): + """ + Sort IDL objects with dependencies on each other such that if A + depends on B then B will come before A. This is needed for + declaring C++ classes in the right order, for example. Objects + that have no dependencies are just sorted by name. + + objects should be something that can produce a set of objects + (e.g. a set, iterator, list, etc). + + dependencyGetter is something that, given an object, should return + the set of objects it depends on. + """ + # XXXbz this will fail if we have two webidl files F1 and F2 such that F1 + # declares an object which depends on an object in F2, and F2 declares an + # object (possibly a different one!) that depends on an object in F1. The + # good news is that I expect this to never happen. + sortedObjects = [] + objects = set(objects) + while len(objects) != 0: + # Find the dictionaries that don't depend on anything else + # anymore and move them over. + toMove = [o for o in objects if + len(dependencyGetter(o) & objects) == 0] + if len(toMove) == 0: + raise TypeError("Loop in dependency graph\n" + + "\n".join(o.location for o in objects)) + objects = objects - set(toMove) + sortedObjects.extend(sorted(toMove, key=nameGetter)) + return sortedObjects + class CGBindingRoot(CGThing): """ Root codegen class for binding generation. Instantiate the class, and call @@ -7270,30 +7301,16 @@ class CGBindingRoot(CGThing): # here, because we have to generate these in order from least derived # to most derived so that class inheritance works out. We also have to # generate members before the dictionary that contains them. - # - # XXXbz this will fail if we have two webidl files A and B such that A - # declares a dictionary which inherits from a dictionary in B and B - # declares a dictionary (possibly a different one!) that inherits from a - # dictionary in A. The good news is that I expect this to never happen. - def sortDictionaries(dictionaries): - reSortedDictionaries = [] - dictionaries = set(dictionaries) - while len(dictionaries) != 0: - # Find the dictionaries that don't depend on anything else - # anymore and move them over. - toMove = [d for d in dictionaries if - len(CGDictionary.getDictionaryDependencies(d) & - dictionaries) == 0] - if len(toMove) == 0: - raise TypeError("Loop in dictionary dependency graph") - dictionaries = dictionaries - set(toMove) - reSortedDictionaries.extend(toMove) - return reSortedDictionaries - cgthings.extend([CGDictionary(d, config.getDescriptorProvider(True)) - for d in sortDictionaries(workerDictionaries)]) + for d in + dependencySortObjects(workerDictionaries, + CGDictionary.getDictionaryDependencies, + lambda d: d.identifier.name)]) cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False)) - for d in sortDictionaries(mainDictionaries)]) + for d in + dependencySortObjects(mainDictionaries, + CGDictionary.getDictionaryDependencies, + lambda d: d.identifier.name)]) # Do codegen for all the callbacks. Only do non-worker codegen for now, # since we don't have a sane setup yet for invoking callbacks in workers From 33622efb8bfe1f803dc1e2323f40770795f5438e Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 3 Apr 2013 22:22:16 -0400 Subject: [PATCH 41/73] Bug 856819 part 2. Sort js-implemented interfaces in a given file so that the declarations in C++ come in the right order. r=mccr8 --- dom/bindings/Bindings.conf | 10 ++++++++++ dom/bindings/Codegen.py | 7 ++++++- dom/bindings/test/TestJSImplInheritanceGen.webidl | 12 ++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 1300e069b6d8..d545f0e1b915 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -1228,6 +1228,16 @@ DOMInterfaces = { 'register': False }, +'TestJSImplInterface5' : { + 'headerFile': 'TestJSImplGenBinding.h', + 'register': False + }, + +'TestJSImplInterface6' : { + 'headerFile': 'TestJSImplGenBinding.h', + 'register': False + }, + 'TestExternalInterface' : { 'nativeType': 'mozilla::dom::TestExternalInterface', 'headerFile': 'TestBindingHeader.h', diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 686224920bf8..78f3fd0c9538 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -7325,7 +7325,12 @@ class CGBindingRoot(CGThing): cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors]) # Do codegen for JS implemented classes - for x in jsImplemented: + def getParentDescriptor(desc): + if not desc.interface.parent: + return set() + return { desc.getDescriptor(desc.interface.parent.identifier.name) } + for x in dependencySortObjects(jsImplemented, getParentDescriptor, + lambda d: d.interface.identifier.name): cgthings.append(CGCallbackInterface(x)) cgthings.append(CGJSImplClass(x)) diff --git a/dom/bindings/test/TestJSImplInheritanceGen.webidl b/dom/bindings/test/TestJSImplInheritanceGen.webidl index 57e618a5d87f..e62dbd10be6e 100644 --- a/dom/bindings/test/TestJSImplInheritanceGen.webidl +++ b/dom/bindings/test/TestJSImplInheritanceGen.webidl @@ -12,6 +12,18 @@ interface TestJSImplInterface2 : TestCImplementedInterface { interface TestJSImplInterface3 : TestCImplementedInterface2 { }; +// Important: TestJSImplInterface5 needs to come before TestJSImplInterface6 in +// this file to test what it's trying to test. +[Constructor, JSImplementation="@mozilla.org/test-js-impl-interface5;1"] +interface TestJSImplInterface5 : TestJSImplInterface6 { +}; + +// Important: TestJSImplInterface6 needs to come after TestJSImplInterface3 in +// this file to test what it's trying to test. +[Constructor, JSImplementation="@mozilla.org/test-js-impl-interface6;1"] +interface TestJSImplInterface6 : TestJSImplInterface3 { +}; + [Constructor, JSImplementation="@mozilla.org/test-js-impl-interface4;1"] interface TestJSImplInterface4 : EventTarget { }; From 381b988a3b1be0e026b9739646819755616bb51a Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 3 Apr 2013 22:22:16 -0400 Subject: [PATCH 42/73] Bug 855700. Make returning an nsIVariant from a WebIDL interface work. r=smaug --- dom/bindings/BindingUtils.cpp | 17 +++++++++++++++++ dom/bindings/BindingUtils.h | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index 2ff0228406e7..e633f7217965 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -640,6 +640,23 @@ XPCOMObjectToJsval(JSContext* cx, JSObject* scope, xpcObjectHelper &helper, return true; } +bool +VariantToJsval(JSContext* aCx, JSObject* aScope, nsIVariant* aVariant, + JS::Value* aRetval) +{ + nsresult rv; + XPCLazyCallContext lccx(JS_CALLER, aCx, aScope); + if (!XPCVariant::VariantDataToJS(lccx, aVariant, &rv, aRetval)) { + // Does it throw? Who knows + if (!JS_IsExceptionPending(aCx)) { + Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED); + } + return false; + } + + return true; +} + JSBool QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp) { diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 42fb5f494a16..3e5e5c3478dd 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -834,6 +834,11 @@ bool XPCOMObjectToJsval(JSContext* cx, JSObject* scope, xpcObjectHelper &helper, const nsIID* iid, bool allowNativeWrapper, JS::Value* rval); +// Special-cased wrapping for variants +bool +VariantToJsval(JSContext* aCx, JSObject* aScope, nsIVariant* aVariant, + JS::Value* aRetval); + // Wrap an object "p" which is not using WebIDL bindings yet. This _will_ // actually work on WebIDL binding objects that are wrappercached, but will be // much slower than WrapNewBindingObject. "cache" must either be null or be the @@ -849,6 +854,18 @@ WrapObject(JSContext* cx, JSObject* scope, T* p, nsWrapperCache* cache, return XPCOMObjectToJsval(cx, scope, helper, iid, true, vp); } +// A specialization of the above for nsIVariant, because that needs to +// do something different. +template<> +inline bool +WrapObject(JSContext* cx, JSObject* scope, nsIVariant* p, + nsWrapperCache* cache, const nsIID* iid, JS::Value* vp) +{ + MOZ_ASSERT(iid); + MOZ_ASSERT(iid->Equals(NS_GET_IID(nsIVariant))); + return VariantToJsval(cx, scope, p, vp); +} + // Wrap an object "p" which is not using WebIDL bindings yet. Just like the // variant that takes an nsWrapperCache above, but will try to auto-derive the // nsWrapperCache* from "p". From 088e490a3248a4dbe0bc702b3d23e69817fc3523 Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Thu, 4 Apr 2013 15:59:24 +1300 Subject: [PATCH 43/73] Bug 855233 - Recreate mask layers if the ContainerParameters offset changes. r=nrc --- layout/base/FrameLayerBuilder.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index a9bcecaa5c87..b869086daa72 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -673,7 +673,8 @@ struct MaskLayerUserData : public LayerUserData { return mRoundedClipRects == aOther.mRoundedClipRects && mScaleX == aOther.mScaleX && - mScaleY == aOther.mScaleY; + mScaleY == aOther.mScaleY && + mOffset == aOther.mOffset; } nsRefPtr mImageKey; @@ -682,6 +683,8 @@ struct MaskLayerUserData : public LayerUserData nsTArray mRoundedClipRects; // scale from the masked layer which is applied to the mask float mScaleX, mScaleY; + // The ContainerParameters offset which is applied to the mask's transform. + nsIntPoint mOffset; }; /** @@ -3721,6 +3724,7 @@ ContainerState::SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aCl } newData.mScaleX = mParameters.mXScale; newData.mScaleY = mParameters.mYScale; + newData.mOffset = mParameters.mOffset; if (*userData == newData) { aLayer->SetMaskLayer(maskLayer); From 439d6d72306dd76711078dbc73af99669575a4ec Mon Sep 17 00:00:00 2001 From: Geoff Brown Date: Wed, 3 Apr 2013 21:11:26 -0600 Subject: [PATCH 44/73] Bug 855146 - Enable SpecialPowers in robocop tests; r=jmaher --- testing/mochitest/runtestsremote.py | 1 - 1 file changed, 1 deletion(-) diff --git a/testing/mochitest/runtestsremote.py b/testing/mochitest/runtestsremote.py index 03172dec7f8f..1ad8707d39c0 100644 --- a/testing/mochitest/runtestsremote.py +++ b/testing/mochitest/runtestsremote.py @@ -312,7 +312,6 @@ class MochiRemote(Mochitest): shutil.rmtree(os.path.join(options.profilePath, 'extensions', 'staged', 'mochikit@mozilla.org')) shutil.rmtree(os.path.join(options.profilePath, 'extensions', 'staged', 'worker-test@mozilla.org')) shutil.rmtree(os.path.join(options.profilePath, 'extensions', 'staged', 'workerbootstrap-test@mozilla.org')) - shutil.rmtree(os.path.join(options.profilePath, 'extensions', 'staged', 'special-powers@mozilla.org')) os.remove(os.path.join(options.profilePath, 'userChrome.css')) if os.path.exists(os.path.join(options.profilePath, 'tests.jar')): os.remove(os.path.join(options.profilePath, 'tests.jar')) From 22387622763a4874eeb4fe76d9dcb9556ae70e67 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Thu, 4 Apr 2013 02:08:16 -0400 Subject: [PATCH 45/73] Bug 856155 - End IME composition before sending touch events to content. r=jchen --- widget/android/nsWindow.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index b967ccc8ce5e..bc50b95e8d50 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1188,6 +1188,10 @@ bool nsWindow::OnMultitouchEvent(AndroidGeckoEvent *ae) { nsRefPtr kungFuDeathGrip(this); + // End any composition in progress in case the touch event listener + // modifies the input field value (see bug 856155) + RemoveIMEComposition(); + // This is set to true once we have called SetPreventPanning() exactly // once for a given sequence of touch events. It is reset on the start // of the next sequence. From 8e012305e4dc494340f55619c11ba358854ad3b1 Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Thu, 4 Apr 2013 11:24:40 +0200 Subject: [PATCH 46/73] Bug 820170 - merge scope members on nsDocument. r=smaug --- content/base/public/nsIDocument.h | 6 ++++-- content/base/src/nsDocument.cpp | 10 +++++++--- content/base/src/nsDocument.h | 5 ----- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index 07c797788a05..49e9363844db 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -110,8 +110,8 @@ typedef CallbackObjectHolder NodeFilterHolder; } // namespace mozilla #define NS_IDOCUMENT_IID \ -{ 0x699e0649, 0x55f2, 0x47f1, \ - { 0x93, 0x38, 0xcd, 0x67, 0xf3, 0x2b, 0x04, 0xe9 } } +{ 0x2adedf2, 0x8d85, 0x4a38, \ + { 0xb6, 0x38, 0x91, 0xf4, 0xd2, 0xa4, 0x9b, 0x36 } } // Flag for AddStyleSheet(). #define NS_STYLESHEET_FROM_CATALOG (1 << 0) @@ -2312,6 +2312,8 @@ protected: // document was created entirely in memory bool mHaveInputEncoding; + bool mHasHadDefaultView; + // The document's script global object, the object from which the // document can get its script context and scope. This is the // *inner* window object. diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 6d6c5e819833..2040466894e0 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -4089,8 +4089,8 @@ nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject) mScriptGlobalObject = aScriptGlobalObject; if (aScriptGlobalObject) { - mScriptObject = nullptr; mHasHadScriptHandlingObject = true; + mHasHadDefaultView = true; // Go back to using the docshell for the layout history state mLayoutHistoryState = nullptr; mScopeObject = do_GetWeakReference(aScriptGlobalObject); @@ -4144,9 +4144,12 @@ nsDocument::GetScriptHandlingObjectInternal() const { NS_ASSERTION(!mScriptGlobalObject, "Do not call this when mScriptGlobalObject is set!"); + if (mHasHadDefaultView) { + return nullptr; + } nsCOMPtr scriptHandlingObject = - do_QueryReferent(mScriptObject); + do_QueryReferent(mScopeObject); nsCOMPtr win = do_QueryInterface(scriptHandlingObject); if (win) { NS_ASSERTION(win->IsInnerWindow(), "Should have inner window here!"); @@ -4166,9 +4169,10 @@ nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject) "Wrong script object!"); nsCOMPtr win = do_QueryInterface(aScriptObject); NS_ASSERTION(!win || win->IsInnerWindow(), "Should have inner window here!"); - mScopeObject = mScriptObject = do_GetWeakReference(aScriptObject); + mScopeObject = do_GetWeakReference(aScriptObject); if (aScriptObject) { mHasHadScriptHandlingObject = true; + mHasHadDefaultView = false; } } diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index e70d7c6d08db..8c3057479761 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -1180,11 +1180,6 @@ protected: // Array of observers nsTObserverArray mObservers; - // If document is created for example using - // document.implementation.createDocument(...), mScriptObject points to - // the script global object of the original document. - nsWeakPtr mScriptObject; - // Weak reference to the scope object (aka the script global object) // that, unlike mScriptGlobalObject, is never unset once set. This // is a weak reference to avoid leaks due to circular references. From 2662f735ebaeaa913842c071a06b4eaba43e25b7 Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Thu, 4 Apr 2013 11:27:06 +0200 Subject: [PATCH 47/73] Bug 820170 - nsIGlobalObject. r=bholley --- .../base/src/nsInProcessTabChildGlobal.cpp | 1 + content/base/src/nsInProcessTabChildGlobal.h | 14 ++++++++- content/xbl/src/nsXBLDocumentInfo.cpp | 8 +++-- .../document/src/nsXULPrototypeDocument.cpp | 8 +++-- dom/base/Makefile.in | 1 + dom/base/nsGlobalWindow.cpp | 1 + dom/base/nsGlobalWindow.h | 5 ++-- dom/base/nsIGlobalObject.h | 29 +++++++++++++++++++ dom/base/nsIScriptGlobalObject.h | 11 +++---- 9 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 dom/base/nsIGlobalObject.h diff --git a/content/base/src/nsInProcessTabChildGlobal.cpp b/content/base/src/nsInProcessTabChildGlobal.cpp index 3ec573568512..df1d185982f2 100644 --- a/content/base/src/nsInProcessTabChildGlobal.cpp +++ b/content/base/src/nsInProcessTabChildGlobal.cpp @@ -167,6 +167,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsInProcessTabChildGlobal) NS_INTERFACE_MAP_ENTRY(nsIInProcessContentFrameMessageManager) NS_INTERFACE_MAP_ENTRY(nsIScriptContextPrincipal) NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) + NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ContentFrameMessageManager) NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper) diff --git a/content/base/src/nsInProcessTabChildGlobal.h b/content/base/src/nsInProcessTabChildGlobal.h index 815e4d528776..d44c9d043749 100644 --- a/content/base/src/nsInProcessTabChildGlobal.h +++ b/content/base/src/nsInProcessTabChildGlobal.h @@ -19,12 +19,13 @@ #include "nsIDOMElement.h" #include "nsCOMArray.h" #include "nsThreadUtils.h" +#include "nsIGlobalObject.h" class nsInProcessTabChildGlobal : public nsDOMEventTargetHelper, public nsFrameScriptExecutor, public nsIInProcessContentFrameMessageManager, - public nsIScriptObjectPrincipal, public nsIScriptContextPrincipal, + public nsIGlobalObject, public mozilla::dom::ipc::MessageManagerCallback { public: @@ -113,6 +114,17 @@ public: } void DelayedDisconnect(); + + virtual JSObject* GetGlobalJSObject() { + if (!mGlobal) { + return nullptr; + } + + JSObject* global; + mGlobal->GetJSObject(&global); + + return global; + } protected: nsresult Init(); nsresult InitTabChildGlobal(); diff --git a/content/xbl/src/nsXBLDocumentInfo.cpp b/content/xbl/src/nsXBLDocumentInfo.cpp index 59f8d9b046f2..dedae03bf455 100644 --- a/content/xbl/src/nsXBLDocumentInfo.cpp +++ b/content/xbl/src/nsXBLDocumentInfo.cpp @@ -41,14 +41,16 @@ static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CI // An XBLDocumentInfo object has a special context associated with it which we can use to pre-compile // properties and methods of XBL bindings against. -class nsXBLDocGlobalObject : public nsIScriptGlobalObject, - public nsIScriptObjectPrincipal +class nsXBLDocGlobalObject : public nsIScriptGlobalObject { public: nsXBLDocGlobalObject(nsXBLDocumentInfo *aGlobalObjectOwner); // nsISupports interface NS_DECL_CYCLE_COLLECTING_ISUPPORTS + + // nsIGlobalObject methods + virtual JSObject *GetGlobalJSObject(); // nsIScriptGlobalObject methods virtual nsresult EnsureScriptEnvironment(); @@ -58,7 +60,6 @@ public: } virtual nsIScriptContext *GetContext(); - virtual JSObject *GetGlobalJSObject(); virtual void OnFinalize(JSObject* aObject); virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts); @@ -199,6 +200,7 @@ NS_IMPL_CYCLE_COLLECTION_1(nsXBLDocGlobalObject, mScriptContext) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXBLDocGlobalObject) NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject) NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) + NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptGlobalObject) NS_INTERFACE_MAP_END diff --git a/content/xul/document/src/nsXULPrototypeDocument.cpp b/content/xul/document/src/nsXULPrototypeDocument.cpp index d48532bbed9d..6f5cfc8cda2d 100644 --- a/content/xul/document/src/nsXULPrototypeDocument.cpp +++ b/content/xul/document/src/nsXULPrototypeDocument.cpp @@ -40,8 +40,7 @@ static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID); -class nsXULPDGlobalObject : public nsIScriptGlobalObject, - public nsIScriptObjectPrincipal +class nsXULPDGlobalObject : public nsIScriptGlobalObject { public: nsXULPDGlobalObject(nsXULPrototypeDocument* owner); @@ -49,11 +48,13 @@ public: // nsISupports interface NS_DECL_CYCLE_COLLECTING_ISUPPORTS + // nsIGlobalJSObjectHolder methods + virtual JSObject* GetGlobalJSObject(); + // nsIScriptGlobalObject methods virtual void OnFinalize(JSObject* aObject); virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts); - virtual JSObject* GetGlobalJSObject(); virtual nsresult EnsureScriptEnvironment(); virtual nsIScriptContext *GetScriptContext(); @@ -729,6 +730,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULPDGlobalObject) NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject) NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) + NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptGlobalObject) NS_INTERFACE_MAP_END diff --git a/dom/base/Makefile.in b/dom/base/Makefile.in index fb02692ba806..b9b862b96b8c 100644 --- a/dom/base/Makefile.in +++ b/dom/base/Makefile.in @@ -40,6 +40,7 @@ EXPORTS = \ nsDOMString.h \ nsIDOMClassInfo.h \ nsIDOMScriptObjectFactory.h \ + nsIGlobalObject.h \ nsIJSEventListener.h \ nsIJSNativeInitializer.h \ nsIScriptContext.h \ diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 5f150173e402..9ae0e7e655db 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1506,6 +1506,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindow) "nsIDOMWindowInternalWarning"); } } else + NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObject) NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget) diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 1034202c5e3e..1516c5f81c5e 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -260,7 +260,6 @@ class nsGlobalWindow : public mozilla::dom::EventTarget, public nsPIDOMWindow, public nsIScriptGlobalObject, public nsIDOMJSWindow, - public nsIScriptObjectPrincipal, public nsIDOMStorageIndexedDB, public nsSupportsWeakReference, public nsIInterfaceRequestor, @@ -297,9 +296,11 @@ public: return EnsureInnerWindow() ? GetWrapper() : nullptr; } + // nsIGlobalJSObjectHolder + virtual JSObject *GetGlobalJSObject(); + // nsIScriptGlobalObject virtual nsIScriptContext *GetContext(); - virtual JSObject *GetGlobalJSObject(); JSObject *FastGetGlobalJSObject() { return mJSObject; diff --git a/dom/base/nsIGlobalObject.h b/dom/base/nsIGlobalObject.h new file mode 100644 index 000000000000..f9ec31995ee7 --- /dev/null +++ b/dom/base/nsIGlobalObject.h @@ -0,0 +1,29 @@ +/* -*- Mode: C++; 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/. */ + +#ifndef nsIGlobalObject_h__ +#define nsIGlobalObject_h__ + +#include "nsISupports.h" +#include "nsIScriptObjectPrincipal.h" + +class JSObject; + +#define NS_IGLOBALOBJECT_IID \ +{ 0x8503e9a9, 0x530, 0x4b26, \ +{ 0xae, 0x24, 0x18, 0xca, 0x38, 0xe5, 0xed, 0x17 } } + +class nsIGlobalObject : public nsIScriptObjectPrincipal +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IGLOBALOBJECT_IID) + + virtual JSObject* GetGlobalJSObject() = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIGlobalObject, + NS_IGLOBALOBJECT_IID) + +#endif // nsIGlobalObject_h__ diff --git a/dom/base/nsIScriptGlobalObject.h b/dom/base/nsIScriptGlobalObject.h index b5682f8d360b..864cbc371fb5 100644 --- a/dom/base/nsIScriptGlobalObject.h +++ b/dom/base/nsIScriptGlobalObject.h @@ -10,6 +10,7 @@ #include "nsISupports.h" #include "nsEvent.h" #include "nsIProgrammingLanguage.h" +#include "nsIGlobalObject.h" class nsIScriptContext; class nsIDOMEvent; @@ -60,15 +61,17 @@ NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal, #define NS_ISCRIPTGLOBALOBJECT_IID \ -{ 0x92569431, 0x6e6e, 0x408a, \ - { 0xa8, 0x8c, 0x45, 0x28, 0x5c, 0x1c, 0x85, 0x73 } } +{ 0xde24b30a, 0x12c6, 0x4e5f, \ + { 0xa8, 0x5e, 0x90, 0xcd, 0xfb, 0x6c, 0x54, 0x51 } } /** * The global object which keeps a script context for each supported script * language. This often used to store per-window global state. + * This is a heavyweight interface implemented only by DOM globals, and + * it might go away some time in the future. */ -class nsIScriptGlobalObject : public nsISupports +class nsIScriptGlobalObject : public nsIGlobalObject { public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCRIPTGLOBALOBJECT_IID) @@ -87,8 +90,6 @@ public: * Get a script context (WITHOUT added reference) for the specified language. */ virtual nsIScriptContext *GetScriptContext() = 0; - - virtual JSObject* GetGlobalJSObject() = 0; nsIScriptContext* GetContext() { return GetScriptContext(); From cfbb3ca535ebd33640d52b5f95dbcae7209b9656 Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Thu, 4 Apr 2013 11:27:36 +0200 Subject: [PATCH 48/73] Bug 820170 - SandboxPrivate. r=bholley --- js/xpconnect/public/Makefile.in | 1 + js/xpconnect/public/SandboxPrivate.h | 45 ++++++++++++++++++++++++++ js/xpconnect/src/XPCComponents.cpp | 39 +++++++++------------- js/xpconnect/src/XPCJSContextStack.cpp | 12 +++---- js/xpconnect/src/xpcprivate.h | 28 ++-------------- 5 files changed, 69 insertions(+), 56 deletions(-) create mode 100644 js/xpconnect/public/SandboxPrivate.h diff --git a/js/xpconnect/public/Makefile.in b/js/xpconnect/public/Makefile.in index 29b20538e865..07398dccbea5 100644 --- a/js/xpconnect/public/Makefile.in +++ b/js/xpconnect/public/Makefile.in @@ -15,6 +15,7 @@ EXPORTS = \ xpc_map_end.h \ nsAutoJSValHolder.h \ nsTArrayHelpers.h \ + SandboxPrivate.h \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/js/xpconnect/public/SandboxPrivate.h b/js/xpconnect/public/SandboxPrivate.h new file mode 100644 index 000000000000..69c668bcd845 --- /dev/null +++ b/js/xpconnect/public/SandboxPrivate.h @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __SANDBOXPRIVATE_H__ +#define __SANDBOXPRIVATE_H__ + +#include "nsIGlobalObject.h" +#include "nsIPrincipal.h" + +// This interface is public only because it is used in jsd. +// Once jsd is gone this file should be moved back to xpconnect/src. + +class SandboxPrivate : public nsIGlobalObject +{ +public: + SandboxPrivate(nsIPrincipal *principal, JSObject *global) + : mPrincipal(principal) + , mGlobalJSObject(global) + { + } + virtual ~SandboxPrivate() { } + + NS_DECL_ISUPPORTS + + nsIPrincipal *GetPrincipal() + { + return mPrincipal; + } + + JSObject *GetGlobalJSObject() + { + return mGlobalJSObject; + } + + void ForgetGlobalObject() + { + mGlobalJSObject = NULL; + } +private: + nsCOMPtr mPrincipal; + JSObject *mGlobalJSObject; +}; + +#endif // __SANDBOXPRIVATE_H__ \ No newline at end of file diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index 5a5e1daffbd4..209654aec9ed 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -2837,13 +2837,7 @@ nsXPCComponents_Utils::ReportError(const JS::Value &error, JSContext *cx) #include "nsNetUtil.h" const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID; -NS_IMPL_THREADSAFE_ISUPPORTS1(PrincipalHolder, nsIScriptObjectPrincipal) - -nsIPrincipal * -PrincipalHolder::GetPrincipal() -{ - return mHoldee; -} +NS_IMPL_THREADSAFE_ISUPPORTS2(SandboxPrivate, nsIScriptObjectPrincipal, nsIGlobalObject) static JSBool SandboxDump(JSContext *cx, unsigned argc, jsval *vp) @@ -2987,7 +2981,9 @@ static void sandbox_finalize(JSFreeOp *fop, JSObject *obj) { nsIScriptObjectPrincipal *sop = - (nsIScriptObjectPrincipal *)xpc_GetJSPrivate(obj); + static_cast(xpc_GetJSPrivate(obj)); + MOZ_ASSERT(sop); + static_cast(sop)->ForgetGlobalObject(); NS_IF_RELEASE(sop); DestroyProtoAndIfaceCache(obj); } @@ -3286,12 +3282,12 @@ xpc_CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, Sandbo if (NS_FAILED(rv)) return NS_ERROR_XPC_UNEXPECTED; - nsCOMPtr sop(do_QueryInterface(prinOrSop)); - - if (!sop) { - nsCOMPtr principal(do_QueryInterface(prinOrSop)); - - if (!principal) { + nsCOMPtr principal = do_QueryInterface(prinOrSop); + if (!principal) { + nsCOMPtr sop = do_QueryInterface(prinOrSop); + if (sop) { + principal = sop->GetPrincipal(); + } else { principal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); NS_ASSERTION(NS_FAILED(rv) || principal, "Bad return from do_CreateInstance"); @@ -3304,14 +3300,8 @@ xpc_CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, Sandbo return rv; } } - - sop = new PrincipalHolder(principal); - if (!sop) - return NS_ERROR_OUT_OF_MEMORY; + MOZ_ASSERT(principal); } - - nsIPrincipal *principal = sop->GetPrincipal(); - JSObject *sandbox; JS::ZoneSpecifier zoneSpec = options.sameZoneAs @@ -3369,8 +3359,11 @@ xpc_CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, Sandbo return NS_ERROR_XPC_UNEXPECTED; } - // Pass on ownership of sop to |sandbox|. - JS_SetPrivate(sandbox, sop.forget().get()); + nsCOMPtr sbp = + new SandboxPrivate(principal, sandbox); + + // Pass on ownership of sbp to |sandbox|. + JS_SetPrivate(sandbox, sbp.forget().get()); XPCCallContext ccx(NATIVE_CALLER, cx); if (!ccx.IsValid()) diff --git a/js/xpconnect/src/XPCJSContextStack.cpp b/js/xpconnect/src/XPCJSContextStack.cpp index ddec734d0463..3d1470c8d72d 100644 --- a/js/xpconnect/src/XPCJSContextStack.cpp +++ b/js/xpconnect/src/XPCJSContextStack.cpp @@ -124,8 +124,9 @@ SafeGlobalResolve(JSContext *cx, JSHandleObject obj, JSHandleId id) static void SafeFinalize(JSFreeOp *fop, JSObject* obj) { - nsIScriptObjectPrincipal* sop = - static_cast(xpc_GetJSPrivate(obj)); + SandboxPrivate* sop = + static_cast(xpc_GetJSPrivate(obj)); + sop->ForgetGlobalObject(); NS_IF_RELEASE(sop); DestroyProtoAndIfaceCache(obj); } @@ -156,8 +157,6 @@ XPCJSContextStack::GetSafeJSContext() if (NS_FAILED(rv)) return NULL; - nsCOMPtr sop = new PrincipalHolder(principal); - nsRefPtr xpc = nsXPConnect::GetXPConnect(); if (!xpc) return NULL; @@ -190,9 +189,8 @@ XPCJSContextStack::GetSafeJSContext() // Note: make sure to set the private before calling // InitClasses - nsIScriptObjectPrincipal* priv = nullptr; - sop.swap(priv); - JS_SetPrivate(glob, priv); + nsCOMPtr sop = new SandboxPrivate(principal, glob); + JS_SetPrivate(glob, sop.forget().get()); } // After this point either glob is null and the diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 278b01418c9a..98affb15145d 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -178,6 +178,8 @@ #include "xpcObjectHelper.h" #include "nsIThreadInternal.h" +#include "SandboxPrivate.h" + #ifdef XP_WIN // Nasty MS defines #ifdef GetClassInfo @@ -4164,32 +4166,6 @@ public: static void GetTraceName(JSTracer* trc, char *buf, size_t bufsize); }; -/***************************************************************************/ - -#define PRINCIPALHOLDER_IID \ -{0xbf109f49, 0xf94a, 0x43d8, {0x93, 0xdb, 0xe4, 0x66, 0x49, 0xc5, 0xd9, 0x7d}} - -class PrincipalHolder : public nsIScriptObjectPrincipal -{ -public: - NS_DECLARE_STATIC_IID_ACCESSOR(PRINCIPALHOLDER_IID) - - PrincipalHolder(nsIPrincipal *holdee) - : mHoldee(holdee) - { - } - virtual ~PrincipalHolder() { } - - NS_DECL_ISUPPORTS - - nsIPrincipal *GetPrincipal(); - -private: - nsCOMPtr mHoldee; -}; - -NS_DEFINE_STATIC_IID_ACCESSOR(PrincipalHolder, PRINCIPALHOLDER_IID) - /***************************************************************************/ // Utilities From 716156d923bcd9e179a8330cedb6b9bcc1ded0cd Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Thu, 4 Apr 2013 11:27:37 +0200 Subject: [PATCH 49/73] Bug 820170 - SandboxPrivate for jsd global. r=bholley --- js/jsd/jsd_high.cpp | 19 ++++++++++++++++--- js/jsd/jsd_xpc.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/js/jsd/jsd_high.cpp b/js/jsd/jsd_high.cpp index 3ed51fea88d9..3a7bea446e36 100644 --- a/js/jsd/jsd_high.cpp +++ b/js/jsd/jsd_high.cpp @@ -37,10 +37,23 @@ void JSD_ASSERT_VALID_CONTEXT(JSDContext* jsdc) } #endif +/***************************************************************************/ +/* xpconnect related utility functions implemented in jsd_xpc.cpp */ + +extern void +global_finalize(JSFreeOp* fop, JSObject* obj); + +extern JSObject* +CreateJSDGlobal(JSContext *cx, JSClass *clasp); + +/***************************************************************************/ + + static JSClass global_class = { - "JSDGlobal", JSCLASS_GLOBAL_FLAGS, + "JSDGlobal", JSCLASS_GLOBAL_FLAGS | + JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, global_finalize }; static JSBool @@ -108,7 +121,7 @@ _newJSDContext(JSRuntime* jsrt, JS_BeginRequest(jsdc->dumbContext); JS_SetOptions(jsdc->dumbContext, JS_GetOptions(jsdc->dumbContext)); - jsdc->glob = JS_NewGlobalObject(jsdc->dumbContext, &global_class, NULL); + jsdc->glob = CreateJSDGlobal(jsdc->dumbContext, &global_class); if( ! jsdc->glob ) goto label_newJSDContext_failure; diff --git a/js/jsd/jsd_xpc.cpp b/js/jsd/jsd_xpc.cpp index 882f62cbb688..4f7c849a0d49 100644 --- a/js/jsd/jsd_xpc.cpp +++ b/js/jsd/jsd_xpc.cpp @@ -31,6 +31,8 @@ /* XXX DOM dependency */ #include "nsIScriptContext.h" #include "nsIJSContextStack.h" +#include "SandboxPrivate.h" +#include "nsJSPrincipals.h" /* * defining CAUTIOUS_SCRIPTHOOK makes jsds disable GC while calling out to the @@ -3434,6 +3436,36 @@ static const mozilla::Module kJSDModule = { NSMODULE_DEFN(JavaScript_Debugger) = &kJSDModule; +void +global_finalize(JSFreeOp *aFop, JSObject *aObj) +{ + nsIScriptObjectPrincipal *sop = + static_cast(js::GetObjectPrivate(aObj)); + MOZ_ASSERT(sop); + static_cast(sop)->ForgetGlobalObject(); + NS_IF_RELEASE(sop); +} + +JSObject * +CreateJSDGlobal(JSContext *aCx, JSClass *aClasp) +{ + nsresult rv; + nsCOMPtr nullPrin = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); + NS_ENSURE_SUCCESS(rv, nullptr); + + JSPrincipals *jsPrin = nsJSPrincipals::get(nullPrin); + JSObject *global = JS_NewGlobalObject(aCx, aClasp, jsPrin); + NS_ENSURE_TRUE(global, nullptr); + + // We have created a new global let's attach a private to it + // that implements nsIGlobalObject. + nsCOMPtr sbp = + new SandboxPrivate(nullPrin, global); + JS_SetPrivate(global, sbp.forget().get()); + + return global; +} + /******************************************************************************** ******************************************************************************** * graveyard From 2edb19076f3061d5b4d03043a8d4baca987ebcab Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Thu, 4 Apr 2013 11:27:38 +0200 Subject: [PATCH 50/73] Bug 820170 - Turning BackstagePass into non-singleton. r=bholley --- content/base/public/nsContentUtils.h | 5 ++ content/base/src/nsContentUtils.cpp | 9 ++++ ipc/testshell/XPCShellEnvironment.cpp | 12 +++-- js/xpconnect/idl/nsIJSRuntimeService.idl | 5 +- js/xpconnect/loader/mozJSComponentLoader.cpp | 9 ++-- js/xpconnect/shell/xpcshell.cpp | 12 +++-- js/xpconnect/src/BackstagePass.h | 52 ++++++++++++++++++++ js/xpconnect/src/Makefile.in | 4 +- js/xpconnect/src/XPCRuntimeService.cpp | 24 +++++++++ js/xpconnect/src/nsXPConnect.cpp | 21 -------- js/xpconnect/src/xpcprivate.h | 28 +---------- 11 files changed, 118 insertions(+), 63 deletions(-) create mode 100644 js/xpconnect/src/BackstagePass.h diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index 688bc9e4584d..6869197caaec 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -1355,6 +1355,11 @@ public: */ static bool IsSystemPrincipal(nsIPrincipal* aPrincipal); + /** + * Gets the system principal from the security manager. + */ + static nsIPrincipal* GetSystemPrincipal(); + /** * *aResourcePrincipal is a principal describing who may access the contents * of a resource. The resource can only be consumed by a principal that diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 381953860896..7d9685afb6c3 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -4698,6 +4698,15 @@ nsContentUtils::IsSystemPrincipal(nsIPrincipal* aPrincipal) return NS_SUCCEEDED(rv) && isSystem; } +nsIPrincipal* +nsContentUtils::GetSystemPrincipal() +{ + nsCOMPtr sysPrin; + nsresult rv = sSecurityManager->GetSystemPrincipal(getter_AddRefs(sysPrin)); + MOZ_ASSERT(NS_SUCCEEDED(rv) && sysPrin); + return sysPrin; +} + bool nsContentUtils::CombineResourcePrincipals(nsCOMPtr* aResourcePrincipal, nsIPrincipal* aExtraPrincipal) diff --git a/ipc/testshell/XPCShellEnvironment.cpp b/ipc/testshell/XPCShellEnvironment.cpp index 7936618ee2d7..da439930b6c0 100644 --- a/ipc/testshell/XPCShellEnvironment.cpp +++ b/ipc/testshell/XPCShellEnvironment.cpp @@ -39,6 +39,8 @@ #include "nsThreadUtils.h" #include "nsXULAppAPI.h" +#include "BackstagePass.h" + #include "TestShellChild.h" #include "TestShellParent.h" @@ -1035,15 +1037,16 @@ XPCShellEnvironment::Init() AutoContextPusher pusher(this); - nsCOMPtr backstagePass; - rv = rtsvc->GetBackstagePass(getter_AddRefs(backstagePass)); + nsRefPtr backstagePass; + rv = NS_NewBackstagePass(getter_AddRefs(backstagePass)); if (NS_FAILED(rv)) { - NS_ERROR("Failed to get backstage pass from rtsvc!"); + NS_ERROR("Failed to create backstage pass!"); return false; } nsCOMPtr holder; - rv = xpc->InitClassesWithNewWrappedGlobal(cx, backstagePass, + rv = xpc->InitClassesWithNewWrappedGlobal(cx, + static_cast(backstagePass), principal, 0, JS::SystemZone, getter_AddRefs(holder)); @@ -1059,6 +1062,7 @@ XPCShellEnvironment::Init() return false; } + backstagePass->SetGlobalObject(globalObj); { JSAutoRequest ar(cx); diff --git a/js/xpconnect/idl/nsIJSRuntimeService.idl b/js/xpconnect/idl/nsIJSRuntimeService.idl index 3ac9059a6400..a14919b60d6a 100644 --- a/js/xpconnect/idl/nsIJSRuntimeService.idl +++ b/js/xpconnect/idl/nsIJSRuntimeService.idl @@ -9,13 +9,12 @@ [ptr] native JSRuntime(JSRuntime); native JSGCCallback(JSGCCallback); -interface nsIXPCScriptable; +interface nsIBackstagePass; -[uuid(364bcec3-7034-4a4e-bff5-b3f796ca9771)] +[uuid(991c0749-a22e-476b-9428-a373df037455)] interface nsIJSRuntimeService : nsISupports { readonly attribute JSRuntime runtime; - readonly attribute nsIXPCScriptable backstagePass; /** * Register additional GC callback which will run after the diff --git a/js/xpconnect/loader/mozJSComponentLoader.cpp b/js/xpconnect/loader/mozJSComponentLoader.cpp index 7836f999b96a..ae12a414638a 100644 --- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -736,11 +736,12 @@ mozJSComponentLoader::PrepareObjectForLocation(JSCLContextHelper& aCx, NS_ENSURE_SUCCESS(rv, nullptr); if (!mLoaderGlobal) { - nsCOMPtr backstagePass; - rv = mRuntimeService->GetBackstagePass(getter_AddRefs(backstagePass)); + nsRefPtr backstagePass; + rv = NS_NewBackstagePass(getter_AddRefs(backstagePass)); NS_ENSURE_SUCCESS(rv, nullptr); - rv = xpc->InitClassesWithNewWrappedGlobal(aCx, backstagePass, + rv = xpc->InitClassesWithNewWrappedGlobal(aCx, + static_cast(backstagePass), mSystemPrincipal, 0, JS::SystemZone, @@ -751,6 +752,8 @@ mozJSComponentLoader::PrepareObjectForLocation(JSCLContextHelper& aCx, rv = holder->GetJSObject(&global); NS_ENSURE_SUCCESS(rv, nullptr); + backstagePass->SetGlobalObject(global); + JSAutoCompartment ac(aCx, global); if (!JS_DefineFunctions(aCx, global, gGlobalFun) || !JS_DefineProfilingFunctions(aCx, global)) { diff --git a/js/xpconnect/shell/xpcshell.cpp b/js/xpconnect/shell/xpcshell.cpp index 94dd4f5bbd2b..767156623aa8 100644 --- a/js/xpconnect/shell/xpcshell.cpp +++ b/js/xpconnect/shell/xpcshell.cpp @@ -45,6 +45,7 @@ #include "nsJSPrincipals.h" #include "xpcpublic.h" #include "nsXULAppAPI.h" +#include "BackstagePass.h" #ifdef XP_MACOSX #include "xpcshellMacUtils.h" #endif @@ -1882,16 +1883,17 @@ main(int argc, char **argv, char **envp) return 1; } - nsCOMPtr backstagePass; - nsresult rv = rtsvc->GetBackstagePass(getter_AddRefs(backstagePass)); + nsRefPtr backstagePass; + rv = NS_NewBackstagePass(getter_AddRefs(backstagePass)); if (NS_FAILED(rv)) { - fprintf(gErrFile, "+++ Failed to get backstage pass from rtsvc: %8x\n", + fprintf(gErrFile, "+++ Failed to create BackstagePass: %8x\n", static_cast(rv)); return 1; } nsCOMPtr holder; - rv = xpc->InitClassesWithNewWrappedGlobal(cx, backstagePass, + rv = xpc->InitClassesWithNewWrappedGlobal(cx, + static_cast(backstagePass), systemprincipal, 0, JS::SystemZone, @@ -1905,6 +1907,8 @@ main(int argc, char **argv, char **envp) return 1; } + backstagePass->SetGlobalObject(glob); + JS_BeginRequest(cx); { JSAutoCompartment ac(cx, glob); diff --git a/js/xpconnect/src/BackstagePass.h b/js/xpconnect/src/BackstagePass.h new file mode 100644 index 000000000000..ddb9a27cf752 --- /dev/null +++ b/js/xpconnect/src/BackstagePass.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; 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/. */ + +#ifndef BackstagePass_h__ +#define BackstagePass_h__ + +#include "nsISupports.h" +#include "nsIGlobalObject.h" + +class BackstagePass : public nsIGlobalObject, + public nsIXPCScriptable, + public nsIClassInfo +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIXPCSCRIPTABLE + NS_DECL_NSICLASSINFO + + virtual nsIPrincipal* GetPrincipal() { + return mPrincipal; + } + + virtual JSObject* GetGlobalJSObject() { + return mGlobal; + } + + virtual void ForgetGlobalObject() { + mGlobal = NULL; + } + + virtual void SetGlobalObject(JSObject* global) { + mGlobal = global; + } + + BackstagePass(nsIPrincipal *prin) : + mPrincipal(prin) + { + } + + virtual ~BackstagePass() { } + +private: + nsCOMPtr mPrincipal; + JSObject *mGlobal; +}; + +NS_EXPORT nsresult +NS_NewBackstagePass(BackstagePass** ret); + +#endif // BackstagePass_h__ diff --git a/js/xpconnect/src/Makefile.in b/js/xpconnect/src/Makefile.in index a61cbe4fab0d..8a52ef2a1cc8 100644 --- a/js/xpconnect/src/Makefile.in +++ b/js/xpconnect/src/Makefile.in @@ -18,7 +18,9 @@ EXPORTS = \ xpcObjectHelper.h \ xpcpublic.h \ XPCJSMemoryReporter.h \ - GeneratedEvents.h + GeneratedEvents.h \ + BackstagePass.h \ + $(NULL) CPPSRCS = \ nsScriptError.cpp \ diff --git a/js/xpconnect/src/XPCRuntimeService.cpp b/js/xpconnect/src/XPCRuntimeService.cpp index 41ced6aa3537..826ff8908749 100644 --- a/js/xpconnect/src/XPCRuntimeService.cpp +++ b/js/xpconnect/src/XPCRuntimeService.cpp @@ -7,9 +7,13 @@ #include "xpcprivate.h" #include "mozilla/dom/workers/Workers.h" +#include "nsIScriptSecurityManager.h" +#include "nsContentUtils.h" + using mozilla::dom::workers::ResolveWorkerClasses; NS_INTERFACE_MAP_BEGIN(BackstagePass) + NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) NS_INTERFACE_MAP_ENTRY(nsIClassInfo) NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) @@ -23,6 +27,8 @@ NS_IMPL_THREADSAFE_RELEASE(BackstagePass) #define XPC_MAP_CLASSNAME BackstagePass #define XPC_MAP_QUOTED_CLASSNAME "BackstagePass" #define XPC_MAP_WANT_NEWRESOLVE +#define XPC_MAP_WANT_FINALIZE + #define XPC_MAP_FLAGS nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY | \ nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY | \ nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY | \ @@ -152,3 +158,21 @@ BackstagePass::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) { return NS_ERROR_NOT_AVAILABLE; } + +NS_IMETHODIMP +BackstagePass::Finalize(nsIXPConnectWrappedNative *wrapper, JSFreeOp * fop, JSObject * obj) +{ + nsCOMPtr bsp(do_QueryWrappedNative(wrapper)); + MOZ_ASSERT(bsp); + static_cast(bsp.get())->ForgetGlobalObject(); + return NS_OK; +} + +nsresult +NS_NewBackstagePass(BackstagePass** ret) +{ + nsRefPtr bsp = new BackstagePass( + nsContentUtils::GetSystemPrincipal()); + bsp.forget(ret); + return NS_OK; +} diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 937754c17996..3069c5e85876 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -1983,27 +1983,6 @@ nsXPConnect::GetRuntime(JSRuntime **runtime) return NS_OK; } -/* attribute nsIXPCScriptable backstagePass; */ -NS_IMETHODIMP -nsXPConnect::GetBackstagePass(nsIXPCScriptable **bsp) -{ - if (!mBackstagePass) { - nsCOMPtr sysprin; - nsCOMPtr secman = - do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); - if (!secman) - return NS_ERROR_NOT_AVAILABLE; - if (NS_FAILED(secman->GetSystemPrincipal(getter_AddRefs(sysprin)))) - return NS_ERROR_NOT_AVAILABLE; - - mBackstagePass = new BackstagePass(sysprin); - if (!mBackstagePass) - return NS_ERROR_OUT_OF_MEMORY; - } - NS_ADDREF(*bsp = mBackstagePass); - return NS_OK; -} - /* [noscript, notxpcom] void registerGCCallback(in JSGCCallback func); */ NS_IMETHODIMP_(void) nsXPConnect::RegisterGCCallback(JSGCCallback func) diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 98affb15145d..72533b6554b7 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -179,6 +179,7 @@ #include "nsIThreadInternal.h" #include "SandboxPrivate.h" +#include "BackstagePass.h" #ifdef XP_WIN // Nasty MS defines @@ -579,8 +580,6 @@ private: // watch out for this, we'll do an unmatched |pop| on the context stack. uint16_t mEventDepth; - nsCOMPtr mBackstagePass; - static uint32_t gReportAllJSExceptions; static JSBool gDebugMode; static JSBool gDesiredDebugMode; @@ -3685,31 +3684,6 @@ private: }; /***************************************************************************/ -#include "nsIScriptSecurityManager.h" - -class BackstagePass : public nsIScriptObjectPrincipal, - public nsIXPCScriptable, - public nsIClassInfo -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIXPCSCRIPTABLE - NS_DECL_NSICLASSINFO - - virtual nsIPrincipal* GetPrincipal() { - return mPrincipal; - } - - BackstagePass(nsIPrincipal *prin) : - mPrincipal(prin) - { - } - - virtual ~BackstagePass() { } - -private: - nsCOMPtr mPrincipal; -}; // 'Components' object class nsXPCComponents : public nsIXPCComponents, From 20c0b1c5f676a2810fc32e8c43d25e9ffa598982 Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Thu, 4 Apr 2013 11:27:39 +0200 Subject: [PATCH 51/73] Bug 820170 - assert in XPCWrappedNativeScope::SetGlobal. r=bholley --- js/xpconnect/src/XPCWrappedNativeScope.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/xpconnect/src/XPCWrappedNativeScope.cpp b/js/xpconnect/src/XPCWrappedNativeScope.cpp index 2e471c25acb2..191f8dc73970 100644 --- a/js/xpconnect/src/XPCWrappedNativeScope.cpp +++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp @@ -129,7 +129,11 @@ XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext *cx, mIsXBLScope(false) { // add ourselves to the scopes list - { // scoped lock + { + MOZ_ASSERT(aGlobal); + MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS | + JSCLASS_HAS_PRIVATE)); + // scoped lock XPCAutoLock lock(XPCJSRuntime::Get()->GetMapLock()); #ifdef DEBUG From 499a22ed0b11e090b32bb1dd317d48657f9a103c Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Thu, 4 Apr 2013 11:27:40 +0200 Subject: [PATCH 52/73] Bug 820170 - Weak ref support for globals. r=bholley --- content/base/src/nsInProcessTabChildGlobal.cpp | 1 + content/base/src/nsInProcessTabChildGlobal.h | 2 ++ js/xpconnect/public/SandboxPrivate.h | 4 +++- js/xpconnect/src/BackstagePass.h | 4 +++- js/xpconnect/src/XPCComponents.cpp | 5 ++++- js/xpconnect/src/XPCRuntimeService.cpp | 1 + 6 files changed, 14 insertions(+), 3 deletions(-) diff --git a/content/base/src/nsInProcessTabChildGlobal.cpp b/content/base/src/nsInProcessTabChildGlobal.cpp index df1d185982f2..4a96f32e31ef 100644 --- a/content/base/src/nsInProcessTabChildGlobal.cpp +++ b/content/base/src/nsInProcessTabChildGlobal.cpp @@ -168,6 +168,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsInProcessTabChildGlobal) NS_INTERFACE_MAP_ENTRY(nsIScriptContextPrincipal) NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) NS_INTERFACE_MAP_ENTRY(nsIGlobalObject) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ContentFrameMessageManager) NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper) diff --git a/content/base/src/nsInProcessTabChildGlobal.h b/content/base/src/nsInProcessTabChildGlobal.h index d44c9d043749..390d61b78a94 100644 --- a/content/base/src/nsInProcessTabChildGlobal.h +++ b/content/base/src/nsInProcessTabChildGlobal.h @@ -20,12 +20,14 @@ #include "nsCOMArray.h" #include "nsThreadUtils.h" #include "nsIGlobalObject.h" +#include "nsWeakReference.h" class nsInProcessTabChildGlobal : public nsDOMEventTargetHelper, public nsFrameScriptExecutor, public nsIInProcessContentFrameMessageManager, public nsIScriptContextPrincipal, public nsIGlobalObject, + public nsSupportsWeakReference, public mozilla::dom::ipc::MessageManagerCallback { public: diff --git a/js/xpconnect/public/SandboxPrivate.h b/js/xpconnect/public/SandboxPrivate.h index 69c668bcd845..c2757e4daf21 100644 --- a/js/xpconnect/public/SandboxPrivate.h +++ b/js/xpconnect/public/SandboxPrivate.h @@ -7,11 +7,13 @@ #include "nsIGlobalObject.h" #include "nsIPrincipal.h" +#include "nsWeakReference.h" // This interface is public only because it is used in jsd. // Once jsd is gone this file should be moved back to xpconnect/src. -class SandboxPrivate : public nsIGlobalObject +class SandboxPrivate : public nsIGlobalObject, + public nsSupportsWeakReference { public: SandboxPrivate(nsIPrincipal *principal, JSObject *global) diff --git a/js/xpconnect/src/BackstagePass.h b/js/xpconnect/src/BackstagePass.h index ddb9a27cf752..eab2c3c2a753 100644 --- a/js/xpconnect/src/BackstagePass.h +++ b/js/xpconnect/src/BackstagePass.h @@ -7,11 +7,13 @@ #define BackstagePass_h__ #include "nsISupports.h" +#include "nsWeakReference.h" #include "nsIGlobalObject.h" class BackstagePass : public nsIGlobalObject, public nsIXPCScriptable, - public nsIClassInfo + public nsIClassInfo, + public nsSupportsWeakReference { public: NS_DECL_ISUPPORTS diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index 209654aec9ed..5ba5954462d6 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -2837,7 +2837,10 @@ nsXPCComponents_Utils::ReportError(const JS::Value &error, JSContext *cx) #include "nsNetUtil.h" const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID; -NS_IMPL_THREADSAFE_ISUPPORTS2(SandboxPrivate, nsIScriptObjectPrincipal, nsIGlobalObject) +NS_IMPL_THREADSAFE_ISUPPORTS3(SandboxPrivate, + nsIScriptObjectPrincipal, + nsIGlobalObject, + nsISupportsWeakReference) static JSBool SandboxDump(JSContext *cx, unsigned argc, jsval *vp) diff --git a/js/xpconnect/src/XPCRuntimeService.cpp b/js/xpconnect/src/XPCRuntimeService.cpp index 826ff8908749..20ac65b61e55 100644 --- a/js/xpconnect/src/XPCRuntimeService.cpp +++ b/js/xpconnect/src/XPCRuntimeService.cpp @@ -17,6 +17,7 @@ NS_INTERFACE_MAP_BEGIN(BackstagePass) NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable) NS_INTERFACE_MAP_ENTRY(nsIClassInfo) NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCScriptable) NS_INTERFACE_MAP_END_THREADSAFE From 9eec3699dccfc3b6aff49d6ee2c4e6eb291421ca Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Thu, 4 Apr 2013 11:27:41 +0200 Subject: [PATCH 53/73] Bug 820170 - using nsIGlobalObject in documents. r=smaug --- content/base/public/nsIDocument.h | 10 ++++--- content/base/src/nsContentUtils.cpp | 2 +- content/base/src/nsDocument.cpp | 26 ++++++++++++++----- content/base/src/nsDocument.h | 4 +-- content/base/src/nsObjectLoadingContent.cpp | 2 +- content/events/src/nsEventListenerManager.cpp | 2 +- content/xbl/src/nsXBLBinding.cpp | 5 ++-- content/xbl/src/nsXBLProtoImpl.cpp | 4 +-- content/xbl/src/nsXBLPrototypeHandler.cpp | 2 +- 9 files changed, 36 insertions(+), 21 deletions(-) diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index 49e9363844db..24d4bf13f215 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -77,6 +77,7 @@ class nsSmallVoidArray; class nsDOMCaretPosition; class nsViewportInfo; class nsDOMEvent; +class nsIGlobalObject; namespace mozilla { class ErrorResult; @@ -110,8 +111,8 @@ typedef CallbackObjectHolder NodeFilterHolder; } // namespace mozilla #define NS_IDOCUMENT_IID \ -{ 0x2adedf2, 0x8d85, 0x4a38, \ - { 0xb6, 0x38, 0x91, 0xf4, 0xd2, 0xa4, 0x9b, 0x36 } } +{ 0x8f33bc23, 0x5625, 0x448a, \ + { 0xb3, 0x38, 0xfe, 0x88, 0x16, 0xe, 0xb3, 0xdb } } // Flag for AddStyleSheet(). #define NS_STYLESHEET_FROM_CATALOG (1 << 0) @@ -806,7 +807,8 @@ public: * document is truly gone. Use this object when you're trying to find a * content wrapper in XPConnect. */ - virtual nsIScriptGlobalObject* GetScopeObject() const = 0; + virtual nsIGlobalObject* GetScopeObject() const = 0; + virtual void SetScopeObject(nsIGlobalObject* aGlobal) = 0; /** * Return the window containing the document (the outer window). @@ -1908,7 +1910,7 @@ public: } // WebIDL API - nsIScriptGlobalObject* GetParentObject() const + nsIGlobalObject* GetParentObject() const { return GetScopeObject(); } diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 7d9685afb6c3..efd6e84bc9e4 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -1666,7 +1666,7 @@ nsContentUtils::InProlog(nsINode *aNode) JSContext * nsContentUtils::GetContextFromDocument(nsIDocument *aDocument) { - nsIScriptGlobalObject *sgo = aDocument->GetScopeObject(); + nsCOMPtr sgo = do_QueryInterface(aDocument->GetScopeObject()); if (!sgo) { // No script global, no context. return nullptr; diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 2040466894e0..c496b56e31d5 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -4020,13 +4020,22 @@ nsDocument::GetScriptGlobalObject() const return mScriptGlobalObject; } -nsIScriptGlobalObject* +nsIGlobalObject* nsDocument::GetScopeObject() const { - nsCOMPtr scope(do_QueryReferent(mScopeObject)); + nsCOMPtr scope(do_QueryReferent(mScopeObject)); return scope; } +void +nsDocument::SetScopeObject(nsIGlobalObject* aGlobal) +{ + mScopeObject = do_GetWeakReference(aGlobal); + if (aGlobal) { + mHasHadScriptHandlingObject = true; + } +} + static void NotifyActivityChanged(nsIContent *aContent, void *aUnused) { @@ -4094,7 +4103,6 @@ nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject) // Go back to using the docshell for the layout history state mLayoutHistoryState = nullptr; mScopeObject = do_GetWeakReference(aScriptGlobalObject); - #ifdef DEBUG if (!mWillReparent) { // We really shouldn't have a wrapper here but if we do we need to make sure @@ -5042,7 +5050,7 @@ nsDocument::Register(JSContext* aCx, const nsAString& aName, return nullptr; } - nsIScriptGlobalObject* sgo = GetScopeObject(); + nsIGlobalObject* sgo = GetScopeObject(); if (!sgo) { rv.Throw(NS_ERROR_UNEXPECTED); return nullptr; @@ -6517,7 +6525,8 @@ GetContextAndScope(nsIDocument* aOldDocument, nsIDocument* aNewDocument, JSObject* newScope = aNewDocument->GetWrapper(); JSObject* global; if (!newScope) { - nsIScriptGlobalObject *newSGO = aNewDocument->GetScopeObject(); + nsIGlobalObject *newSGO = aNewDocument->GetScopeObject(); + if (!newSGO || !(global = newSGO->GetGlobalJSObject())) { return NS_OK; } @@ -8309,8 +8318,11 @@ nsDocument::CloneDocHelper(nsDocument* clone) const nsIScriptGlobalObject* scriptObject = GetScriptHandlingObject(hasHadScriptObject); NS_ENSURE_STATE(scriptObject || !hasHadScriptObject); - clone->SetScriptHandlingObject(scriptObject); - + if (scriptObject) { + clone->SetScriptHandlingObject(scriptObject); + } else { + clone->SetScopeObject(GetScopeObject()); + } // Make the clone a data document clone->SetLoadedAsData(true); diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index 8c3057479761..c2a8a5ccd9c9 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -637,8 +637,8 @@ public: virtual void SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject); - virtual nsIScriptGlobalObject* GetScopeObject() const; - + virtual nsIGlobalObject* GetScopeObject() const; + void SetScopeObject(nsIGlobalObject* aGlobal); /** * Get the script loader for this document */ diff --git a/content/base/src/nsObjectLoadingContent.cpp b/content/base/src/nsObjectLoadingContent.cpp index 206881dfca24..9d00d1fbf358 100644 --- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -2609,7 +2609,7 @@ nsObjectLoadingContent::NotifyContentObjectWrapper() if (!doc) return; - nsIScriptGlobalObject *sgo = doc->GetScopeObject(); + nsCOMPtr sgo = do_QueryInterface(doc->GetScopeObject()); if (!sgo) return; diff --git a/content/events/src/nsEventListenerManager.cpp b/content/events/src/nsEventListenerManager.cpp index 208bf70ae8b9..a71bc51f36ef 100644 --- a/content/events/src/nsEventListenerManager.cpp +++ b/content/events/src/nsEventListenerManager.cpp @@ -628,7 +628,7 @@ nsEventListenerManager::SetEventHandler(nsIAtom *aName, // We want to allow compiling an event handler even in an unloaded // document, so use GetScopeObject here, not GetScriptHandlingObject. - global = doc->GetScopeObject(); + global = do_QueryInterface(doc->GetScopeObject()); } else { nsCOMPtr win = GetTargetAsInnerWindow(); if (win) { diff --git a/content/xbl/src/nsXBLBinding.cpp b/content/xbl/src/nsXBLBinding.cpp index 3f25f3658038..94d5c243a5a0 100644 --- a/content/xbl/src/nsXBLBinding.cpp +++ b/content/xbl/src/nsXBLBinding.cpp @@ -925,8 +925,9 @@ nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocumen // Only style bindings get their prototypes unhooked. First do ourselves. if (mIsStyleBinding) { // Now the binding dies. Unhook our prototypes. - if (mPrototypeBinding->HasImplementation()) { - nsIScriptGlobalObject *global = aOldDocument->GetScopeObject(); + if (mPrototypeBinding->HasImplementation()) { + nsCOMPtr global = do_QueryInterface( + aOldDocument->GetScopeObject()); if (global) { JSObject *scope = global->GetGlobalJSObject(); // scope might be null if we've cycle-collected the global diff --git a/content/xbl/src/nsXBLProtoImpl.cpp b/content/xbl/src/nsXBLProtoImpl.cpp index b09d3ac2a69c..ad46bfaa9926 100644 --- a/content/xbl/src/nsXBLProtoImpl.cpp +++ b/content/xbl/src/nsXBLProtoImpl.cpp @@ -56,7 +56,7 @@ nsXBLProtoImpl::InstallImplementation(nsXBLPrototypeBinding* aPrototypeBinding, // nsXBLProtoImplAnonymousMethod::Execute nsIDocument* document = aBinding->GetBoundElement()->OwnerDoc(); - nsIScriptGlobalObject *global = document->GetScopeObject(); + nsCOMPtr global = do_QueryInterface(document->GetScopeObject()); if (!global) return NS_OK; nsCOMPtr context = global->GetContext(); @@ -164,7 +164,7 @@ nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding* aBinding, } nsIDocument *ownerDoc = aBoundElement->OwnerDoc(); - nsIScriptGlobalObject *sgo; + nsIGlobalObject *sgo; if (!(sgo = ownerDoc->GetScopeObject())) { return NS_ERROR_UNEXPECTED; diff --git a/content/xbl/src/nsXBLPrototypeHandler.cpp b/content/xbl/src/nsXBLPrototypeHandler.cpp index a046e28b6d78..cbdae918f2cb 100644 --- a/content/xbl/src/nsXBLPrototypeHandler.cpp +++ b/content/xbl/src/nsXBLPrototypeHandler.cpp @@ -262,7 +262,7 @@ nsXBLPrototypeHandler::ExecuteHandler(nsIDOMEventTarget* aTarget, boundDocument = content->OwnerDoc(); } - boundGlobal = boundDocument->GetScopeObject(); + boundGlobal = do_QueryInterface(boundDocument->GetScopeObject()); } if (!boundGlobal) From 31f45643be5a2d7e4d7a3a82b8f57a9c366a0f0d Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Thu, 4 Apr 2013 11:27:42 +0200 Subject: [PATCH 54/73] Bug 820170 - GetNativeForGlobal. r=bholley --- js/xpconnect/src/xpcpublic.h | 7 ++++++ js/xpconnect/wrappers/WrapperFactory.cpp | 28 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index c16376cffa35..a4f426786d24 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -25,11 +25,13 @@ #include "mozilla/dom/DOMJSClass.h" #include "nsMathUtils.h" #include "nsStringBuffer.h" +#include "nsIGlobalObject.h" #include "mozilla/dom/BindingDeclarations.h" class nsIPrincipal; class nsIXPConnectWrappedJS; class nsScriptNameSpaceManager; +class nsIGlobalObject; #ifndef BAD_TLS_INDEX #define BAD_TLS_INDEX ((uint32_t) -1) @@ -400,6 +402,11 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, bool Throw(JSContext *cx, nsresult rv); +/** + * Every global should hold a native that implements the nsIGlobalObject interface. + */ +nsIGlobalObject * +GetNativeForGlobal(JSObject *global); } // namespace xpc nsCycleCollectionParticipant * diff --git a/js/xpconnect/wrappers/WrapperFactory.cpp b/js/xpconnect/wrappers/WrapperFactory.cpp index e3645dea9796..ebe8d63cce55 100644 --- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -686,4 +686,32 @@ TransplantObjectWithWrapper(JSContext *cx, return newSameCompartmentWrapper; } +nsIGlobalObject * +GetNativeForGlobal(JSObject *obj) +{ + MOZ_ASSERT(JS_IsGlobalObject(obj)); + if (!EnsureCompartmentPrivate(obj)->scope) + return nullptr; + + // Every global needs to hold a native as its private. + MOZ_ASSERT(GetObjectClass(obj)->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS | + JSCLASS_HAS_PRIVATE)); + nsISupports *native = + static_cast(js::GetObjectPrivate(obj)); + MOZ_ASSERT(native); + + // In some cases (like for windows) it is a wrapped native, + // in other cases (sandboxes, backstage passes) it's just + // a direct pointer to the native. If it's a wrapped native + // let's unwrap it first. + if (nsCOMPtr wn = do_QueryInterface(native)) { + native = wn->Native(); + } + + nsCOMPtr global = do_QueryInterface(native); + MOZ_ASSERT(global, "Native held by global needs to implement nsIGlobalObject!"); + + return global; +} + } From 6d0a8bf7714b5b196cb14b440f803f3235532c02 Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Thu, 4 Apr 2013 11:27:43 +0200 Subject: [PATCH 55/73] Bug 820170 - JunkScope. r=bholley --- js/xpconnect/src/XPCJSRuntime.cpp | 44 ++++++++++++++++++++++++++++++- js/xpconnect/src/nsXPConnect.cpp | 1 + js/xpconnect/src/xpcprivate.h | 3 +++ js/xpconnect/src/xpcpublic.h | 14 ++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 80c15bef264c..b151259177e2 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -296,6 +296,14 @@ EnableUniversalXPConnect(JSContext *cx) return nsXPCComponents::AttachComponentsObject(ccx, scope); } +JSObject * +GetJunkScope() +{ + XPCJSRuntime *self = nsXPConnect::GetRuntimeInstance(); + NS_ENSURE_TRUE(self, nullptr); + return self->GetJunkScope(); +} + } static void @@ -2651,7 +2659,8 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) mWatchdogThread(nullptr), mWatchdogHibernating(false), mLastActiveTime(-1), - mExceptionManagerNotAvailable(false) + mExceptionManagerNotAvailable(false), + mJunkScope(nullptr) #ifdef DEBUG , mObjectToUnlink(nullptr) #endif @@ -3000,3 +3009,36 @@ XPCJSRuntime::RemoveGCCallback(JSGCCallback cb) NS_ERROR("Removing a callback which was never added."); } } + +JSObject * +XPCJSRuntime::GetJunkScope() +{ + if (!mJunkScope) { + JS::Value v; + SafeAutoJSContext cx; + SandboxOptions options; + options.sandboxName.AssignASCII("XPConnect Junk Compartment"); + JSAutoRequest ac(cx); + nsresult rv = xpc_CreateSandboxObject(cx, &v, + nsContentUtils::GetSystemPrincipal(), + options); + + NS_ENSURE_SUCCESS(rv, nullptr); + + mJunkScope = js::UnwrapObject(&v.toObject()); + JS_AddNamedObjectRoot(cx, &mJunkScope, "XPConnect Junk Compartment"); + } + return mJunkScope; +} + +void +XPCJSRuntime::DeleteJunkScope() +{ + if(!mJunkScope) + return; + + JSContext *cx = mJSContextStack->GetSafeJSContext(); + JSAutoRequest ac(cx); + JS_RemoveObjectRoot(cx, &mJunkScope); + mJunkScope = nullptr; +} diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 3069c5e85876..2d3e7d886fcb 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -92,6 +92,7 @@ nsXPConnect::nsXPConnect() nsXPConnect::~nsXPConnect() { + mRuntime->DeleteJunkScope(); nsCycleCollector_forgetJSRuntime(); JSContext *cx = nullptr; diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 72533b6554b7..964dce710b1b 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -909,6 +909,8 @@ public: AutoMarkingPtr** GetAutoRootsAdr() {return &mAutoRoots;} + JSObject* GetJunkScope(); + void DeleteJunkScope(); private: XPCJSRuntime(); // no implementation XPCJSRuntime(nsXPConnect* aXPConnect); @@ -959,6 +961,7 @@ private: PRTime mLastActiveTime; // -1 if active NOW nsRefPtr mReleaseRunnable; JS::GCSliceCallback mPrevGCSliceCallback; + JSObject* mJunkScope; nsCOMPtr mPendingException; nsCOMPtr mExceptionManager; diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index a4f426786d24..43c684b3950f 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -407,6 +407,20 @@ Throw(JSContext *cx, nsresult rv); */ nsIGlobalObject * GetNativeForGlobal(JSObject *global); + +/** + * In some cases a native object does not really belong to any compartment (XBL, + * document created from by XHR of a worker, etc.). But when for some reason we + * have to wrap these natives (because of an event for example) instead of just + * wrapping them into some random compartment we find on the context stack (like + * we did previously) a default compartment is used. This function returns that + * compartment's global. It is a singleton on the runtime. + * If you find yourself wanting to use this compartment, you're probably doing + * something wrong. Callers MUST consult with the XPConnect module owner before + * using this compartment. If you don't, bholley will hunt you down. + */ +JSObject * +GetJunkScope(); } // namespace xpc nsCycleCollectionParticipant * From 2e8e9ba1de3c96ec45d35be2c2570b1e634747dd Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Thu, 4 Apr 2013 11:30:36 +0200 Subject: [PATCH 56/73] Bug 820170 - nsDOMEventTarget holds a global. r=smaug --- content/base/public/nsIXMLHttpRequest.idl | 11 +-- content/base/src/nsXMLHttpRequest.cpp | 24 +++++-- content/base/src/nsXMLHttpRequest.h | 18 +++-- content/events/src/nsDOMEventTargetHelper.cpp | 67 ++++++++++++------- content/events/src/nsDOMEventTargetHelper.h | 34 +++++----- content/xul/templates/src/Makefile.in | 1 + .../src/nsXULTemplateQueryProcessorXML.cpp | 8 ++- dom/workers/XMLHttpRequest.cpp | 3 +- js/xpconnect/src/Makefile.in | 1 + js/xpconnect/src/XPCComponents.cpp | 14 ++-- 10 files changed, 117 insertions(+), 64 deletions(-) diff --git a/content/base/public/nsIXMLHttpRequest.idl b/content/base/public/nsIXMLHttpRequest.idl index 52fb039b4966..42271d0360f8 100644 --- a/content/base/public/nsIXMLHttpRequest.idl +++ b/content/base/public/nsIXMLHttpRequest.idl @@ -12,7 +12,7 @@ interface nsIPrincipal; interface nsIScriptContext; interface nsIURI; interface nsIVariant; -interface nsPIDOMWindow; +interface nsIGlobalObject; interface nsIInputStream; interface nsIDOMBlob; @@ -79,7 +79,7 @@ interface nsIXMLHttpRequestUpload : nsIXMLHttpRequestEventTarget { * you're aware of all the security implications. And then think twice about * it. */ -[scriptable, uuid(cd31a34e-71b5-4bea-8366-c926de9d3d62)] +[scriptable, uuid(977f1406-416a-40ac-ab89-ccd7ca0622ea)] interface nsIXMLHttpRequest : nsISupports { /** @@ -301,13 +301,16 @@ interface nsIXMLHttpRequest : nsISupports * null. * @param scriptContext The script context to use for the request. May be * null. - * @param ownerWindow The associated window for the request. May be null. + * @param globalObject The associated global for the request. Can be the + * outer window, a sandbox, or a backstage pass. + * May be null, but then the request cannot create a + * document. * @param baseURI The base URI to use when resolving relative URIs. May be * null. */ [noscript] void init(in nsIPrincipal principal, in nsIScriptContext scriptContext, - in nsPIDOMWindow ownerWindow, + in nsIGlobalObject globalObject, in nsIURI baseURI); /** diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index 28a0665feb0b..0a33ff48fa7f 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -342,7 +342,11 @@ nsXMLHttpRequest::Init() secMan->GetSystemPrincipal(getter_AddRefs(subjectPrincipal)); } NS_ENSURE_STATE(subjectPrincipal); - Construct(subjectPrincipal, nullptr); + + // Instead of grabbing some random global from the context stack, + // let's use the default one (junk drawer) for now. + // We should move away from this Init... + Construct(subjectPrincipal, xpc::GetNativeForGlobal(xpc::GetJunkScope())); return NS_OK; } @@ -352,15 +356,21 @@ nsXMLHttpRequest::Init() NS_IMETHODIMP nsXMLHttpRequest::Init(nsIPrincipal* aPrincipal, nsIScriptContext* aScriptContext, - nsPIDOMWindow* aOwnerWindow, + nsIGlobalObject* aGlobalObject, nsIURI* aBaseURI) { - NS_ASSERTION(!aOwnerWindow || aOwnerWindow->IsOuterWindow(), - "Expecting an outer window here!"); NS_ENSURE_ARG_POINTER(aPrincipal); - Construct(aPrincipal, - aOwnerWindow ? aOwnerWindow->GetCurrentInnerWindow() : nullptr, - aBaseURI); + + if (nsCOMPtr win = do_QueryInterface(aGlobalObject)) { + if (win->IsOuterWindow()) { + // Must be bound to inner window, innerize if necessary. + nsCOMPtr inner = do_QueryInterface( + win->GetCurrentInnerWindow()); + aGlobalObject = inner.get(); + } + } + + Construct(aPrincipal, aGlobalObject, aBaseURI); return NS_OK; } diff --git a/content/base/src/nsXMLHttpRequest.h b/content/base/src/nsXMLHttpRequest.h index 60724b245cbe..208c0f632092 100644 --- a/content/base/src/nsXMLHttpRequest.h +++ b/content/base/src/nsXMLHttpRequest.h @@ -147,16 +147,16 @@ public: const mozilla::dom::MozXMLHttpRequestParameters& aParams, ErrorResult& aRv) { - nsCOMPtr window = do_QueryInterface(aGlobal.Get()); + nsCOMPtr global = do_QueryInterface(aGlobal.Get()); nsCOMPtr principal = do_QueryInterface(aGlobal.Get()); - if (!window || ! principal) { + if (!global || ! principal) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } nsRefPtr req = new nsXMLHttpRequest(); - req->Construct(principal->GetPrincipal(), window); + req->Construct(principal->GetPrincipal(), global); req->InitParameters(aParams.mMozAnon, aParams.mMozSystem); return req.forget(); } @@ -178,13 +178,14 @@ public: } void Construct(nsIPrincipal* aPrincipal, - nsPIDOMWindow* aOwnerWindow, + nsIGlobalObject* aGlobalObject, nsIURI* aBaseURI = nullptr) { MOZ_ASSERT(aPrincipal); - MOZ_ASSERT_IF(aOwnerWindow, aOwnerWindow->IsInnerWindow()); + MOZ_ASSERT_IF(nsCOMPtr win = do_QueryInterface( + aGlobalObject), win->IsInnerWindow()); mPrincipal = aPrincipal; - BindToOwner(aOwnerWindow); + BindToOwner(aGlobalObject); mBaseURI = aBaseURI; } @@ -458,6 +459,11 @@ public: // This is called by the factory constructor. nsresult Init(); + nsresult init(nsIPrincipal* principal, + nsIScriptContext* scriptContext, + nsPIDOMWindow* globalObject, + nsIURI* baseURI); + void SetRequestObserver(nsIRequestObserver* aObserver); NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_INHERITED(nsXMLHttpRequest, diff --git a/content/events/src/nsDOMEventTargetHelper.cpp b/content/events/src/nsDOMEventTargetHelper.cpp index fb2098b0f9dd..e7c8e153a32f 100644 --- a/content/events/src/nsDOMEventTargetHelper.cpp +++ b/content/events/src/nsDOMEventTargetHelper.cpp @@ -26,8 +26,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDOMEventTargetHelper) if (MOZ_UNLIKELY(cb.WantDebugInfo())) { char name[512]; nsAutoString uri; - if (tmp->mOwner && tmp->mOwner->GetExtantDocument()) { - tmp->mOwner->GetExtantDocument()->GetDocumentURI(uri); + if (tmp->mOwnerWindow && tmp->mOwnerWindow->GetExtantDocument()) { + tmp->mOwnerWindow->GetExtantDocument()->GetDocumentURI(uri); } PR_snprintf(name, sizeof(name), "nsDOMEventTargetHelper %s", NS_ConvertUTF16toUTF8(uri).get()); @@ -77,8 +77,8 @@ NS_IMPL_DOMTARGET_DEFAULTS(nsDOMEventTargetHelper) nsDOMEventTargetHelper::~nsDOMEventTargetHelper() { - if (mOwner) { - static_cast(mOwner)->RemoveEventTargetObject(this); + if (nsPIDOMWindow* owner = GetOwner()) { + static_cast(owner)->RemoveEventTargetObject(this); } if (mListenerManager) { mListenerManager->Disconnect(); @@ -89,32 +89,51 @@ nsDOMEventTargetHelper::~nsDOMEventTargetHelper() void nsDOMEventTargetHelper::BindToOwner(nsPIDOMWindow* aOwner) { - if (mOwner) { - static_cast(mOwner)->RemoveEventTargetObject(this); - mOwner = nullptr; - mHasOrHasHadOwner = false; + nsCOMPtr glob = do_QueryInterface(aOwner); + BindToOwner(glob); +} + +void +nsDOMEventTargetHelper::BindToOwner(nsIGlobalObject* aOwner) +{ + if (mParentObject) { + if (mOwnerWindow) { + static_cast(mOwnerWindow)->RemoveEventTargetObject(this); + mOwnerWindow = nullptr; + } + mParentObject = nullptr; + mHasOrHasHadOwnerWindow = false; } if (aOwner) { - mOwner = aOwner; - mHasOrHasHadOwner = true; - static_cast(mOwner)->AddEventTargetObject(this); + mParentObject = aOwner; + // Let's cache the result of this QI for fast access and off main thread usage + mOwnerWindow = nsCOMPtr(do_QueryInterface(aOwner)).get(); + if (mOwnerWindow) { + mHasOrHasHadOwnerWindow = true; + static_cast(mOwnerWindow)->AddEventTargetObject(this); + } } } void nsDOMEventTargetHelper::BindToOwner(nsDOMEventTargetHelper* aOther) { - if (mOwner) { - static_cast(mOwner)->RemoveEventTargetObject(this); - mOwner = nullptr; - mHasOrHasHadOwner = false; + if (mOwnerWindow) { + static_cast(mOwnerWindow)->RemoveEventTargetObject(this); + mOwnerWindow = nullptr; + mParentObject = nullptr; + mHasOrHasHadOwnerWindow = false; } if (aOther) { - mHasOrHasHadOwner = aOther->HasOrHasHadOwner(); - if (aOther->GetOwner()) { - mOwner = aOther->GetOwner(); - mHasOrHasHadOwner = true; - static_cast(mOwner)->AddEventTargetObject(this); + mHasOrHasHadOwnerWindow = aOther->HasOrHasHadOwner(); + if (aOther->GetParentObject()) { + mParentObject = aOther->GetParentObject(); + // Let's cache the result of this QI for fast access and off main thread usage + mOwnerWindow = nsCOMPtr(do_QueryInterface(mParentObject)).get(); + if (mOwnerWindow) { + mHasOrHasHadOwnerWindow = true; + static_cast(mOwnerWindow)->AddEventTargetObject(this); + } } } } @@ -122,7 +141,8 @@ nsDOMEventTargetHelper::BindToOwner(nsDOMEventTargetHelper* aOther) void nsDOMEventTargetHelper::DisconnectFromOwner() { - mOwner = nullptr; + mOwnerWindow = nullptr; + mParentObject = nullptr; // Event listeners can't be handled anymore, so we can release them here. if (mListenerManager) { mListenerManager->Disconnect(); @@ -308,8 +328,9 @@ nsDOMEventTargetHelper::GetContextForEventHandlers(nsresult* aRv) if (NS_FAILED(*aRv)) { return nullptr; } - return mOwner ? static_cast(mOwner)->GetContextInternal() - : nullptr; + nsPIDOMWindow* owner = GetOwner(); + return owner ? static_cast(owner)->GetContextInternal() + : nullptr; } void diff --git a/content/events/src/nsDOMEventTargetHelper.h b/content/events/src/nsDOMEventTargetHelper.h index fdc42f768f6e..d851e0d48761 100644 --- a/content/events/src/nsDOMEventTargetHelper.h +++ b/content/events/src/nsDOMEventTargetHelper.h @@ -25,7 +25,7 @@ class nsDOMEvent; class nsDOMEventTargetHelper : public mozilla::dom::EventTarget { public: - nsDOMEventTargetHelper() : mOwner(nullptr), mHasOrHasHadOwner(false) {} + nsDOMEventTargetHelper() : mParentObject(nullptr), mOwnerWindow(nullptr), mHasOrHasHadOwnerWindow(false) {} virtual ~nsDOMEventTargetHelper(); NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(nsDOMEventTargetHelper) @@ -36,10 +36,9 @@ public: void GetParentObject(nsIScriptGlobalObject **aParentObject) { - if (mOwner) { - CallQueryInterface(mOwner, aParentObject); - } - else { + if (mParentObject) { + CallQueryInterface(mParentObject, aParentObject); + } else { *aParentObject = nullptr; } } @@ -90,22 +89,24 @@ public: nsresult CheckInnerWindowCorrectness() { - NS_ENSURE_STATE(!mHasOrHasHadOwner || mOwner); - if (mOwner) { - NS_ASSERTION(mOwner->IsInnerWindow(), "Should have inner window here!\n"); - nsPIDOMWindow* outer = mOwner->GetOuterWindow(); - if (!outer || outer->GetCurrentInnerWindow() != mOwner) { + NS_ENSURE_STATE(!mHasOrHasHadOwnerWindow || mOwnerWindow); + if (mOwnerWindow) { + NS_ASSERTION(mOwnerWindow->IsInnerWindow(), "Should have inner window here!\n"); + nsPIDOMWindow* outer = mOwnerWindow->GetOuterWindow(); + if (!outer || outer->GetCurrentInnerWindow() != mOwnerWindow) { return NS_ERROR_FAILURE; } } return NS_OK; } + nsPIDOMWindow* GetOwner() const { return mOwnerWindow; } + void BindToOwner(nsIGlobalObject* aOwner); void BindToOwner(nsPIDOMWindow* aOwner); void BindToOwner(nsDOMEventTargetHelper* aOther); virtual void DisconnectFromOwner(); - nsPIDOMWindow* GetOwner() const { return mOwner; } - bool HasOrHasHadOwner() { return mHasOrHasHadOwner; } + nsIGlobalObject* GetParentObject() const { return mParentObject; } + bool HasOrHasHadOwner() { return mHasOrHasHadOwnerWindow; } protected: nsRefPtr mListenerManager; // Dispatch a trusted, non-cancellable and non-bubbling event to |this|. @@ -113,9 +114,12 @@ protected: // Make |event| trusted and dispatch |aEvent| to |this|. nsresult DispatchTrustedEvent(nsIDOMEvent* aEvent); private: - // These may be null (native callers or xpcshell). - nsPIDOMWindow* mOwner; // Inner window. - bool mHasOrHasHadOwner; + // Inner window or sandbox. + nsIGlobalObject* mParentObject; + // mParentObject pre QI-ed and cached + // (it is needed for off main thread access) + nsPIDOMWindow* mOwnerWindow; + bool mHasOrHasHadOwnerWindow; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsDOMEventTargetHelper, diff --git a/content/xul/templates/src/Makefile.in b/content/xul/templates/src/Makefile.in index 34360082f6ef..13705a4ee73a 100644 --- a/content/xul/templates/src/Makefile.in +++ b/content/xul/templates/src/Makefile.in @@ -50,6 +50,7 @@ include $(topsrcdir)/config/rules.mk LOCAL_INCLUDES = -I$(srcdir)/../../../base/src \ -I$(srcdir)/../../content/src \ + -I$(srcdir)/../../../../dom/base \ -I$(srcdir)/../../../../layout/xul/tree/ \ $(NULL) diff --git a/content/xul/templates/src/nsXULTemplateQueryProcessorXML.cpp b/content/xul/templates/src/nsXULTemplateQueryProcessorXML.cpp index ea77713e6991..100bb99ef014 100644 --- a/content/xul/templates/src/nsXULTemplateQueryProcessorXML.cpp +++ b/content/xul/templates/src/nsXULTemplateQueryProcessorXML.cpp @@ -21,6 +21,7 @@ #include "nsArrayUtils.h" #include "nsPIDOMWindow.h" #include "nsXULContentUtils.h" +#include "nsXMLHttpRequest.h" #include "nsXULTemplateQueryProcessorXML.h" #include "nsXULTemplateResultXML.h" @@ -174,9 +175,10 @@ nsXULTemplateQueryProcessorXML::GetDatasource(nsIArray* aDataSources, do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr owner = do_QueryInterface(scriptObject); - req->Init(docPrincipal, context, owner ? owner->GetOuterWindow() : nullptr, - nullptr); + rv = req->Init(docPrincipal, context, + scriptObject ? scriptObject : doc->GetScopeObject(), + nullptr); + NS_ENSURE_SUCCESS(rv, rv); rv = req->Open(NS_LITERAL_CSTRING("GET"), uriStr, true, EmptyString(), EmptyString()); diff --git a/dom/workers/XMLHttpRequest.cpp b/dom/workers/XMLHttpRequest.cpp index 4ed53e7053f4..9a85fab197b9 100644 --- a/dom/workers/XMLHttpRequest.cpp +++ b/dom/workers/XMLHttpRequest.cpp @@ -172,9 +172,10 @@ public: mXHR = new nsXMLHttpRequest(); + nsCOMPtr global = do_QueryInterface(ownerWindow); if (NS_FAILED(mXHR->Init(mWorkerPrivate->GetPrincipal(), mWorkerPrivate->GetScriptContext(), - ownerWindow, mWorkerPrivate->GetBaseURI()))) { + global, mWorkerPrivate->GetBaseURI()))) { mXHR = nullptr; return false; } diff --git a/js/xpconnect/src/Makefile.in b/js/xpconnect/src/Makefile.in index 8a52ef2a1cc8..e4dc3b73ea0a 100644 --- a/js/xpconnect/src/Makefile.in +++ b/js/xpconnect/src/Makefile.in @@ -65,6 +65,7 @@ LOCAL_INCLUDES = \ -I$(srcdir)/../loader \ -I$(topsrcdir)/caps/include \ -I$(topsrcdir)/content/base/src \ + -I$(topsrcdir)/content/base/public \ -I$(topsrcdir)/content/events/src \ -I$(topsrcdir)/content/html/content/src \ -I$(topsrcdir)/content/html/document/src \ diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index 5ba5954462d6..4a1b26fef611 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -2952,14 +2952,18 @@ CreateXMLHttpRequest(JSContext *cx, unsigned argc, jsval *vp) if (!subjectPrincipal) return false; - nsCOMPtr xhr = new nsXMLHttpRequest(); - nsresult rv = xhr->Init(subjectPrincipal, NULL, NULL, NULL); - if (NS_FAILED(rv)) - return false; - JSObject *global = JS_GetGlobalForScopeChain(cx); MOZ_ASSERT(global); + nsIScriptObjectPrincipal *sop = + static_cast(xpc_GetJSPrivate(global)); + nsCOMPtr iglobal = do_QueryInterface(sop); + + nsCOMPtr xhr = new nsXMLHttpRequest(); + nsresult rv = xhr->Init(subjectPrincipal, nullptr, iglobal, nullptr); + if (NS_FAILED(rv)) + return false; + rv = nsContentUtils::WrapNative(cx, global, xhr, vp); if (NS_FAILED(rv)) return false; From e18caa1c32f254539c189a144c098f25a30d3878 Mon Sep 17 00:00:00 2001 From: Gabor Krizsanits Date: Thu, 4 Apr 2013 11:32:29 +0200 Subject: [PATCH 57/73] Bug 820170 - documents should know their globals. r=smaug --- content/base/public/nsIDocument.h | 2 +- content/base/src/DOMImplementation.cpp | 4 ++-- content/base/src/DOMImplementation.h | 2 +- content/base/src/nsDocument.cpp | 5 +++-- content/base/src/nsXMLHttpRequest.cpp | 2 +- content/xml/document/src/XMLDocument.cpp | 10 +++++++--- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index 24d4bf13f215..1dd074cf1bfd 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -2515,7 +2515,7 @@ NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult, nsIURI* aBaseURI, nsIPrincipal* aPrincipal, bool aLoadedAsData, - nsIScriptGlobalObject* aEventObject, + nsIGlobalObject* aEventObject, DocumentFlavor aFlavor); // This is used only for xbl documents created from the startup cache. diff --git a/content/base/src/DOMImplementation.cpp b/content/base/src/DOMImplementation.cpp index 13619123a651..fb12d8d4a983 100644 --- a/content/base/src/DOMImplementation.cpp +++ b/content/base/src/DOMImplementation.cpp @@ -114,7 +114,7 @@ DOMImplementation::CreateDocument(const nsAString& aNamespaceURI, } } - nsCOMPtr scriptHandlingObject = + nsCOMPtr scriptHandlingObject = do_QueryReferent(mScriptObject); NS_ENSURE_STATE(!mScriptObject || scriptHandlingObject); @@ -182,7 +182,7 @@ DOMImplementation::CreateHTMLDocument(const nsAString& aTitle, NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr scriptHandlingObject = + nsCOMPtr scriptHandlingObject = do_QueryReferent(mScriptObject); NS_ENSURE_STATE(!mScriptObject || scriptHandlingObject); diff --git a/content/base/src/DOMImplementation.h b/content/base/src/DOMImplementation.h index d00af2fbf214..28b6673dd314 100644 --- a/content/base/src/DOMImplementation.h +++ b/content/base/src/DOMImplementation.h @@ -29,7 +29,7 @@ class DOMImplementation MOZ_FINAL : public nsIDOMDOMImplementation { public: DOMImplementation(nsIDocument* aOwner, - nsIScriptGlobalObject* aScriptObject, + nsIGlobalObject* aScriptObject, nsIURI* aDocumentURI, nsIURI* aBaseURI) : mOwner(aOwner) diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index c496b56e31d5..564dfdf1a550 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -4177,8 +4177,8 @@ nsDocument::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject) "Wrong script object!"); nsCOMPtr win = do_QueryInterface(aScriptObject); NS_ASSERTION(!win || win->IsInnerWindow(), "Should have inner window here!"); - mScopeObject = do_GetWeakReference(aScriptObject); if (aScriptObject) { + mScopeObject = do_GetWeakReference(aScriptObject); mHasHadScriptHandlingObject = true; mHasHadDefaultView = false; } @@ -4647,7 +4647,8 @@ nsDocument::GetImplementation(ErrorResult& rv) rv.Throw(NS_ERROR_UNEXPECTED); return nullptr; } - mDOMImplementation = new DOMImplementation(this, scriptObject, uri, uri); + mDOMImplementation = new DOMImplementation(this, + scriptObject ? scriptObject : GetScopeObject(), uri, uri); } return mDOMImplementation; diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index 0a33ff48fa7f..b1a7a24ed017 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -2000,8 +2000,8 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt) // principal, so use mPrincipal when creating the document, then reset the // principal. const nsAString& emptyStr = EmptyString(); - nsCOMPtr global = do_QueryInterface(GetOwner()); nsCOMPtr responseDoc; + nsIGlobalObject* global = nsDOMEventTargetHelper::GetParentObject(); rv = NS_NewDOMDocument(getter_AddRefs(responseDoc), emptyStr, emptyStr, nullptr, docURI, baseURI, mPrincipal, true, global, diff --git a/content/xml/document/src/XMLDocument.cpp b/content/xml/document/src/XMLDocument.cpp index ce2f7f517cf3..d0e3a9463ab5 100644 --- a/content/xml/document/src/XMLDocument.cpp +++ b/content/xml/document/src/XMLDocument.cpp @@ -71,7 +71,7 @@ NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult, nsIURI* aBaseURI, nsIPrincipal* aPrincipal, bool aLoadedAsData, - nsIScriptGlobalObject* aEventObject, + nsIGlobalObject* aEventObject, DocumentFlavor aFlavor) { // Note: can't require that aDocumentURI/aBaseURI/aPrincipal be non-null, @@ -127,8 +127,12 @@ NS_NewDOMDocument(nsIDOMDocument** aInstancePtrResult, return rv; } - d->SetScriptHandlingObject(aEventObject); - + if (nsCOMPtr sgo = do_QueryInterface(aEventObject)) { + d->SetScriptHandlingObject(sgo); + } else if (aEventObject){ + d->SetScopeObject(aEventObject); + } + if (isHTML) { nsCOMPtr htmlDoc = do_QueryInterface(d); NS_ASSERTION(htmlDoc, "HTML Document doesn't implement nsIHTMLDocument?"); From 30bf2eb38b7e32a50f57d067e123e4235541cff0 Mon Sep 17 00:00:00 2001 From: Mounir Lamouri Date: Thu, 4 Apr 2013 10:39:29 +0100 Subject: [PATCH 58/73] Bug 857536 - Cleanup the CSS properties applied on from forms.css. r=bz --- .../input/file/input-file-background-ref.xul | 19 +++++++++++ .../input/file/input-file-background.html | 13 ++++++++ .../forms/input/file/input-file-rtl-ref.xul | 2 +- layout/reftests/forms/input/file/reftest.list | 1 + layout/reftests/forms/input/file/style.css | 33 ++++++++++++------- layout/style/forms.css | 7 ++-- 6 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 layout/reftests/forms/input/file/input-file-background-ref.xul create mode 100644 layout/reftests/forms/input/file/input-file-background.html diff --git a/layout/reftests/forms/input/file/input-file-background-ref.xul b/layout/reftests/forms/input/file/input-file-background-ref.xul new file mode 100644 index 000000000000..d5593b5bbc19 --- /dev/null +++ b/layout/reftests/forms/input/file/input-file-background-ref.xul @@ -0,0 +1,19 @@ + + + + + + window { + background-color: blue; + } + + + Browse… + + + Browse… + + diff --git a/layout/reftests/forms/input/file/input-file-background.html b/layout/reftests/forms/input/file/input-file-background.html new file mode 100644 index 000000000000..6479ef91be82 --- /dev/null +++ b/layout/reftests/forms/input/file/input-file-background.html @@ -0,0 +1,13 @@ + + + + + +
+ + + diff --git a/layout/reftests/forms/input/file/input-file-rtl-ref.xul b/layout/reftests/forms/input/file/input-file-rtl-ref.xul index d9604766b0c1..a05051219fd7 100644 --- a/layout/reftests/forms/input/file/input-file-rtl-ref.xul +++ b/layout/reftests/forms/input/file/input-file-rtl-ref.xul @@ -4,7 +4,7 @@ xmlns:html="http://www.w3.org/1999/xhtml"> - + Browse… diff --git a/layout/reftests/forms/input/file/reftest.list b/layout/reftests/forms/input/file/reftest.list index c93b25db1627..f0f69217ae35 100644 --- a/layout/reftests/forms/input/file/reftest.list +++ b/layout/reftests/forms/input/file/reftest.list @@ -1,3 +1,4 @@ fuzzy-if(OSX==10.6,8,128) == input-file-simple.html input-file-simple-ref.xul fuzzy-if(OSX==10.6,8,114) == input-file-rtl.html input-file-rtl-ref.xul fuzzy-if(OSX==10.6,8,128) == input-file-size.html input-file-simple-ref.xul +fuzzy-if(OSX==10.6,8,128) == input-file-background.html input-file-background-ref.xul diff --git a/layout/reftests/forms/input/file/style.css b/layout/reftests/forms/input/file/style.css index 3a1b66665010..c6e13abb4a49 100644 --- a/layout/reftests/forms/input/file/style.css +++ b/layout/reftests/forms/input/file/style.css @@ -4,13 +4,8 @@ vbox { .file { display: inline; - -moz-appearance: none; - padding: 0; - border: 2px inset ThreeDFace; - border-style: none; - - background-color: -moz-Field; + /* Copy of input properties that apply of forms.css below this */ color: -moz-FieldText; font: -moz-field; text-rendering: optimizeLegibility; @@ -19,31 +14,45 @@ vbox { text-transform: none; word-spacing: normal; letter-spacing: normal; - cursor: text; text-indent: 0; -moz-user-select: text; text-shadow: none; + + /* Copy of the type=file part of forms.css below this */ + -moz-appearance: none; + white-space: nowrap; + cursor: default; + -moz-binding: none; + + border: none; + background-color: none; + + padding: 0 !important; } .file > label { display: inline-block; + + /* Copy from forms.css below this */ width: 12em; -moz-padding-start: 5px; - border-color: inherit; - background-color: inherit; color: inherit; font-size: inherit; letter-spacing: inherit; + + direction: ltr !important; } -.file > label.rtl { - text-align: right; +.file[dir='rtl'] > label { + /* Copy from forms.css below this */ -moz-padding-start: 0px; - padding-right: 1px; + padding-right: 5px; + text-align: right; } .file > button { + /* Copy from forms.css below this */ height: inherit; font-size: inherit; letter-spacing: inherit; diff --git a/layout/style/forms.css b/layout/style/forms.css index 20d1dffd137f..b34b1d44d97d 100644 --- a/layout/style/forms.css +++ b/layout/style/forms.css @@ -417,16 +417,17 @@ input[type="file"] { cursor: default; -moz-binding: none; + border: none; + background-color: transparent; + + /* TODO: check why. */ padding: 0 !important; - border-style: none !important; } input[type="file"] > xul|label { width: 12em; -moz-padding-start: 5px; - border-color: inherit; - background-color: inherit; color: inherit; font-size: inherit; letter-spacing: inherit; From 08e0c34dae563250e476615899df7bb920c076f2 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Thu, 4 Apr 2013 12:14:40 +0100 Subject: [PATCH 59/73] Bug 820170 - A clobber is required on Windows to avoid crashes; CLOSED TREE --- CLOBBER | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CLOBBER b/CLOBBER index 7ddac128b89b..88184ee1a154 100644 --- a/CLOBBER +++ b/CLOBBER @@ -15,8 +15,6 @@ # # Note: The description below will be part of the error message shown to users. # -# *** Important! *** -# If changing this file you must manually clobber immediately before landing, -# (due to bug 837323), using: https://secure.pub.build.mozilla.org/clobberer/ +# Modifying this file will now automatically clobber the buildbot machines \o/ # -Bug 737100 broke Windows builds. +Bug 820170 requires a clobber on Windows. From a97df75631175950c370a02c58bdafdfa764f482 Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Sat, 9 Feb 2013 15:50:28 -0800 Subject: [PATCH 60/73] Bug 839831 - make jsjni_* functions usable without MOZILLA_INTERNAL_API. r=blassey --- widget/android/AndroidBridge.cpp | 52 +------------------ widget/android/AndroidBridge.h | 7 --- widget/android/AndroidJNIWrapper.cpp | 75 ++++++++++++++++++++++++++++ widget/android/AndroidJNIWrapper.h | 21 ++++++++ widget/android/Makefile.in | 3 +- 5 files changed, 99 insertions(+), 59 deletions(-) create mode 100644 widget/android/AndroidJNIWrapper.cpp create mode 100644 widget/android/AndroidJNIWrapper.h diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 14321b57b92d..87b756819648 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -16,6 +16,7 @@ #include "nsXPCOMStrings.h" #include "AndroidBridge.h" +#include "AndroidJNIWrapper.h" #include "nsAppShell.h" #include "nsOSHelperAppService.h" #include "nsWindow.h" @@ -2558,54 +2559,3 @@ AndroidBridge::UnlockProfile() return ret; } -extern "C" { - __attribute__ ((visibility("default"))) - jclass - jsjni_FindClass(const char *className) { - JNIEnv *env = AndroidBridge::GetJNIEnv(); - if (!env) return NULL; - return env->FindClass(className); - } - - __attribute__ ((visibility("default"))) - jmethodID - jsjni_GetStaticMethodID(jclass methodClass, - const char *methodName, - const char *signature) { - JNIEnv *env = AndroidBridge::GetJNIEnv(); - if (!env) return NULL; - return env->GetStaticMethodID(methodClass, methodName, signature); - } - - __attribute__ ((visibility("default"))) - bool - jsjni_ExceptionCheck() { - JNIEnv *env = AndroidBridge::GetJNIEnv(); - if (!env) return NULL; - return env->ExceptionCheck(); - } - - __attribute__ ((visibility("default"))) - void - jsjni_CallStaticVoidMethodA(jclass cls, - jmethodID method, - jvalue *values) { - JNIEnv *env = AndroidBridge::GetJNIEnv(); - if (!env) return; - - AutoLocalJNIFrame jniFrame(env); - env->CallStaticVoidMethodA(cls, method, values); - } - - __attribute__ ((visibility("default"))) - int - jsjni_CallStaticIntMethodA(jclass cls, - jmethodID method, - jvalue *values) { - JNIEnv *env = AndroidBridge::GetJNIEnv(); - if (!env) return -1; - - AutoLocalJNIFrame jniFrame(env); - return env->CallStaticIntMethodA(cls, method, values); - } -} diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index 260ae67cd4cb..d3e0e5aeb51c 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -39,13 +39,6 @@ class nsIDOMMozSmsMessage; /* See the comment in AndroidBridge about this function before using it */ extern "C" JNIEnv * GetJNIForThread(); -extern "C" jclass jsjni_FindClass(const char *className); -extern "C" jmethodID jsjni_GetStaticMethodID(jclass methodClass, - const char *methodName, - const char *signature); -extern "C" bool jsjni_ExceptionCheck(); -extern "C" void jsjni_CallStaticVoidMethodA(jclass cls, jmethodID method, jvalue *values); -extern "C" int jsjni_CallStaticIntMethodA(jclass cls, jmethodID method, jvalue *values); extern bool mozilla_AndroidBridge_SetMainThread(void *); extern jclass GetGeckoAppShellClass(); diff --git a/widget/android/AndroidJNIWrapper.cpp b/widget/android/AndroidJNIWrapper.cpp new file mode 100644 index 000000000000..c2c5a76c706b --- /dev/null +++ b/widget/android/AndroidJNIWrapper.cpp @@ -0,0 +1,75 @@ +/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * 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 "mozilla/Util.h" + +#include +#include +#include + +#include "mozilla/Assertions.h" +#include "nsThreadUtils.h" +#include "AndroidBridge.h" + +#ifdef DEBUG +#define ALOG_BRIDGE(args...) ALOG(args) +#else +#define ALOG_BRIDGE(args...) +#endif + +extern "C" { + __attribute__ ((visibility("default"))) + jclass + jsjni_FindClass(const char *className) { + // FindClass outside the main thread will run into problems due + // to missing the classpath + MOZ_ASSERT(NS_IsMainThread()); + JNIEnv *env = mozilla::AndroidBridge::GetJNIEnv(); + if (!env) return NULL; + return env->FindClass(className); + } + + __attribute__ ((visibility("default"))) + jmethodID + jsjni_GetStaticMethodID(jclass methodClass, + const char *methodName, + const char *signature) { + JNIEnv *env = mozilla::AndroidBridge::GetJNIEnv(); + if (!env) return NULL; + return env->GetStaticMethodID(methodClass, methodName, signature); + } + + __attribute__ ((visibility("default"))) + bool + jsjni_ExceptionCheck() { + JNIEnv *env = mozilla::AndroidBridge::GetJNIEnv(); + if (!env) return NULL; + return env->ExceptionCheck(); + } + + __attribute__ ((visibility("default"))) + void + jsjni_CallStaticVoidMethodA(jclass cls, + jmethodID method, + jvalue *values) { + JNIEnv *env = mozilla::AndroidBridge::GetJNIEnv(); + if (!env) return; + + mozilla::AutoLocalJNIFrame jniFrame(env); + env->CallStaticVoidMethodA(cls, method, values); + } + + __attribute__ ((visibility("default"))) + int + jsjni_CallStaticIntMethodA(jclass cls, + jmethodID method, + jvalue *values) { + JNIEnv *env = mozilla::AndroidBridge::GetJNIEnv(); + if (!env) return -1; + + mozilla::AutoLocalJNIFrame jniFrame(env); + return env->CallStaticIntMethodA(cls, method, values); + } +} diff --git a/widget/android/AndroidJNIWrapper.h b/widget/android/AndroidJNIWrapper.h new file mode 100644 index 000000000000..b31d2bd2a7fa --- /dev/null +++ b/widget/android/AndroidJNIWrapper.h @@ -0,0 +1,21 @@ +/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef AndroidJNIWrapper_h__ +#define AndroidJNIWrapper_h__ + +#include +#include + +extern "C" jclass jsjni_FindClass(const char *className); +extern "C" jmethodID jsjni_GetStaticMethodID(jclass methodClass, + const char *methodName, + const char *signature); +extern "C" bool jsjni_ExceptionCheck(); +extern "C" void jsjni_CallStaticVoidMethodA(jclass cls, jmethodID method, jvalue *values); +extern "C" int jsjni_CallStaticIntMethodA(jclass cls, jmethodID method, jvalue *values); + + +#endif /* AndroidJNIWrapper_h__ */ diff --git a/widget/android/Makefile.in b/widget/android/Makefile.in index 0714d6931e3c..b4fbf3154c25 100644 --- a/widget/android/Makefile.in +++ b/widget/android/Makefile.in @@ -32,6 +32,7 @@ CPPSRCS = \ AndroidDirectTexture.cpp \ AndroidGraphicBuffer.cpp \ AndroidJNI.cpp \ + AndroidJNIWrapper.cpp \ nsWindow.cpp \ nsLookAndFeel.cpp \ nsScreenManagerAndroid.cpp \ @@ -54,7 +55,7 @@ NOT_THERE_YET_CPPSRCS = \ SHARED_LIBRARY_LIBS = ../xpwidgets/libxpwidgets_s.a -EXPORTS = AndroidBridge.h AndroidJavaWrappers.h +EXPORTS = AndroidBridge.h AndroidJavaWrappers.h AndroidJNIWrapper.h include $(topsrcdir)/config/rules.mk From a9d73177ee0f0cfba05c645fca5b15b3c01881f9 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sat, 9 Feb 2013 17:30:52 -0800 Subject: [PATCH 61/73] Bug 839836 - Add an alternative to FindClass that works outside the main thread. r=blassey --- widget/android/AndroidJNIWrapper.cpp | 47 ++++++++++++++++++++++++++++ widget/android/AndroidJNIWrapper.h | 10 ++++++ 2 files changed, 57 insertions(+) diff --git a/widget/android/AndroidJNIWrapper.cpp b/widget/android/AndroidJNIWrapper.cpp index c2c5a76c706b..5d7a4d9dfaf0 100644 --- a/widget/android/AndroidJNIWrapper.cpp +++ b/widget/android/AndroidJNIWrapper.cpp @@ -9,6 +9,7 @@ #include #include +#include "mozilla/DebugOnly.h" #include "mozilla/Assertions.h" #include "nsThreadUtils.h" #include "AndroidBridge.h" @@ -19,6 +20,23 @@ #define ALOG_BRIDGE(args...) #endif +extern "C" { + jclass __jsjni_GetGlobalClassRef(const char *className); +} + +class GetGlobalClassRefRunnable : public nsRunnable { + public: + GetGlobalClassRefRunnable(const char *className, jclass *foundClass) : + mClassName(className), mResult(foundClass) {} + NS_IMETHOD Run() { + *mResult = __jsjni_GetGlobalClassRef(mClassName); + return NS_OK; + } + private: + const char *mClassName; + jclass *mResult; +}; + extern "C" { __attribute__ ((visibility("default"))) jclass @@ -31,6 +49,35 @@ extern "C" { return env->FindClass(className); } + jclass + __jsjni_GetGlobalClassRef(const char *className) { + // root class globally + JNIEnv *env = mozilla::AndroidBridge::GetJNIEnv(); + jclass globalRef = static_cast(env->NewGlobalRef(env->FindClass(className))); + if (!globalRef) + return NULL; + + // return the newly create global reference + return globalRef; + } + + __attribute__ ((visibility("default"))) + jclass + jsjni_GetGlobalClassRef(const char *className) { + nsCOMPtr mainThread; + mozilla::DebugOnly rv = NS_GetMainThread(getter_AddRefs(mainThread)); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + jclass foundClass; + nsRefPtr runnable_ref(new GetGlobalClassRefRunnable(className, + &foundClass)); + mainThread->Dispatch(runnable_ref, NS_DISPATCH_SYNC); + if (!foundClass) + return NULL; + + return foundClass; + } + __attribute__ ((visibility("default"))) jmethodID jsjni_GetStaticMethodID(jclass methodClass, diff --git a/widget/android/AndroidJNIWrapper.h b/widget/android/AndroidJNIWrapper.h index b31d2bd2a7fa..621ad08d93f1 100644 --- a/widget/android/AndroidJNIWrapper.h +++ b/widget/android/AndroidJNIWrapper.h @@ -10,6 +10,16 @@ #include extern "C" jclass jsjni_FindClass(const char *className); + +/** + * JNIEnv::FindClass alternative. + * Callable from any thread, including code + * invoked via the JNI that doesn't have MOZILLA_INTERNAL_API defined. + * The caller is responsible for ensuring that the class is not leaked by + * calling DeleteGlobalRef at an appropriate time. + */ +extern "C" jclass jsjni_GetGlobalClassRef(const char *className); + extern "C" jmethodID jsjni_GetStaticMethodID(jclass methodClass, const char *methodName, const char *signature); From ed9a35ece6f0d1e95587e530cd883a4270f81019 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sat, 9 Feb 2013 13:38:08 -0800 Subject: [PATCH 62/73] Bug 830935 - Add a method for getting Fennec's Activity Context from AndroidBridge. r=blassey --- mobile/android/base/GeckoAppShell.java | 4 ++++ widget/android/AndroidBridge.cpp | 21 +++++++++++++++++++++ widget/android/AndroidBridge.h | 7 +++++++ 3 files changed, 32 insertions(+) diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 722385d3f814..06e2b2601973 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -1697,6 +1697,10 @@ public class GeckoAppShell } } + public static Context getContext() { + return GeckoApp.mAppContext; + } + public static android.hardware.Camera sCamera = null; static native void cameraCallbackBridge(byte[] data); diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 87b756819648..704ce136f69a 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -225,6 +225,8 @@ AndroidBridge::Init(JNIEnv *jEnv, jEGLSurfacePointerField = 0; } + jGetContext = (jmethodID)jEnv->GetStaticMethodID(jGeckoAppShellClass, "getContext", "()Landroid/content/Context;"); + InitAndroidJavaWrappers(jEnv); // jEnv should NOT be cached here by anything -- the jEnv here @@ -2033,6 +2035,25 @@ AndroidBridge::LockWindow(void *window, unsigned char **bits, int *width, int *h return true; } +jobject +AndroidBridge::GetGlobalContextRef() { + JNIEnv *env = GetJNIForThread(); + if (!env) + return 0; + + AutoLocalJNIFrame jniFrame(env, 0); + + jobject context = env->CallStaticObjectMethod(mGeckoAppShellClass, jGetContext); + if (jniFrame.CheckForException()) { + return 0; + } + + jobject globalRef = env->NewGlobalRef(context); + MOZ_ASSERT(globalRef); + + return globalRef; +} + bool AndroidBridge::UnlockWindow(void* window) { diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index d3e0e5aeb51c..048d4a574513 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -268,6 +268,11 @@ public: void *LockBitmap(jobject bitmap); + // Returns a global reference to the Context for Fennec's Activity. The + // caller is responsible for ensuring this doesn't leak by calling + // DeleteGlobalRef() when the context is no longer needed. + jobject GetGlobalContextRef(void); + void UnlockBitmap(jobject bitmap); bool UnlockProfile(); @@ -486,6 +491,8 @@ protected: jclass jThumbnailHelperClass; jmethodID jNotifyThumbnail; + jmethodID jGetContext; + // for GfxInfo (gfx feature detection and blacklisting) jmethodID jGetGfxInfoData; From d6b0e8e1bb54b6e029f6d0ac559d4ced5a58b576 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sat, 9 Feb 2013 14:20:03 -0800 Subject: [PATCH 63/73] Bug 830942 - Make VideoEngine attach to JVM on Android to support getUserMedia video capture. r=blassey --- content/media/webrtc/MediaEngineWebRTC.cpp | 17 +++++++++++++++++ .../android/video_capture_android.cc | 4 +--- .../android/video_capture_android.h | 1 - 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/content/media/webrtc/MediaEngineWebRTC.cpp b/content/media/webrtc/MediaEngineWebRTC.cpp index fe0b9f92c52d..21dcda3eef57 100644 --- a/content/media/webrtc/MediaEngineWebRTC.cpp +++ b/content/media/webrtc/MediaEngineWebRTC.cpp @@ -32,6 +32,9 @@ GetUserMediaLog() #include "MediaEngineWebRTC.h" #include "ImageContainer.h" +#ifdef MOZ_WIDGET_ANDROID +#include "AndroidBridge.h" +#endif namespace mozilla { @@ -43,6 +46,20 @@ MediaEngineWebRTC::EnumerateVideoDevices(nsTArrayGetGlobalContextRef(); + + // get the JVM + JavaVM *jvm = mozilla::AndroidBridge::Bridge()->GetVM(); + + JNIEnv *env; + jint res = jvm->AttachCurrentThread(&env, NULL); + + webrtc::VideoEngine::SetAndroidObjects(jvm, (void*)context); + + env->DeleteGlobalRef(context); +#endif + if (!mVideoEngine) { if (!(mVideoEngine = webrtc::VideoEngine::Create())) { return; diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc b/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc index f5abbc91cbf0..7d8a6bdd2b2d 100644 --- a/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc +++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc @@ -59,7 +59,6 @@ jclass VideoCaptureAndroid::g_javaCmClass = NULL; jclass VideoCaptureAndroid::g_javaCmDevInfoClass = NULL; //static instance of VideoCaptureDeviceInfoAndroid.java jobject VideoCaptureAndroid::g_javaCmDevInfoObject = NULL; -jobject VideoCaptureAndroid::g_javaContext = NULL; /* * Register references to Java Capture class. @@ -68,7 +67,6 @@ WebRtc_Word32 VideoCaptureAndroid::SetAndroidObjects(void* javaVM, void* javaContext) { g_jvm = static_cast (javaVM); - g_javaContext = static_cast (javaContext); if (javaVM) { JNIEnv* env = NULL; @@ -161,7 +159,7 @@ WebRtc_Word32 VideoCaptureAndroid::SetAndroidObjects(void* javaVM, jobject javaCameraDeviceInfoObjLocal = env->CallStaticObjectMethod(g_javaCmDevInfoClass, cid, (int) -1, - g_javaContext); + javaContext); if (!javaCameraDeviceInfoObjLocal) { WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, -1, "%s: could not create Java Capture Device info object", diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.h b/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.h index 3fd7e64b1640..15211cb61e3f 100644 --- a/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.h +++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.h @@ -58,7 +58,6 @@ class VideoCaptureAndroid : public VideoCaptureImpl { static jclass g_javaCmDevInfoClass; //Static java object implementing the needed device info functions; static jobject g_javaCmDevInfoObject; - static jobject g_javaContext; // Java Application context }; } // namespace videocapturemodule From 078442006ad0eea5d61777edbc6eb351872d2fcd Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 10 Feb 2013 10:42:44 -0800 Subject: [PATCH 64/73] Bug 839841 - Load Android classes needed by getUserMedia reliably. r=blassey --- .../android/device_info_android.cc | 14 ++++-- .../videoengine/VideoCaptureAndroid.java | 10 ++-- .../android/video_capture_android.cc | 47 +++++-------------- 3 files changed, 27 insertions(+), 44 deletions(-) diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/android/device_info_android.cc b/media/webrtc/trunk/webrtc/modules/video_capture/android/device_info_android.cc index d119531b5e30..70ebc51c8f4c 100644 --- a/media/webrtc/trunk/webrtc/modules/video_capture/android/device_info_android.cc +++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/device_info_android.cc @@ -16,6 +16,8 @@ #include "trace.h" #include "video_capture_android.h" +#include "AndroidJNIWrapper.h" + namespace webrtc { @@ -172,8 +174,8 @@ WebRtc_Word32 DeviceInfoAndroid::CreateCapabilityMap( return -1; // Find the capability class - jclass javaCapClassLocal = env->FindClass(AndroidJavaCaptureCapabilityClass); - if (javaCapClassLocal == NULL) { + jclass javaCapClass = jsjni_GetGlobalClassRef(AndroidJavaCaptureCapabilityClass); + if (javaCapClass == NULL) { VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, "%s: Can't find java class VideoCaptureCapabilityAndroid.", @@ -216,9 +218,9 @@ WebRtc_Word32 DeviceInfoAndroid::CreateCapabilityMap( return -1; } - jfieldID widthField = env->GetFieldID(javaCapClassLocal, "width", "I"); - jfieldID heigtField = env->GetFieldID(javaCapClassLocal, "height", "I"); - jfieldID maxFpsField = env->GetFieldID(javaCapClassLocal, "maxFPS", "I"); + jfieldID widthField = env->GetFieldID(javaCapClass, "width", "I"); + jfieldID heigtField = env->GetFieldID(javaCapClass, "height", "I"); + jfieldID maxFpsField = env->GetFieldID(javaCapClass, "maxFPS", "I"); if (widthField == NULL || heigtField == NULL || maxFpsField == NULL) { VideoCaptureAndroid::ReleaseAndroidDeviceInfoObjects(attached); WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, _id, @@ -257,6 +259,8 @@ WebRtc_Word32 DeviceInfoAndroid::CreateCapabilityMap( WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _id, "CreateCapabilityMap %d", _captureCapabilities.Size()); + env->DeleteGlobalRef(javaCapClass); + return _captureCapabilities.Size(); } diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java index ef7fc7b56341..762798a7db7e 100644 --- a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java +++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java @@ -84,9 +84,9 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { Log.d(TAG, "tryStartCapture " + width + " height " + height +" frame rate " + frameRate + - "isCaptureRunning " + isCaptureRunning + - "isSurfaceReady " + isSurfaceReady + - "isCaptureStarted " + isCaptureStarted); + " isCaptureRunning " + isCaptureRunning + + " isSurfaceReady " + isSurfaceReady + + " isCaptureStarted " + isCaptureStarted); if (isCaptureRunning || !isSurfaceReady || !isCaptureStarted) { return 0; @@ -179,8 +179,8 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { previewBufferLock.lock(); // The following line is for debug only - // Log.v(TAG, "preview frame length " + data.length + - // " context" + context); + Log.v(TAG, "preview frame length " + data.length + + " context" + context); if (isCaptureRunning) { // If StartCapture has been called but not StopCapture // Call the C++ layer with the captured frame diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc b/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc index 7d8a6bdd2b2d..823ee2ed2102 100644 --- a/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc +++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc @@ -16,6 +16,8 @@ #include "ref_count.h" #include "trace.h" +#include "AndroidJNIWrapper.h" + namespace webrtc { #if defined(WEBRTC_ANDROID) && !defined(WEBRTC_CHROMIUM_BUILD) @@ -76,26 +78,12 @@ WebRtc_Word32 VideoCaptureAndroid::SetAndroidObjects(void* javaVM, return -1; } // get java capture class type (note path to class packet) - jclass javaCmClassLocal = env->FindClass(AndroidJavaCaptureClass); - if (!javaCmClassLocal) { + g_javaCmClass = jsjni_GetGlobalClassRef(AndroidJavaCaptureClass); + if (!g_javaCmClass) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, "%s: could not find java class", __FUNCTION__); return -1; } - // create a global reference to the class - // (to tell JNI that we are referencing it - // after this function has returned) - g_javaCmClass = static_cast - (env->NewGlobalRef(javaCmClassLocal)); - if (!g_javaCmClass) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: InitVideoEngineJava(): could not create" - " Java Camera class reference", - __FUNCTION__); - return -1; - } - // Delete local class ref, we only use the global ref - env->DeleteLocalRef(javaCmClassLocal); JNINativeMethod nativeFunctions = { "ProvideCameraFrame", "([BIJ)V", (void*) &VideoCaptureAndroid::ProvideCameraFrame }; @@ -111,29 +99,14 @@ WebRtc_Word32 VideoCaptureAndroid::SetAndroidObjects(void* javaVM, } // get java capture class type (note path to class packet) - jclass javaCmDevInfoClassLocal = env->FindClass( - AndroidJavaCaptureDeviceInfoClass); - if (!javaCmDevInfoClassLocal) { + g_javaCmDevInfoClass = jsjni_GetGlobalClassRef( + AndroidJavaCaptureDeviceInfoClass); + if (!g_javaCmDevInfoClass) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, "%s: could not find java class", __FUNCTION__); return -1; } - // create a global reference to the class - // (to tell JNI that we are referencing it - // after this function has returned) - g_javaCmDevInfoClass = static_cast - (env->NewGlobalRef(javaCmDevInfoClassLocal)); - if (!g_javaCmDevInfoClass) { - WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, - "%s: InitVideoEngineJava(): could not create Java " - "Camera Device info class reference", - __FUNCTION__); - return -1; - } - // Delete local class ref, we only use the global ref - env->DeleteLocalRef(javaCmDevInfoClassLocal); - WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, "VideoCaptureDeviceInfoAndroid get method id"); @@ -436,7 +409,13 @@ VideoCaptureAndroid::~VideoCaptureAndroid() { // Delete global object ref to the camera. env->DeleteGlobalRef(_javaCaptureObj); + // Clean up the global class references + env->DeleteGlobalRef(g_javaCmClass); + env->DeleteGlobalRef(g_javaCmDevInfoClass); + _javaCaptureObj = NULL; + VideoCaptureAndroid::g_javaCmClass = NULL; + VideoCaptureAndroid::g_javaCmDevInfoClass = NULL; } else { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, From d08b19e35cf5961547f7955d9ff5a2cd3e347b61 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sat, 9 Feb 2013 14:49:49 -0800 Subject: [PATCH 65/73] Bug 835973 - Build and package Android Java video capture classes so they can be found for use by getUserMedia. r=glandium --- mobile/android/base/Makefile.in | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 4318869a98b1..1489271338d9 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -222,6 +222,18 @@ ifdef MOZ_WEBSMS_BACKEND FENNEC_JAVA_FILES += GeckoSmsManager.java endif +ifdef MOZ_WEBRTC +WEBRTC_VIDEO_CAPTURE_JAVA_FILES = \ + CaptureCapabilityAndroid.java \ + VideoCaptureAndroid.java \ + VideoCaptureDeviceInfoAndroid.java \ + $(NULL) + +WEBRTC_JAVA_FILES = \ + $(addprefix ../../../media/webrtc/trunk/src/modules/video_capture/main/source/android/java/org/webrtc/videoengine/, $(WEBRTC_VIDEO_CAPTURE_JAVA_FILES)) \ + $(NULL) +endif + ifdef MOZ_ANDROID_ANR_REPORTER DEFINES += -DMOZ_ANDROID_ANR_REPORTER=1 FENNEC_JAVA_FILES += ANRReporter.java @@ -1085,7 +1097,7 @@ include $(topsrcdir)/config/android-common.mk # Sync dependencies are provided in a single jar. Sync classes themselves are delivered as source, # because Android resource classes must be compiled together in order to avoid overlapping resource # indices. -classes.dex: jars/gecko-browser.jar +classes.dex: jars/gecko-browser.jar jars/webrtc.jar @echo "DX classes.dex" $(DX) --dex --output=classes.dex jars $(ANDROID_COMPAT_LIB) @@ -1113,6 +1125,14 @@ jars/sync-thirdparty.jar: $(addprefix $(srcdir)/,$(SYNC_THIRDPARTY_JAVA_FILES)) $(JAVAC) $(JAVAC_FLAGS) -d classes/sync-thirdparty $(addprefix $(srcdir)/,$(SYNC_THIRDPARTY_JAVA_FILES)) $(JAR) cMf jars/sync-thirdparty.jar -C classes/sync-thirdparty . +ifdef MOZ_WEBRTC +jars/webrtc.jar: $(addprefix $(srcdir)/, $(WEBRTC_JAVA_FILES)) jars + @echo "JAR webrtc.jar" + $(NSINSTALL) -D classes/webrtc + $(JAVAC) $(JAVAC_FLAGS) -d classes/webrtc $(addprefix $(srcdir)/,$(WEBRTC_JAVA_FILES)) + $(JAR) cMf jars/webrtc.jar -C classes/webrtc . +endif + jars: @echo "MKDIR jars" $(NSINSTALL) -D jars From b4688da21186fb5c81610ac68efe8fa83df429bb Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sat, 9 Feb 2013 16:18:06 -0800 Subject: [PATCH 66/73] Bug 835973 - Fix Java capture build bustage by commenting out ref to unused Java rendering code. r=blassey --- .../java/org/webrtc/videoengine/VideoCaptureAndroid.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java index 762798a7db7e..1a906d949da8 100644 --- a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java +++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java @@ -137,12 +137,6 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { public int StartCapture(int width, int height, int frameRate) { Log.d(TAG, "StartCapture width " + width + " height " + height +" frame rate " + frameRate); - // Get the local preview SurfaceHolder from the static render class - localPreview = ViERenderer.GetLocalRenderer(); - if (localPreview != null) { - localPreview.addCallback(this); - } - captureLock.lock(); isCaptureStarted = true; mCaptureWidth = width; From c3fe2c0753c285d988311adb3de3115e9e9109ac Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Sun, 10 Feb 2013 11:30:26 -0800 Subject: [PATCH 67/73] Bug 839907 - Fix Android getUserMedia video by giving it a dummy surface. Compile Java classes. r=glandium. --- .../videoengine/VideoCaptureAndroid.java | 37 +++++++++++++++++++ mobile/android/base/Makefile.in | 6 +-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java index 1a906d949da8..90d6ca02f51a 100644 --- a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java +++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java @@ -27,6 +27,9 @@ import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; +import org.mozilla.gecko.GeckoApp; +import org.mozilla.gecko.GeckoAppShell; + public class VideoCaptureAndroid implements PreviewCallback, Callback { private final static String TAG = "WEBRTC-JC"; @@ -66,6 +69,21 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { captureAndroid.camera.release(); captureAndroid.camera = null; captureAndroid.context = 0; + + GeckoApp.mAppContext.cameraView.getHolder(). + removeCallback(captureAndroid); + GeckoAppShell.getMainHandler().post(new Runnable() { + @Override + public void run() { + try { + GeckoApp.mAppContext.disableCameraView(); + } catch (Exception e) { + Log.e(TAG, + "VideoCaptureAndroid disableCameraView exception: " + + e.getLocalizedMessage()); + } + } + }); } public VideoCaptureAndroid(int in_id, long in_context, Camera in_camera, @@ -74,6 +92,25 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { context = in_context; camera = in_camera; currentDevice = in_device; + + try { + GeckoApp.mAppContext.cameraView.getHolder().addCallback(this); + GeckoAppShell.getMainHandler().post(new Runnable() { + @Override + public void run() { + try { + GeckoApp.mAppContext.enableCameraView(); + } catch (Exception e) { + Log.e(TAG, + "VideoCaptureAndroid enableCameraView exception: " + + e.getLocalizedMessage()); + } + } + }); + } catch (Exception ex) { + Log.e(TAG, "VideoCaptureAndroid constructor exception: " + + ex.getLocalizedMessage()); + } } private int tryStartCapture(int width, int height, int frameRate) { diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 1489271338d9..8babb1b85c33 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -230,7 +230,7 @@ WEBRTC_VIDEO_CAPTURE_JAVA_FILES = \ $(NULL) WEBRTC_JAVA_FILES = \ - $(addprefix ../../../media/webrtc/trunk/src/modules/video_capture/main/source/android/java/org/webrtc/videoengine/, $(WEBRTC_VIDEO_CAPTURE_JAVA_FILES)) \ + $(addprefix $(DEPTH)/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/, $(WEBRTC_VIDEO_CAPTURE_JAVA_FILES)) \ $(NULL) endif @@ -1126,10 +1126,10 @@ jars/sync-thirdparty.jar: $(addprefix $(srcdir)/,$(SYNC_THIRDPARTY_JAVA_FILES)) $(JAR) cMf jars/sync-thirdparty.jar -C classes/sync-thirdparty . ifdef MOZ_WEBRTC -jars/webrtc.jar: $(addprefix $(srcdir)/, $(WEBRTC_JAVA_FILES)) jars +jars/webrtc.jar: $(addprefix $(srcdir)/, $(WEBRTC_JAVA_FILES)) jars jars/gecko-browser.jar jars/gecko-util.jar @echo "JAR webrtc.jar" $(NSINSTALL) -D classes/webrtc - $(JAVAC) $(JAVAC_FLAGS) -d classes/webrtc $(addprefix $(srcdir)/,$(WEBRTC_JAVA_FILES)) + $(JAVAC) $(JAVAC_FLAGS) -d classes/webrtc -classpath classes/gecko-browser:classes/gecko-util $(addprefix $(srcdir)/,$(WEBRTC_JAVA_FILES)) $(JAR) cMf jars/webrtc.jar -C classes/webrtc . endif From b2107d8493f465097846434d7612c45407090021 Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Thu, 4 Apr 2013 13:48:02 +0200 Subject: [PATCH 68/73] Bug 835973 - Make build work when WebRTC is disabled by not trying to link the WebRTC jar. r=glandium --- mobile/android/base/Makefile.in | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 8babb1b85c33..9322b6089ecc 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -1087,6 +1087,17 @@ RES_DIRS= \ res/menu-xlarge-v11 \ $(NULL) +ALL_JARS = \ + jars/gecko-browser.jar \ + jars/gecko-util.jar \ + jars/sync-thirdparty.jar \ + jars/gecko-mozglue.jar \ + $(NULL) + +ifdef MOZ_WEBRTC +ALL_JARS += jars/webrtc.jar +endif + include $(topsrcdir)/config/rules.mk # Override the Java settings with some specific android settings @@ -1097,7 +1108,7 @@ include $(topsrcdir)/config/android-common.mk # Sync dependencies are provided in a single jar. Sync classes themselves are delivered as source, # because Android resource classes must be compiled together in order to avoid overlapping resource # indices. -classes.dex: jars/gecko-browser.jar jars/webrtc.jar +classes.dex: $(ALL_JARS) @echo "DX classes.dex" $(DX) --dex --output=classes.dex jars $(ANDROID_COMPAT_LIB) From 4ee5c65e2f56377ff768d080d05e76d704c6fae3 Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Thu, 4 Apr 2013 13:48:02 +0200 Subject: [PATCH 69/73] Bug 835973 - Update alder code for mozilla-central changes in getMainHandler. r=blassey --- .../java/org/webrtc/videoengine/VideoCaptureAndroid.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java index 90d6ca02f51a..2769b138f47b 100644 --- a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java +++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java @@ -29,6 +29,7 @@ import android.view.SurfaceHolder.Callback; import org.mozilla.gecko.GeckoApp; import org.mozilla.gecko.GeckoAppShell; +import org.mozilla.gecko.util.ThreadUtils; public class VideoCaptureAndroid implements PreviewCallback, Callback { @@ -72,7 +73,7 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { GeckoApp.mAppContext.cameraView.getHolder(). removeCallback(captureAndroid); - GeckoAppShell.getMainHandler().post(new Runnable() { + ThreadUtils.getUiHandler().post(new Runnable() { @Override public void run() { try { @@ -95,7 +96,7 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { try { GeckoApp.mAppContext.cameraView.getHolder().addCallback(this); - GeckoAppShell.getMainHandler().post(new Runnable() { + ThreadUtils.getUiHandler().post(new Runnable() { @Override public void run() { try { From 14361d21a049e2b36a83fec4c4d69dbb83304654 Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Fri, 22 Feb 2013 11:12:25 +0100 Subject: [PATCH 70/73] Bug 840244 - Calculate camera and device rotation, rotate video to compensate. r=blassey --- .../videoengine/VideoCaptureAndroid.java | 37 ++++++++++++++++-- .../VideoCaptureDeviceInfoAndroid.java | 13 ++++--- .../android/video_capture_android.cc | 38 +++++++++++++++++++ 3 files changed, 79 insertions(+), 9 deletions(-) diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java index 2769b138f47b..220425853a22 100644 --- a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java +++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureAndroid.java @@ -24,6 +24,7 @@ import android.graphics.YuvImage; import android.hardware.Camera; import android.hardware.Camera.PreviewCallback; import android.util.Log; +import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; @@ -36,6 +37,7 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { private final static String TAG = "WEBRTC-JC"; private Camera camera; + private int cameraId; private AndroidVideoCaptureDevice currentDevice = null; public ReentrantLock previewBufferLock = new ReentrantLock(); // This lock takes sync with StartCapture and SurfaceChanged @@ -88,10 +90,12 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { } public VideoCaptureAndroid(int in_id, long in_context, Camera in_camera, - AndroidVideoCaptureDevice in_device) { + AndroidVideoCaptureDevice in_device, + int in_cameraId) { id = in_id; context = in_context; camera = in_camera; + cameraId = in_cameraId; currentDevice = in_device; try { @@ -114,6 +118,30 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { } } + public int GetRotateAmount() { + android.hardware.Camera.CameraInfo info = + new android.hardware.Camera.CameraInfo(); + android.hardware.Camera.getCameraInfo(cameraId, info); + int rotation = GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getRotation(); + int degrees = 0; + switch (rotation) { + case Surface.ROTATION_0: degrees = 0; break; + case Surface.ROTATION_90: degrees = 90; break; + case Surface.ROTATION_180: degrees = 180; break; + case Surface.ROTATION_270: degrees = 270; break; + } + + int result; + if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { + result = (info.orientation + degrees) % 360; + result = (360 - result) % 360; // compensate the mirror + } else { // back-facing + result = (info.orientation - degrees + 360) % 360; + } + + return result; + } + private int tryStartCapture(int width, int height, int frameRate) { if (camera == null) { Log.e(TAG, "Camera not initialized %d" + id); @@ -164,7 +192,7 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { } catch (Exception ex) { - Log.e(TAG, "Failed to start camera"); + Log.e(TAG, "Failed to start camera: " + ex.getMessage()); return -1; } @@ -230,13 +258,14 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { // Sets the rotation of the preview render window. // Does not affect the captured video image. public void SetPreviewRotation(int rotation) { - Log.v(TAG, "SetPreviewRotation:" + rotation); + Log.v(TAG, "SetPreviewRotation: " + rotation); if (camera != null) { previewBufferLock.lock(); int width = 0; int height = 0; int framerate = 0; + boolean wasCaptureRunning = isCaptureRunning; if (isCaptureRunning) { width = mCaptureWidth; @@ -259,7 +288,7 @@ public class VideoCaptureAndroid implements PreviewCallback, Callback { } camera.setDisplayOrientation(resultRotation); - if (isCaptureRunning) { + if (wasCaptureRunning) { StartCapture(width, height, framerate); } previewBufferLock.unlock(); diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java index b0e75cc438e8..dc55e4f2f919 100644 --- a/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java +++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/java/org/webrtc/videoengine/VideoCaptureDeviceInfoAndroid.java @@ -258,6 +258,7 @@ public class VideoCaptureDeviceInfoAndroid { Log.d(TAG, "AllocateCamera " + deviceUniqueId); Camera camera = null; + int cameraId = 0; AndroidVideoCaptureDevice deviceToUse = null; for (AndroidVideoCaptureDevice device: deviceList) { if(device.deviceUniqueName.equals(deviceUniqueId)) { @@ -272,10 +273,12 @@ public class VideoCaptureDeviceInfoAndroid { break; default: // From Android 2.3 and onwards) - if(android.os.Build.VERSION.SDK_INT>8) - camera=Camera.open(device.index); - else - camera=Camera.open(); // Default camera + if(android.os.Build.VERSION.SDK_INT>8) { + cameraId = device.index; + camera = Camera.open(device.index); + } else { + camera = Camera.open(); // Default_ camera + } } } } @@ -285,7 +288,7 @@ public class VideoCaptureDeviceInfoAndroid { } Log.v(TAG, "AllocateCamera - creating VideoCaptureAndroid"); - return new VideoCaptureAndroid(id, context, camera, deviceToUse); + return new VideoCaptureAndroid(id, context, camera, deviceToUse, cameraId); }catch (Exception ex) { Log.e(TAG, "AllocateCamera Failed to open camera- ex " + diff --git a/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc b/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc index 823ee2ed2102..58e591b7f9aa 100644 --- a/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc +++ b/media/webrtc/trunk/webrtc/modules/video_capture/android/video_capture_android.cc @@ -442,6 +442,7 @@ WebRtc_Word32 VideoCaptureAndroid::StartCapture( bool isAttached = false; WebRtc_Word32 result = 0; + WebRtc_Word32 rotation = 0; // get the JNI env for this thread JNIEnv *env; if (g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { @@ -488,6 +489,21 @@ WebRtc_Word32 VideoCaptureAndroid::StartCapture( "%s: Failed to find StartCapture id", __FUNCTION__); } + // get the method ID for the Android Java + // CaptureClass static GetRotateAmount method. + cid = env->GetMethodID(g_javaCmClass, "GetRotateAmount", "()I"); + if (cid != NULL) { + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, + "%s: Call GetRotateAmount", __FUNCTION__); + rotation = env->CallIntMethod(_javaCaptureObj, cid); + WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, -1, + "%s, GetRotateAmount = %d", __FUNCTION__, rotation); + } + else { + WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoCapture, -1, + "%s: Failed to find GetRotateAmount id", __FUNCTION__); + } + // Detach this thread if it was attached if (isAttached) { if (g_jvm->DetachCurrentThread() < 0) { @@ -495,6 +511,28 @@ WebRtc_Word32 VideoCaptureAndroid::StartCapture( "%s: Could not detach thread from JVM", __FUNCTION__); } } + + // I guess the libyuv rotate is CCW vs Android being CW, + // so we need to invert. + // Note that SetCaptureRotation calls SetDisplayOrientation, + // but we don't use a visible Surface so we can ignore that one. + rotation = (360 - rotation) % 360; + switch (rotation) { + case 90: + SetCaptureRotation(kCameraRotate90); + break; + case 180: + SetCaptureRotation(kCameraRotate180); + break; + case 270: + SetCaptureRotation(kCameraRotate270); + break; + case 0: + default: + SetCaptureRotation(kCameraRotate0); + break; + } + if (result == 0) { _requestedCapability = capability; _captureStarted = true; From ed919c3997d29510cfac08ba7dcb30f6ffe08425 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Thu, 4 Apr 2013 10:39:05 +0100 Subject: [PATCH 71/73] Bug 855350 - GC: Add CustomAutoRooter and use it internally r=terrence --HG-- extra : rebase_source : ff4fdd39c15223bff4682b8c7b9833922e355999 --- CLOBBER | 2 +- js/src/gc/RootMarking.cpp | 97 ++++++++++++++--------------------- js/src/jsapi.h | 75 +++++++++++++++++---------- js/src/vm/ObjectImpl.h | 13 +++-- js/src/vm/RegExpStatics-inl.h | 22 ++++++-- js/src/vm/Shape.h | 42 +++++++++------ 6 files changed, 141 insertions(+), 110 deletions(-) diff --git a/CLOBBER b/CLOBBER index 88184ee1a154..169c3cb76519 100644 --- a/CLOBBER +++ b/CLOBBER @@ -17,4 +17,4 @@ # # Modifying this file will now automatically clobber the buildbot machines \o/ # -Bug 820170 requires a clobber on Windows. +Bug 855350 broke Windows builds. diff --git a/js/src/gc/RootMarking.cpp b/js/src/gc/RootMarking.cpp index 4d3b7a9ea03d..2f6e71da4ce6 100644 --- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -530,53 +530,6 @@ AutoGCRooter::trace(JSTracer *trc) return; } - case PROPDESC: { - PropDesc::AutoRooter *rooter = static_cast(this); - MarkValueRoot(trc, &rooter->pd->pd_, "PropDesc::AutoRooter pd"); - MarkValueRoot(trc, &rooter->pd->value_, "PropDesc::AutoRooter value"); - MarkValueRoot(trc, &rooter->pd->get_, "PropDesc::AutoRooter get"); - MarkValueRoot(trc, &rooter->pd->set_, "PropDesc::AutoRooter set"); - return; - } - - case STACKSHAPE: { - StackShape::AutoRooter *rooter = static_cast(this); - if (rooter->shape->base) - MarkBaseShapeRoot(trc, (BaseShape**) &rooter->shape->base, "StackShape::AutoRooter base"); - MarkIdRoot(trc, (jsid*) &rooter->shape->propid, "StackShape::AutoRooter id"); - return; - } - - case STACKBASESHAPE: { - StackBaseShape::AutoRooter *rooter = static_cast(this); - if (rooter->base->parent) - MarkObjectRoot(trc, (JSObject**) &rooter->base->parent, "StackBaseShape::AutoRooter parent"); - if ((rooter->base->flags & BaseShape::HAS_GETTER_OBJECT) && rooter->base->rawGetter) { - MarkObjectRoot(trc, (JSObject**) &rooter->base->rawGetter, - "StackBaseShape::AutoRooter getter"); - } - if ((rooter->base->flags & BaseShape::HAS_SETTER_OBJECT) && rooter->base->rawSetter) { - MarkObjectRoot(trc, (JSObject**) &rooter->base->rawSetter, - "StackBaseShape::AutoRooter setter"); - } - return; - } - - case GETTERSETTER: { - AutoRooterGetterSetter::Inner *rooter = static_cast(this); - if ((rooter->attrs & JSPROP_GETTER) && *rooter->pgetter) - MarkObjectRoot(trc, (JSObject**) rooter->pgetter, "AutoRooterGetterSetter getter"); - if ((rooter->attrs & JSPROP_SETTER) && *rooter->psetter) - MarkObjectRoot(trc, (JSObject**) rooter->psetter, "AutoRooterGetterSetter setter"); - return; - } - - case REGEXPSTATICS: { - RegExpStatics::AutoRooter *rooter = static_cast(this); - rooter->trace(trc); - return; - } - case HASHABLEVALUE: { /* HashableValue::AutoRooter *rooter = static_cast(this); @@ -625,6 +578,10 @@ AutoGCRooter::trace(JSTracer *trc) case JSONPARSER: static_cast(this)->trace(trc); return; + + case CUSTOM: + static_cast(this)->trace(trc); + return; } JS_ASSERT(tag_ >= 0); @@ -648,18 +605,34 @@ AutoGCRooter::traceAllWrappers(JSTracer *trc) } } -void -RegExpStatics::AutoRooter::trace(JSTracer *trc) +/* static */ void +JS::CustomAutoRooter::traceObject(JSTracer *trc, JSObject **thingp, const char *name) { - if (statics->matchesInput) - MarkStringRoot(trc, reinterpret_cast(&statics->matchesInput), - "RegExpStatics::AutoRooter matchesInput"); - if (statics->lazySource) - MarkStringRoot(trc, reinterpret_cast(&statics->lazySource), - "RegExpStatics::AutoRooter lazySource"); - if (statics->pendingInput) - MarkStringRoot(trc, reinterpret_cast(&statics->pendingInput), - "RegExpStatics::AutoRooter pendingInput"); + MarkObjectRoot(trc, thingp, name); +} + +/* static */ void +JS::CustomAutoRooter::traceScript(JSTracer *trc, JSScript **thingp, const char *name) +{ + MarkScriptRoot(trc, thingp, name); +} + +/* static */ void +JS::CustomAutoRooter::traceString(JSTracer *trc, JSString **thingp, const char *name) +{ + MarkStringRoot(trc, thingp, name); +} + +/* static */ void +JS::CustomAutoRooter::traceId(JSTracer *trc, jsid *thingp, const char *name) +{ + MarkIdRoot(trc, thingp, name); +} + +/* static */ void +JS::CustomAutoRooter::traceValue(JSTracer *trc, JS::Value *thingp, const char *name) +{ + MarkValueRoot(trc, thingp, name); } void @@ -668,6 +641,14 @@ HashableValue::AutoRooter::trace(JSTracer *trc) MarkValueRoot(trc, reinterpret_cast(&v->value), "HashableValue::AutoRooter"); } +void +StackShape::AutoRooter::trace(JSTracer *trc) +{ + if (shape->base) + MarkBaseShapeRoot(trc, (BaseShape**) &shape->base, "StackShape::AutoRooter base"); + MarkIdRoot(trc, (jsid*) &shape->propid, "StackShape::AutoRooter id"); +} + void js::gc::MarkRuntime(JSTracer *trc, bool useSavedRoots) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 82743e610a4b..2d311adbb312 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -118,32 +118,26 @@ class JS_PUBLIC_API(AutoGCRooter) { SHAPEVECTOR = -4, /* js::AutoShapeVector */ IDARRAY = -6, /* js::AutoIdArray */ DESCRIPTORS = -7, /* js::AutoPropDescArrayRooter */ - // UNUSED -8 - // UNUSED -9 - OBJECT = -10, /* js::AutoObjectRooter */ - ID = -11, /* js::AutoIdRooter */ - VALVECTOR = -12, /* js::AutoValueVector */ - DESCRIPTOR = -13, /* js::AutoPropertyDescriptorRooter */ - STRING = -14, /* js::AutoStringRooter */ - IDVECTOR = -15, /* js::AutoIdVector */ - OBJVECTOR = -16, /* js::AutoObjectVector */ - STRINGVECTOR =-17, /* js::AutoStringVector */ - SCRIPTVECTOR =-18, /* js::AutoScriptVector */ - PROPDESC = -19, /* js::PropDesc::AutoRooter */ - STACKSHAPE = -21, /* js::StackShape::AutoRooter */ - STACKBASESHAPE=-22,/* js::StackBaseShape::AutoRooter */ - GETTERSETTER =-24, /* js::AutoRooterGetterSetter */ - REGEXPSTATICS=-25, /* js::RegExpStatics::AutoRooter */ - NAMEVECTOR = -26, /* js::AutoNameVector */ - HASHABLEVALUE=-27, - IONMASM = -28, /* js::ion::MacroAssembler */ - IONALLOC = -29, /* js::ion::AutoTempAllocatorRooter */ - WRAPVECTOR = -30, /* js::AutoWrapperVector */ - WRAPPER = -31, /* js::AutoWrapperRooter */ - OBJOBJHASHMAP=-32, /* js::AutoObjectObjectHashMap */ - OBJU32HASHMAP=-33, /* js::AutoObjectUnsigned32HashMap */ - OBJHASHSET = -34, /* js::AutoObjectHashSet */ - JSONPARSER = -35 /* js::JSONParser */ + OBJECT = -8, /* js::AutoObjectRooter */ + ID = -9, /* js::AutoIdRooter */ + VALVECTOR = -10, /* js::AutoValueVector */ + DESCRIPTOR = -11, /* js::AutoPropertyDescriptorRooter */ + STRING = -12, /* js::AutoStringRooter */ + IDVECTOR = -13, /* js::AutoIdVector */ + OBJVECTOR = -14, /* js::AutoObjectVector */ + STRINGVECTOR =-15, /* js::AutoStringVector */ + SCRIPTVECTOR =-16, /* js::AutoScriptVector */ + NAMEVECTOR = -17, /* js::AutoNameVector */ + HASHABLEVALUE=-18, /* js::HashableValue */ + IONMASM = -19, /* js::ion::MacroAssembler */ + IONALLOC = -20, /* js::ion::AutoTempAllocatorRooter */ + WRAPVECTOR = -21, /* js::AutoWrapperVector */ + WRAPPER = -22, /* js::AutoWrapperRooter */ + OBJOBJHASHMAP=-23, /* js::AutoObjectObjectHashMap */ + OBJU32HASHMAP=-24, /* js::AutoObjectUnsigned32HashMap */ + OBJHASHSET = -25, /* js::AutoObjectHashSet */ + JSONPARSER = -26, /* js::JSONParser */ + CUSTOM = -27 /* js::CustomAutoRooter */ }; private: @@ -667,6 +661,35 @@ class AutoScriptVector : public AutoVectorRooter MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; +/* + * Cutsom rooting behavior for internal and external clients. + */ +class JS_PUBLIC_API(CustomAutoRooter) : private AutoGCRooter +{ + public: + explicit CustomAutoRooter(JSContext *cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : AutoGCRooter(cx, CUSTOM) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + } + + friend void AutoGCRooter::trace(JSTracer *trc); + + protected: + /* Supplied by derived class to trace roots. */ + virtual void trace(JSTracer *trc) = 0; + + /* Methods for trace() to call to mark roots, for external clients. */ + static void traceObject(JSTracer *trc, JSObject **thingp, const char *name); + static void traceScript(JSTracer *trc, JSScript **thingp, const char *name); + static void traceString(JSTracer *trc, JSString **thingp, const char *name); + static void traceId(JSTracer *trc, jsid *thingp, const char *name); + static void traceValue(JSTracer *trc, JS::Value *thingp, const char *name); + + private: + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + /* Returns true if |v| is considered an acceptable this-value. */ typedef bool (*IsAcceptableThis)(const Value &v); diff --git a/js/src/vm/ObjectImpl.h b/js/src/vm/ObjectImpl.h index 218ee2b69fb1..70888c4dd510 100644 --- a/js/src/vm/ObjectImpl.h +++ b/js/src/vm/ObjectImpl.h @@ -294,19 +294,24 @@ struct PropDesc { bool wrapInto(JSContext *cx, HandleObject obj, const jsid &id, jsid *wrappedId, PropDesc *wrappedDesc) const; - class AutoRooter : private AutoGCRooter + class AutoRooter : private JS::CustomAutoRooter { public: explicit AutoRooter(JSContext *cx, PropDesc *pd_ MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, PROPDESC), pd(pd_), skip(cx, pd_) + : CustomAutoRooter(cx), pd(pd_), skip(cx, pd_) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } - friend void AutoGCRooter::trace(JSTracer *trc); - private: + virtual void trace(JSTracer *trc) { + traceValue(trc, &pd->pd_, "PropDesc::AutoRooter pd"); + traceValue(trc, &pd->value_, "PropDesc::AutoRooter value"); + traceValue(trc, &pd->get_, "PropDesc::AutoRooter get"); + traceValue(trc, &pd->set_, "PropDesc::AutoRooter set"); + } + PropDesc *pd; SkipRoot skip; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER diff --git a/js/src/vm/RegExpStatics-inl.h b/js/src/vm/RegExpStatics-inl.h index ce43b6e4f379..d70fdf2dccec 100644 --- a/js/src/vm/RegExpStatics-inl.h +++ b/js/src/vm/RegExpStatics-inl.h @@ -150,20 +150,32 @@ class RegExpStatics /* PreserveRegExpStatics helpers. */ - class AutoRooter : private AutoGCRooter + class AutoRooter : private JS::CustomAutoRooter { public: explicit AutoRooter(JSContext *cx, RegExpStatics *statics_ MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, REGEXPSTATICS), statics(statics_), skip(cx, statics_) + : CustomAutoRooter(cx), statics(statics_), skip(cx, statics_) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } - friend void AutoGCRooter::trace(JSTracer *trc); - void trace(JSTracer *trc); - private: + virtual void trace(JSTracer *trc) { + if (statics->matchesInput) { + traceString(trc, reinterpret_cast(&statics->matchesInput), + "RegExpStatics::AutoRooter matchesInput"); + } + if (statics->lazySource) { + traceString(trc, reinterpret_cast(&statics->lazySource), + "RegExpStatics::AutoRooter lazySource"); + } + if (statics->pendingInput) { + traceString(trc, reinterpret_cast(&statics->pendingInput), + "RegExpStatics::AutoRooter pendingInput"); + } + } + RegExpStatics *statics; SkipRoot skip; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER diff --git a/js/src/vm/Shape.h b/js/src/vm/Shape.h index 7c03a0f29480..68e3841d6409 100644 --- a/js/src/vm/Shape.h +++ b/js/src/vm/Shape.h @@ -427,19 +427,26 @@ struct StackBaseShape static inline HashNumber hash(const StackBaseShape *lookup); static inline bool match(RawUnownedBaseShape key, const StackBaseShape *lookup); - class AutoRooter : private AutoGCRooter + class AutoRooter : private JS::CustomAutoRooter { public: explicit AutoRooter(JSContext *cx, const StackBaseShape *base_ MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, STACKBASESHAPE), base(base_), skip(cx, base_) + : CustomAutoRooter(cx), base(base_), skip(cx, base_) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } - friend void AutoGCRooter::trace(JSTracer *trc); - private: + virtual void trace(JSTracer *trc) { + if (base->parent) + traceObject(trc, (JSObject**)&base->parent, "StackBaseShape::AutoRooter parent"); + if ((base->flags & BaseShape::HAS_GETTER_OBJECT) && base->rawGetter) + traceObject(trc, (JSObject**)&base->rawGetter, "StackBaseShape::AutoRooter getter"); + if ((base->flags & BaseShape::HAS_SETTER_OBJECT) && base->rawSetter) + traceObject(trc, (JSObject**)&base->rawSetter, "StackBaseShape::AutoRooter setter"); + } + const StackBaseShape *base; SkipRoot skip; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER @@ -851,22 +858,27 @@ class Shape : public js::gc::Cell class AutoRooterGetterSetter { - class Inner : private AutoGCRooter + class Inner : private JS::CustomAutoRooter { public: Inner(JSContext *cx, uint8_t attrs, PropertyOp *pgetter_, StrictPropertyOp *psetter_) - : AutoGCRooter(cx, GETTERSETTER), attrs(attrs), - pgetter(pgetter_), psetter(psetter_), - getterRoot(cx, pgetter_), setterRoot(cx, psetter_) + : CustomAutoRooter(cx), attrs(attrs), + pgetter(pgetter_), psetter(psetter_), + getterRoot(cx, pgetter_), setterRoot(cx, psetter_) { JS_ASSERT_IF(attrs & JSPROP_GETTER, !IsPoisonedPtr(*pgetter)); JS_ASSERT_IF(attrs & JSPROP_SETTER, !IsPoisonedPtr(*psetter)); } - friend void AutoGCRooter::trace(JSTracer *trc); - private: + virtual void trace(JSTracer *trc) { + if ((attrs & JSPROP_GETTER) && *pgetter) + traceObject(trc, (JSObject**) pgetter, "AutoRooterGetterSetter getter"); + if ((attrs & JSPROP_SETTER) && *psetter) + traceObject(trc, (JSObject**) psetter, "AutoRooterGetterSetter setter"); + } + uint8_t attrs; PropertyOp *pgetter; StrictPropertyOp *psetter; @@ -883,8 +895,6 @@ class AutoRooterGetterSetter MOZ_GUARD_OBJECT_NOTIFIER_INIT; } - friend void AutoGCRooter::trace(JSTracer *trc); - private: mozilla::Maybe inner; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER @@ -1006,19 +1016,19 @@ struct StackShape inline HashNumber hash() const; - class AutoRooter : private AutoGCRooter + class AutoRooter : private JS::CustomAutoRooter { public: explicit AutoRooter(JSContext *cx, const StackShape *shape_ MOZ_GUARD_OBJECT_NOTIFIER_PARAM) - : AutoGCRooter(cx, STACKSHAPE), shape(shape_), skip(cx, shape_) + : CustomAutoRooter(cx), shape(shape_), skip(cx, shape_) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } - friend void AutoGCRooter::trace(JSTracer *trc); - private: + virtual void trace(JSTracer *trc); + const StackShape *shape; SkipRoot skip; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER From ec0f1b2bb311d1cfde7a29d654fcb0ea9aeb8de7 Mon Sep 17 00:00:00 2001 From: Aryeh Gregor Date: Thu, 21 Mar 2013 14:54:43 +0200 Subject: [PATCH 72/73] Bug 851916 part 1 - Update dom/imptests/webapps/ and harness; r=Ms2ger --HG-- rename : dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_ChildNode-remove.js => dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/ChildNode-remove.js --- .../tests/approved/test_interfaces.html.json | 143 +++++++--- .../tests/submissions/Ms2ger/Makefile.in | 6 - .../test_CharacterData-remove.html.json | 15 +- ...st_Document-getElementsByTagName.html.json | 5 +- .../Ms2ger/test_DocumentType-remove.html.json | 5 +- .../Ms2ger/test_Element-remove.html.json | 5 +- .../Ms2ger/test_Range-attributes.html.json | 3 - ...st_Range-commonAncestorContainer.html.json | 3 - .../Ms2ger/test_Range-comparePoint.html.json | 3 - .../Ms2ger/test_Range-detach.html.json | 3 - dom/imptests/idlharness.js | 115 ++++++-- dom/imptests/testharness.js | 255 +++++++++++++++--- dom/imptests/webapps.mozbuild | 2 +- .../tests/approved/test_interfaces.html | 235 +++++++--------- ...hildNode-remove.js => ChildNode-remove.js} | 0 .../tests/submissions/Ms2ger/Makefile.in | 4 +- ...test_DOMImplementation-createDocument.html | 93 ++++--- .../Ms2ger/test_Document-createElement.html | 19 +- .../Ms2ger/test_Document-createEvent.html | 2 +- .../test_Document-createTreeWalker.html | 42 +++ .../test_Document-getElementsByTagName.html | 61 ++++- .../submissions/Ms2ger/test_Event-type.html | 22 ++ .../Ms2ger/test_Node-isEqualNode.xhtml | 1 + .../Ms2ger/test_NodeFilter-constants.html | 1 - .../Ms2ger/test_Range-attributes.html | 10 +- .../test_Range-commonAncestorContainer.html | 4 +- .../Ms2ger/test_Range-comparePoint.html | 2 +- .../submissions/Ms2ger/test_Range-detach.html | 2 +- .../tests/submissions/Ms2ger/Makefile.in | 1 + .../Ms2ger/test_FormData-append.html | 14 + 30 files changed, 757 insertions(+), 319 deletions(-) delete mode 100644 dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-attributes.html.json delete mode 100644 dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-commonAncestorContainer.html.json delete mode 100644 dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-comparePoint.html.json delete mode 100644 dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-detach.html.json rename dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/{test_ChildNode-remove.js => ChildNode-remove.js} (100%) create mode 100644 dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Document-createTreeWalker.html create mode 100644 dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Event-type.html create mode 100644 dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_FormData-append.html diff --git a/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json b/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json index ec21e48426d5..fc7c5d1d4524 100644 --- a/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json +++ b/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json @@ -30,67 +30,130 @@ "DOMException exception: constant DATA_CLONE_ERR on exception interface prototype object": true, "DOMException exception: field code on exception interface prototype object": true, "DOMError interface: existence and properties of interface object": true, + "DOMError interface constructor": true, "DOMError interface: existence and properties of interface prototype object": true, "DOMError interface: existence and properties of interface prototype object's \"constructor\" property": true, "DOMError interface: attribute name": true, - "Event interface: document.createEvent(\"Event\") must inherit property \"timeStamp\" with the proper type (14)": true, - "Event interface: new Event(\"foo\") must inherit property \"timeStamp\" with the proper type (14)": true, + "Event interface: document.createEvent(\"Event\") must have own property \"isTrusted\"": true, + "Event interface: document.createEvent(\"Event\") must inherit property \"timeStamp\" with the proper type (15)": true, + "Event interface: new Event(\"foo\") must have own property \"isTrusted\"": true, + "Event interface: new Event(\"foo\") must inherit property \"timeStamp\" with the proper type (15)": true, "CustomEvent interface: existence and properties of interface object": true, "CustomEvent interface constructor": true, "CustomEvent interface: existence and properties of interface prototype object": true, "CustomEvent interface: existence and properties of interface prototype object's \"constructor\" property": true, "CustomEvent interface: attribute detail": true, "Stringification of new CustomEvent(\"foo\")": "debug", - "Event interface: new CustomEvent(\"foo\") must inherit property \"timeStamp\" with the proper type (14)": true, + "CustomEvent interface: calling initCustomEvent(DOMString,boolean,boolean,any) on new CustomEvent(\"foo\") with too few arguments must throw TypeError": true, + "Event interface: new CustomEvent(\"foo\") must have own property \"isTrusted\"": true, + "Event interface: new CustomEvent(\"foo\") must inherit property \"timeStamp\" with the proper type (15)": true, "Event interface: calling initEvent(DOMString,boolean,boolean) on new CustomEvent(\"foo\") with too few arguments must throw TypeError": true, - "EventListener interface: existence and properties of interface object": true, - "EventListener interface: existence and properties of interface prototype object": true, - "EventListener interface: existence and properties of interface prototype object's \"constructor\" property": true, - "EventListener interface: operation handleEvent(Event)": true, - "MutationCallback interface: existence and properties of interface object": true, - "MutationCallback interface: existence and properties of interface prototype object": true, - "MutationCallback interface: existence and properties of interface prototype object's \"constructor\" property": true, - "MutationCallback interface: operation handleEvent(MutationRecord,MutationObserver)": true, + "MutationObserver interface: operation observe(Node,MutationObserverInit)": true, + "Document interface: attribute children": true, + "Document interface: attribute firstElementChild": true, + "Document interface: attribute lastElementChild": true, + "Document interface: attribute childElementCount": true, + "Document interface: operation prepend(union)": true, + "Document interface: operation append(union)": true, + "Document interface: xmlDoc must inherit property \"children\" with the proper type (24)": true, + "Document interface: xmlDoc must inherit property \"firstElementChild\" with the proper type (25)": true, + "Document interface: xmlDoc must inherit property \"lastElementChild\" with the proper type (26)": true, + "Document interface: xmlDoc must inherit property \"childElementCount\" with the proper type (27)": true, + "Document interface: xmlDoc must inherit property \"prepend\" with the proper type (28)": true, + "Document interface: calling prepend(union) on xmlDoc with too few arguments must throw TypeError": true, + "Document interface: xmlDoc must inherit property \"append\" with the proper type (29)": true, + "Document interface: calling append(union) on xmlDoc with too few arguments must throw TypeError": true, + "DOMImplementation interface: operation createHTMLDocument(DOMString)": true, + "DocumentFragment interface: attribute children": true, + "DocumentFragment interface: attribute firstElementChild": true, + "DocumentFragment interface: attribute lastElementChild": true, + "DocumentFragment interface: attribute childElementCount": true, + "DocumentFragment interface: operation prepend(union)": true, + "DocumentFragment interface: operation append(union)": true, + "DocumentFragment interface: document.createDocumentFragment() must inherit property \"children\" with the proper type (0)": true, + "DocumentFragment interface: document.createDocumentFragment() must inherit property \"firstElementChild\" with the proper type (1)": true, + "DocumentFragment interface: document.createDocumentFragment() must inherit property \"lastElementChild\" with the proper type (2)": true, + "DocumentFragment interface: document.createDocumentFragment() must inherit property \"childElementCount\" with the proper type (3)": true, + "DocumentFragment interface: document.createDocumentFragment() must inherit property \"prepend\" with the proper type (4)": true, + "DocumentFragment interface: calling prepend(union) on document.createDocumentFragment() with too few arguments must throw TypeError": true, + "DocumentFragment interface: document.createDocumentFragment() must inherit property \"append\" with the proper type (5)": true, + "DocumentFragment interface: calling append(union) on document.createDocumentFragment() with too few arguments must throw TypeError": true, + "DocumentType interface: attribute previousElementSibling": true, + "DocumentType interface: attribute nextElementSibling": true, + "DocumentType interface: operation before(union)": true, + "DocumentType interface: operation after(union)": true, + "DocumentType interface: operation replace(union)": true, "DocumentType interface: operation remove()": true, - "DocumentType interface: document.doctype must inherit property \"remove\" with the proper type (3)": true, + "DocumentType interface: document.doctype must inherit property \"previousElementSibling\" with the proper type (3)": true, + "DocumentType interface: document.doctype must inherit property \"nextElementSibling\" with the proper type (4)": true, + "DocumentType interface: document.doctype must inherit property \"before\" with the proper type (5)": true, + "DocumentType interface: calling before(union) on document.doctype with too few arguments must throw TypeError": true, + "DocumentType interface: document.doctype must inherit property \"after\" with the proper type (6)": true, + "DocumentType interface: calling after(union) on document.doctype with too few arguments must throw TypeError": true, + "DocumentType interface: document.doctype must inherit property \"replace\" with the proper type (7)": true, + "DocumentType interface: calling replace(union) on document.doctype with too few arguments must throw TypeError": true, + "DocumentType interface: document.doctype must inherit property \"remove\" with the proper type (8)": true, "Element interface: attribute namespaceURI": true, "Element interface: attribute prefix": true, "Element interface: attribute localName": true, "Element interface: attribute className": true, + "Element interface: operation prepend(union)": true, + "Element interface: operation append(union)": true, + "Element interface: operation before(union)": true, + "Element interface: operation after(union)": true, + "Element interface: operation replace(union)": true, "Element interface: operation remove()": true, "Element interface: element must inherit property \"className\" with the proper type (5)": true, - "Element interface: element must inherit property \"remove\" with the proper type (25)": true, + "Element interface: element must inherit property \"prepend\" with the proper type (23)": true, + "Element interface: calling prepend(union) on element with too few arguments must throw TypeError": true, + "Element interface: element must inherit property \"append\" with the proper type (24)": true, + "Element interface: calling append(union) on element with too few arguments must throw TypeError": true, + "Element interface: element must inherit property \"before\" with the proper type (27)": true, + "Element interface: calling before(union) on element with too few arguments must throw TypeError": true, + "Element interface: element must inherit property \"after\" with the proper type (28)": true, + "Element interface: calling after(union) on element with too few arguments must throw TypeError": true, + "Element interface: element must inherit property \"replace\" with the proper type (29)": true, + "Element interface: calling replace(union) on element with too few arguments must throw TypeError": true, + "Element interface: element must inherit property \"remove\" with the proper type (30)": true, "Attr interface: existence and properties of interface object": true, "Attr interface: existence and properties of interface prototype object": true, "Attr interface: existence and properties of interface prototype object's \"constructor\" property": true, - "Attr interface: attribute name": true, "Attr interface: attribute value": true, + "Attr interface: attribute name": true, "Stringification of document.querySelector(\"[id]\").attributes[0]": "debug", + "CharacterData interface: attribute previousElementSibling": true, + "CharacterData interface: attribute nextElementSibling": true, + "CharacterData interface: operation before(union)": true, + "CharacterData interface: operation after(union)": true, + "CharacterData interface: operation replace(union)": true, "CharacterData interface: operation remove()": true, - "CharacterData interface: document.createTextNode(\"abc\") must inherit property \"remove\" with the proper type (7)": true, - "CharacterData interface: xmlDoc.createProcessingInstruction(\"abc\", \"def\") must inherit property \"remove\" with the proper type (7)": true, - "CharacterData interface: document.createComment(\"abc\") must inherit property \"remove\" with the proper type (7)": true, - "NodeFilter interface: existence and properties of interface prototype object": true, - "NodeFilter interface: existence and properties of interface prototype object's \"constructor\" property": true, - "NodeFilter interface: constant FILTER_ACCEPT on interface prototype object": true, - "NodeFilter interface: constant FILTER_REJECT on interface prototype object": true, - "NodeFilter interface: constant FILTER_SKIP on interface prototype object": true, - "NodeFilter interface: constant SHOW_ALL on interface prototype object": true, - "NodeFilter interface: constant SHOW_ELEMENT on interface prototype object": true, - "NodeFilter interface: constant SHOW_ATTRIBUTE on interface prototype object": true, - "NodeFilter interface: constant SHOW_TEXT on interface prototype object": true, - "NodeFilter interface: constant SHOW_CDATA_SECTION on interface prototype object": true, - "NodeFilter interface: constant SHOW_ENTITY_REFERENCE on interface prototype object": true, - "NodeFilter interface: constant SHOW_ENTITY on interface prototype object": true, - "NodeFilter interface: constant SHOW_PROCESSING_INSTRUCTION on interface prototype object": true, - "NodeFilter interface: constant SHOW_COMMENT on interface prototype object": true, - "NodeFilter interface: constant SHOW_DOCUMENT on interface prototype object": true, - "NodeFilter interface: constant SHOW_DOCUMENT_TYPE on interface prototype object": true, - "NodeFilter interface: constant SHOW_DOCUMENT_FRAGMENT on interface prototype object": true, - "NodeFilter interface: constant SHOW_NOTATION on interface prototype object": true, - "NodeFilter interface: operation acceptNode(Node)": true, - "DOMStringList interface: existence and properties of interface object": true, - "DOMStringList interface: existence and properties of interface prototype object": true, - "DOMStringList interface: existence and properties of interface prototype object's \"constructor\" property": true, - "DOMStringList interface: attribute length": true + "CharacterData interface: document.createTextNode(\"abc\") must inherit property \"previousElementSibling\" with the proper type (7)": true, + "CharacterData interface: document.createTextNode(\"abc\") must inherit property \"nextElementSibling\" with the proper type (8)": true, + "CharacterData interface: document.createTextNode(\"abc\") must inherit property \"before\" with the proper type (9)": true, + "CharacterData interface: calling before(union) on document.createTextNode(\"abc\") with too few arguments must throw TypeError": true, + "CharacterData interface: document.createTextNode(\"abc\") must inherit property \"after\" with the proper type (10)": true, + "CharacterData interface: calling after(union) on document.createTextNode(\"abc\") with too few arguments must throw TypeError": true, + "CharacterData interface: document.createTextNode(\"abc\") must inherit property \"replace\" with the proper type (11)": true, + "CharacterData interface: calling replace(union) on document.createTextNode(\"abc\") with too few arguments must throw TypeError": true, + "CharacterData interface: document.createTextNode(\"abc\") must inherit property \"remove\" with the proper type (12)": true, + "CharacterData interface: xmlDoc.createProcessingInstruction(\"abc\", \"def\") must inherit property \"previousElementSibling\" with the proper type (7)": true, + "CharacterData interface: xmlDoc.createProcessingInstruction(\"abc\", \"def\") must inherit property \"nextElementSibling\" with the proper type (8)": true, + "CharacterData interface: xmlDoc.createProcessingInstruction(\"abc\", \"def\") must inherit property \"before\" with the proper type (9)": true, + "CharacterData interface: calling before(union) on xmlDoc.createProcessingInstruction(\"abc\", \"def\") with too few arguments must throw TypeError": true, + "CharacterData interface: xmlDoc.createProcessingInstruction(\"abc\", \"def\") must inherit property \"after\" with the proper type (10)": true, + "CharacterData interface: calling after(union) on xmlDoc.createProcessingInstruction(\"abc\", \"def\") with too few arguments must throw TypeError": true, + "CharacterData interface: xmlDoc.createProcessingInstruction(\"abc\", \"def\") must inherit property \"replace\" with the proper type (11)": true, + "CharacterData interface: calling replace(union) on xmlDoc.createProcessingInstruction(\"abc\", \"def\") with too few arguments must throw TypeError": true, + "CharacterData interface: xmlDoc.createProcessingInstruction(\"abc\", \"def\") must inherit property \"remove\" with the proper type (12)": true, + "CharacterData interface: document.createComment(\"abc\") must inherit property \"previousElementSibling\" with the proper type (7)": true, + "CharacterData interface: document.createComment(\"abc\") must inherit property \"nextElementSibling\" with the proper type (8)": true, + "CharacterData interface: document.createComment(\"abc\") must inherit property \"before\" with the proper type (9)": true, + "CharacterData interface: calling before(union) on document.createComment(\"abc\") with too few arguments must throw TypeError": true, + "CharacterData interface: document.createComment(\"abc\") must inherit property \"after\" with the proper type (10)": true, + "CharacterData interface: calling after(union) on document.createComment(\"abc\") with too few arguments must throw TypeError": true, + "CharacterData interface: document.createComment(\"abc\") must inherit property \"replace\" with the proper type (11)": true, + "CharacterData interface: calling replace(union) on document.createComment(\"abc\") with too few arguments must throw TypeError": true, + "CharacterData interface: document.createComment(\"abc\") must inherit property \"remove\" with the proper type (12)": true, + "NodeFilter interface: existence and properties of interface object": true, + "NodeList interface: existence and properties of interface prototype object": true } diff --git a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/Makefile.in b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/Makefile.in index 3bb1d87b54ae..324622368dc4 100644 --- a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/Makefile.in +++ b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/Makefile.in @@ -11,18 +11,12 @@ include $(DEPTH)/config/autoconf.mk MOCHITEST_FILES := \ test_CharacterData-remove.html.json \ - test_DOMImplementation-createDocument.html.json \ test_DOMImplementation-createHTMLDocument.html.json \ test_Document-createElementNS.html.json \ test_Document-getElementsByTagName.html.json \ test_DocumentType-remove.html.json \ test_Element-remove.html.json \ test_Node-isEqualNode.xhtml.json \ - test_NodeFilter-constants.html.json \ - test_Range-attributes.html.json \ - test_Range-commonAncestorContainer.html.json \ - test_Range-comparePoint.html.json \ - test_Range-detach.html.json \ test_attributes.html.json \ test_case.html.json \ test_historical.html.json \ diff --git a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_CharacterData-remove.html.json b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_CharacterData-remove.html.json index 6051ea02cd65..cc52c3168475 100644 --- a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_CharacterData-remove.html.json +++ b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_CharacterData-remove.html.json @@ -1,5 +1,14 @@ { - "CharacterData.remove": true, - "CharacterData.remove 1": true, - "CharacterData.remove 2": true + "text should support remove()": true, + "remove() should work if text doesn't have a parent": true, + "remove() should work if text does have a parent": true, + "remove() should work if text does have a parent and siblings": true, + "comment should support remove()": true, + "remove() should work if comment doesn't have a parent": true, + "remove() should work if comment does have a parent": true, + "remove() should work if comment does have a parent and siblings": true, + "PI should support remove()": true, + "remove() should work if PI doesn't have a parent": true, + "remove() should work if PI does have a parent": true, + "remove() should work if PI does have a parent and siblings": true } diff --git a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Document-getElementsByTagName.html.json b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Document-getElementsByTagName.html.json index d933e3c7cce8..53be448185a3 100644 --- a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Document-getElementsByTagName.html.json +++ b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Document-getElementsByTagName.html.json @@ -1,4 +1,5 @@ { - "Document.getElementsByTagName 2": true, - "Document.getElementsByTagName 3": true + "Shouldn't be able to set unsigned properties on a HTMLCollection (strict mode)": true, + "Document.getElementsByTagName 1": true, + "Document.getElementsByTagName 2": true } diff --git a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_DocumentType-remove.html.json b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_DocumentType-remove.html.json index 39a5c3433e3c..01c853f2f2a5 100644 --- a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_DocumentType-remove.html.json +++ b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_DocumentType-remove.html.json @@ -1,3 +1,6 @@ { - "DocumentType.remove": true + "doctype should support remove()": true, + "remove() should work if doctype doesn't have a parent": true, + "remove() should work if doctype does have a parent": true, + "remove() should work if doctype does have a parent and siblings": true } diff --git a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Element-remove.html.json b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Element-remove.html.json index 8a6b528fb360..fa30f6dfa773 100644 --- a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Element-remove.html.json +++ b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Element-remove.html.json @@ -1,3 +1,6 @@ { - "Element.remove": true + "element should support remove()": true, + "remove() should work if element doesn't have a parent": true, + "remove() should work if element does have a parent": true, + "remove() should work if element does have a parent and siblings": true } diff --git a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-attributes.html.json b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-attributes.html.json deleted file mode 100644 index 5e4e20839949..000000000000 --- a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-attributes.html.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Range attributes": true -} diff --git a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-commonAncestorContainer.html.json b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-commonAncestorContainer.html.json deleted file mode 100644 index a8609e51b12d..000000000000 --- a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-commonAncestorContainer.html.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Detached Range": true -} diff --git a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-comparePoint.html.json b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-comparePoint.html.json deleted file mode 100644 index d7d8b443a70e..000000000000 --- a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-comparePoint.html.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Range.comparePoint": true -} diff --git a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-detach.html.json b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-detach.html.json deleted file mode 100644 index 61c49bd75bc5..000000000000 --- a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-detach.html.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Range.detach": true -} diff --git a/dom/imptests/idlharness.js b/dom/imptests/idlharness.js index b627b40f8fca..312d35a88af0 100644 --- a/dom/imptests/idlharness.js +++ b/dom/imptests/idlharness.js @@ -303,6 +303,10 @@ IdlArray.prototype.internal_add_idls = function(parsed_idls) // TODO break; + case "callback": + // TODO + break; + default: throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported"; } @@ -1029,18 +1033,42 @@ IdlException.prototype.test_object = function(desc) /// IdlInterface /// function IdlInterface(obj) { IdlExceptionOrInterface.call(this, obj); } IdlInterface.prototype = Object.create(IdlExceptionOrInterface.prototype); +IdlInterface.prototype.is_callback = function() +//@{ +{ + return this.has_extended_attribute("Callback"); +} +//@} + +IdlInterface.prototype.has_constants = function() +//@{ +{ + return this.members.some(function(member) { + return member.type === "const"; + }); +} +//@} + IdlInterface.prototype.test_self = function() //@{ { test(function() { - // "For every interface that is not declared with the - // [NoInterfaceObject] extended attribute, a corresponding property - // must exist on the interface’s relevant namespace object. The name of - // the property is the identifier of the interface, and its value is an - // object called the interface object. The property has the attributes - // { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true - // }." + // This function tests WebIDL as of 2012-11-28. + + // "For every interface that: + // * is a callback interface that has constants declared on it, or + // * is a non-callback interface that is not declared with the + // [NoInterfaceObject] extended attribute, + // a corresponding property MUST exist on the ECMAScript global object. + // The name of the property is the identifier of the interface, and its + // value is an object called the interface object. + // The property has the attributes { [[Writable]]: true, + // [[Enumerable]]: false, [[Configurable]]: true }." + if (this.is_callback() && !this.has_constants()) { + return; + } + // TODO: Should we test here that the property is actually writable // etc., or trust getOwnPropertyDescriptor? assert_own_property(window, this.name, @@ -1052,43 +1080,53 @@ IdlInterface.prototype.test_self = function() assert_false(desc.enumerable, "window's property " + format_value(this.name) + " is enumerable"); assert_true(desc.configurable, "window's property " + format_value(this.name) + " is not configurable"); - // "Interface objects are always function objects." + if (this.is_callback()) { + // "The internal [[Prototype]] property of an interface object for + // a callback interface MUST be the Object.prototype object." + assert_equals(Object.getPrototypeOf(window[this.name]), Object.prototype, + "prototype of window's property " + format_value(this.name) + " is not Object.prototype"); + + return; + } + + // "The interface object for a given non-callback interface is a + // function object." // "If an object is defined to be a function object, then it has // characteristics as follows:" - // "Its [[Prototype]] internal property is the Function prototype - // object." - // Note: This doesn't match browsers as of December 2011, see - // http://www.w3.org/Bugs/Public/show_bug.cgi?id=14813 + + // "* Its [[Prototype]] internal property is the Function prototype + // object." assert_equals(Object.getPrototypeOf(window[this.name]), Function.prototype, "prototype of window's property " + format_value(this.name) + " is not Function.prototype"); - // "Its [[Get]] internal property is set as described in ECMA-262 - // section 15.3.5.4." + + // "* Its [[Get]] internal property is set as described in ECMA-262 + // section 15.3.5.4." // Not much to test for this. - // "Its [[Construct]] internal property is set as described in ECMA-262 - // section 13.2.2." + + // "* Its [[Construct]] internal property is set as described in + // ECMA-262 section 13.2.2." // Tested below if no constructor is defined. TODO: test constructors // if defined. - // "Its [[HasInstance]] internal property is set as described in - // ECMA-262 section 15.3.5.3, unless otherwise specified." + + // "* Its [[HasInstance]] internal property is set as described in + // ECMA-262 section 15.3.5.3, unless otherwise specified." // TODO - // "Its [[Class]] internal property is “Function”." + + // "* Its [[NativeBrand]] internal property is “Function”." // String() returns something implementation-dependent, because it calls // Function#toString. assert_class_string(window[this.name], "Function", "class string of " + this.name); - if (!this.has_extended_attribute("Constructor")) - { + if (!this.has_extended_attribute("Constructor")) { // "The internal [[Call]] method of the interface object behaves as // follows . . . // // "If I was not declared with a [Constructor] extended attribute, // then throw a TypeError." - assert_throws(new TypeError(), function() - { + assert_throws(new TypeError(), function() { window[this.name](); }.bind(this), "interface object didn't throw TypeError when called as a function"); - assert_throws(new TypeError(), function() - { + assert_throws(new TypeError(), function() { new window[this.name](); }.bind(this), "interface object didn't throw TypeError when called as a constructor"); } @@ -1138,6 +1176,12 @@ IdlInterface.prototype.test_self = function() assert_own_property(window, this.name, "window does not have own property " + format_value(this.name)); + if (this.has_extended_attribute("Callback")) { + assert_false("prototype" in window[this.name], + this.name + ' should not have a "prototype" property'); + return; + } + // "The interface object must also have a property named “prototype” // with attributes { [[Writable]]: false, [[Enumerable]]: false, // [[Configurable]]: false } whose value is an object called the @@ -1219,6 +1263,13 @@ IdlInterface.prototype.test_self = function() { assert_own_property(window, this.name, "window does not have own property " + format_value(this.name)); + + if (this.has_extended_attribute("Callback")) { + assert_false("prototype" in window[this.name], + this.name + ' should not have a "prototype" property'); + return; + } + assert_own_property(window[this.name], "prototype", 'interface "' + this.name + '" does not have own property "prototype"'); @@ -1282,6 +1333,13 @@ IdlInterface.prototype.test_members = function() { assert_own_property(window, this.name, "window does not have own property " + format_value(this.name)); + + if (this.has_extended_attribute("Callback")) { + assert_false("prototype" in window[this.name], + this.name + ' should not have a "prototype" property'); + return; + } + assert_own_property(window[this.name], "prototype", 'interface "' + this.name + '" does not have own property "prototype"'); @@ -1331,6 +1389,13 @@ IdlInterface.prototype.test_members = function() { assert_own_property(window, this.name, "window does not have own property " + format_value(this.name)); + + if (this.has_extended_attribute("Callback")) { + assert_false("prototype" in window[this.name], + this.name + ' should not have a "prototype" property'); + return; + } + assert_own_property(window[this.name], "prototype", 'interface "' + this.name + '" does not have own property "prototype"'); diff --git a/dom/imptests/testharness.js b/dom/imptests/testharness.js index 4568983dcc86..4489782f40dc 100644 --- a/dom/imptests/testharness.js +++ b/dom/imptests/testharness.js @@ -71,6 +71,9 @@ policies and contribution forms [3]. * author - Name and contact information for the author of the test in the * format: "Name " or "Name http://contact/url" * + * flags - space separated list of test flags in addition to any present in + * the head metadata + * * == Asynchronous Tests == * * Testing asynchronous features is somewhat more complex since the result of @@ -94,6 +97,19 @@ policies and contribution forms [3]. * * t.done(); * + * As a convenience, async_test can also takes a function as first argument. + * This function is called with the test object as both its `this` object and + * first argument. The above example can be rewritten as: + * + * async_test(function(t) { + * object.some_event = function() { + * t.step(function (){assert_true(true); t.done();}); + * }; + * }, "Simple async test"); + * + * which avoids cluttering the global scope with references to async + * tests instances. + * * The properties argument is identical to that for test(). * * In many cases it is convenient to run a step in response to an event or a @@ -222,7 +238,7 @@ policies and contribution forms [3]. * * In order to collect the results of multiple pages containing tests, the test * harness will, when loaded in a nested browsing context, attempt to call - * certain functions in each ancestor browsing context: + * certain functions in each ancestor and opener browsing context: * * start - start_callback * result - result_callback @@ -231,6 +247,22 @@ policies and contribution forms [3]. * These are given the same arguments as the corresponding internal callbacks * described above. * + * == External API through cross-document messaging == + * + * Where supported, the test harness will also send messages using + * cross-document messaging to each ancestor and opener browsing context. Since + * it uses the wildcard keyword (*), cross-origin communication is enabled and + * script on different origins can collect the results. + * + * This API follows similar conventions as those described above only slightly + * modified to accommodate message event API. Each message is sent by the harness + * is passed a single vanilla object, available as the `data` property of the + * event object. These objects are structures as follows: + * + * start - { type: "start" } + * result - { type: "result", test: Test } + * complete - { type: "complete", tests: [Test, ...], status: TestsStatus } + * * == List of assertions == * * assert_true(actual, description) @@ -376,11 +408,19 @@ policies and contribution forms [3]. } } - function async_test(name, properties) + function async_test(func, name, properties) { + if (typeof func !== "function") { + properties = name; + name = func; + func = null; + } var test_name = name ? name : next_default_name(); properties = properties ? properties : {}; var test_obj = new Test(test_name, properties); + if (func) { + test_obj.step(func, test_obj, test_obj); + } return test_obj; } @@ -652,7 +692,7 @@ policies and contribution forms [3]. } else { - assert(actual[p] === expected[p], "assert_object_equals", description, + assert(same_value(actual[p], expected[p]), "assert_object_equals", description, "property ${p} expected ${expected} got ${actual}", {p:p, expected:expected, actual:actual}); } @@ -683,7 +723,7 @@ policies and contribution forms [3]. "property ${i}, property expected to be $expected but was $actual", {i:i, expected:expected.hasOwnProperty(i) ? "present" : "missing", actual:actual.hasOwnProperty(i) ? "present" : "missing"}); - assert(expected[i] === actual[i], + assert(same_value(expected[i], actual[i]), "assert_array_equals", description, "property ${i}, expected ${expected} but got ${actual}", {i:i, expected:expected[i], actual:actual[i]}); @@ -777,7 +817,7 @@ policies and contribution forms [3]. //Note that this can have side effects in the case where //the property has PutForwards object[property_name] = initial_value + "a"; //XXX use some other value here? - assert(object[property_name] === initial_value, + assert(same_value(object[property_name], initial_value), "assert_readonly", description, "changing property ${p} succeeded", {p:property_name}); @@ -838,7 +878,7 @@ policies and contribution forms [3]. QUOTA_EXCEEDED_ERR: 'QuotaExceededError', TIMEOUT_ERR: 'TimeoutError', INVALID_NODE_TYPE_ERR: 'InvalidNodeTypeError', - DATA_CLONE_ERR: 'DataCloneError', + DATA_CLONE_ERR: 'DataCloneError' }; var name = code in code_name_map ? code_name_map[code] : code; @@ -871,7 +911,7 @@ policies and contribution forms [3]. DataError: 0, TransactionInactiveError: 0, ReadOnlyError: 0, - VersionError: 0, + VersionError: 0 }; if (!(name in name_code_map)) @@ -954,13 +994,29 @@ policies and contribution forms [3]. tests.push(this); } - Test.prototype = { + Test.statuses = { PASS:0, FAIL:1, TIMEOUT:2, NOTRUN:3 }; + Test.prototype = merge({}, Test.statuses); + + Test.prototype.structured_clone = function() + { + if(!this._structured_clone) + { + var msg = this.message; + msg = msg ? String(msg) : msg; + this._structured_clone = merge({ + name:String(this.name), + status:this.status, + message:msg + }, Test.statuses); + } + return this._structured_clone; + }; Test.prototype.step = function(func, this_obj) { @@ -1085,12 +1141,29 @@ policies and contribution forms [3]. this.status = null; this.message = null; } - TestsStatus.prototype = { + + TestsStatus.statuses = { OK:0, ERROR:1, TIMEOUT:2 }; + TestsStatus.prototype = merge({}, TestsStatus.statuses); + + TestsStatus.prototype.structured_clone = function() + { + if(!this._structured_clone) + { + var msg = this.message; + msg = msg ? String(msg) : msg; + this._structured_clone = merge({ + status:this.status, + message:msg + }, TestsStatus.statuses); + } + return this._structured_clone; + }; + function Tests() { this.tests = []; @@ -1232,10 +1305,10 @@ policies and contribution forms [3]. { callback(this_obj.properties); }); - forEach(ancestor_windows(), - function(w) + forEach_windows( + function(w, is_same_origin) { - if(w.start_callback) + if(is_same_origin && w.start_callback) { try { @@ -1249,6 +1322,13 @@ policies and contribution forms [3]. } } } + if (supports_post_message(w) && w !== self) + { + w.postMessage({ + type: "start", + properties: this_obj.properties + }, "*"); + } }); }; @@ -1272,10 +1352,10 @@ policies and contribution forms [3]. callback(test, this_obj); }); - forEach(ancestor_windows(), - function(w) + forEach_windows( + function(w, is_same_origin) { - if(w.result_callback) + if(is_same_origin && w.result_callback) { try { @@ -1288,6 +1368,13 @@ policies and contribution forms [3]. } } } + if (supports_post_message(w) && w !== self) + { + w.postMessage({ + type: "result", + test: test.structured_clone() + }, "*"); + } }); this.processing_callbacks = false; if (this_obj.all_done()) @@ -1318,6 +1405,11 @@ policies and contribution forms [3]. { clearTimeout(this.timeout_id); var this_obj = this; + var tests = map(this_obj.tests, + function(test) + { + return test.structured_clone(); + }); if (this.status.status === null) { this.status.status = this.status.OK; @@ -1329,10 +1421,10 @@ policies and contribution forms [3]. callback(this_obj.tests, this_obj.status); }); - forEach(ancestor_windows(), - function(w) + forEach_windows( + function(w, is_same_origin) { - if(w.completion_callback) + if(is_same_origin && w.completion_callback) { try { @@ -1346,6 +1438,14 @@ policies and contribution forms [3]. } } } + if (supports_post_message(w) && w !== self) + { + w.postMessage({ + type: "complete", + tests: tests, + status: this_obj.status.structured_clone() + }, "*"); + } }); }; @@ -1382,7 +1482,7 @@ policies and contribution forms [3]. */ function Output() { - this.output_document = null; + this.output_document = document; this.output_node = null; this.done_count = 0; this.enabled = settings.output; @@ -1939,22 +2039,107 @@ policies and contribution forms [3]. target[components[components.length - 1]] = object; } - function ancestor_windows() { - //Get the windows [self ... top] as an array - if ("result_cache" in ancestor_windows) - { - return ancestor_windows.result_cache; - } - var rv = [self]; - var w = self; - while (w != w.parent) - { - w = w.parent; - rv.push(w); - } - ancestor_windows.result_cache = rv; - return rv; - } + function forEach_windows(callback) { + // Iterate of the the windows [self ... top, opener]. The callback is passed + // two objects, the first one is the windows object itself, the second one + // is a boolean indicating whether or not its on the same origin as the + // current window. + var cache = forEach_windows.result_cache; + if (!cache) { + cache = [[self, true]]; + var w = self; + var i = 0; + var so; + var origins = location.ancestorOrigins; + while (w != w.parent) + { + w = w.parent; + // In WebKit, calls to parent windows' properties that aren't on the same + // origin cause an error message to be displayed in the error console but + // don't throw an exception. This is a deviation from the current HTML5 + // spec. See: https://bugs.webkit.org/show_bug.cgi?id=43504 + // The problem with WebKit's behavior is that it pollutes the error console + // with error messages that can't be caught. + // + // This issue can be mitigated by relying on the (for now) proprietary + // `location.ancestorOrigins` property which returns an ordered list of + // the origins of enclosing windows. See: + // http://trac.webkit.org/changeset/113945. + if(origins) { + so = (location.origin == origins[i]); + } + else + { + so = is_same_origin(w); + } + cache.push([w, so]); + i++; + } + w = window.opener; + if(w) + { + // window.opener isn't included in the `location.ancestorOrigins` prop. + // We'll just have to deal with a simple check and an error msg on WebKit + // browsers in this case. + cache.push([w, is_same_origin(w)]); + } + forEach_windows.result_cache = cache; + } + forEach(cache, + function(a) + { + callback.apply(null, a); + }); + } + + function is_same_origin(w) { + try { + 'random_prop' in w; + return true; + } catch(e) { + return false; + } + } + + function supports_post_message(w) + { + var supports; + var type; + // Given IE implements postMessage across nested iframes but not across + // windows or tabs, you can't infer cross-origin communication from the presence + // of postMessage on the current window object only. + // + // Touching the postMessage prop on a window can throw if the window is + // not from the same origin AND post message is not supported in that + // browser. So just doing an existence test here won't do, you also need + // to wrap it in a try..cacth block. + try + { + type = typeof w.postMessage; + if (type === "function") + { + supports = true; + } + // IE8 supports postMessage, but implements it as a host object which + // returns "object" as its `typeof`. + else if (type === "object") + { + supports = true; + } + // This is the case where postMessage isn't supported AND accessing a + // window property across origins does NOT throw (e.g. old Safari browser). + else + { + supports = false; + } + } + catch(e) { + // This is the case where postMessage isn't supported AND accessing a + // window property across origins throws (e.g. old Firefox browser). + supports = false; + } + return supports; + } })(); // vim: set expandtab shiftwidth=4 tabstop=4: diff --git a/dom/imptests/webapps.mozbuild b/dom/imptests/webapps.mozbuild index 2da26c9784f6..63da7a606ba4 100644 --- a/dom/imptests/webapps.mozbuild +++ b/dom/imptests/webapps.mozbuild @@ -1,4 +1,4 @@ -# THIS FILE IS AUTOGENERATED BY importTestsuite.py - DO NOT EDIT +# THIS FILE IS AUTOGENERATED BY importTestSuites.py - DO NOT EDIT DIRS += [ 'webapps/DOMCore/tests/approved', diff --git a/dom/imptests/webapps/DOMCore/tests/approved/test_interfaces.html b/dom/imptests/webapps/DOMCore/tests/approved/test_interfaces.html index 4c6bd2d330ae..8d2134f7421f 100644 --- a/dom/imptests/webapps/DOMCore/tests/approved/test_interfaces.html +++ b/dom/imptests/webapps/DOMCore/tests/approved/test_interfaces.html @@ -23,7 +23,7 @@ exception DOMException { const unsigned short NAMESPACE_ERR = 14; const unsigned short INVALID_ACCESS_ERR = 15; const unsigned short VALIDATION_ERR = 16; // historical - const unsigned short TYPE_MISMATCH_ERR = 17; + const unsigned short TYPE_MISMATCH_ERR = 17; // historical; use TypeError instead const unsigned short SECURITY_ERR = 18; const unsigned short NETWORK_ERR = 19; const unsigned short ABORT_ERR = 20; @@ -35,6 +35,7 @@ exception DOMException { unsigned short code; }; +[Constructor(DOMString name)] interface DOMError { readonly attribute DOMString name; }; @@ -45,6 +46,7 @@ interface Event { readonly attribute EventTarget? target; readonly attribute EventTarget? currentTarget; + const unsigned short NONE = 0; const unsigned short CAPTURING_PHASE = 1; const unsigned short AT_TARGET = 2; const unsigned short BUBBLING_PHASE = 3; @@ -58,29 +60,31 @@ interface Event { void preventDefault(); readonly attribute boolean defaultPrevented; - readonly attribute boolean isTrusted; + [Unforgeable] readonly attribute boolean isTrusted; readonly attribute DOMTimeStamp timeStamp; void initEvent(DOMString type, boolean bubbles, boolean cancelable); }; dictionary EventInit { - boolean bubbles; - boolean cancelable; + boolean bubbles = false; + boolean cancelable = false; }; [Constructor(DOMString type, optional CustomEventInit eventInitDict)] interface CustomEvent : Event { readonly attribute any detail; + + void initCustomEvent(DOMString type, boolean bubbles, boolean cancelable, any details); }; dictionary CustomEventInit : EventInit { - any detail; + any detail = null; }; interface EventTarget { - void addEventListener(DOMString type, EventListener? listener, optional boolean capture); - void removeEventListener(DOMString type, EventListener? listener, optional boolean capture); + void addEventListener(DOMString type, EventListener? callback, optional boolean capture); + void removeEventListener(DOMString type, EventListener? callback, optional boolean capture); boolean dispatchEvent(Event event); }; @@ -89,71 +93,63 @@ interface EventListener { void handleEvent(Event event); }; +[NoInterfaceObject] +interface ParentNode { + readonly attribute HTMLCollection children; + readonly attribute Element? firstElementChild; + readonly attribute Element? lastElementChild; + readonly attribute unsigned long childElementCount; + + void prepend((Node or DOMString)... nodes); + void append((Node or DOMString)... nodes); +}; +Document implements ParentNode; +DocumentFragment implements ParentNode; +Element implements ParentNode; + +[NoInterfaceObject] +interface ChildNode { + readonly attribute Element? previousElementSibling; + readonly attribute Element? nextElementSibling; + + void before((Node or DOMString)... nodes); + void after((Node or DOMString)... nodes); + void replace((Node or DOMString)... nodes); + void remove(); +}; +DocumentType implements ChildNode; +Element implements ChildNode; +CharacterData implements ChildNode; + [Constructor(MutationCallback callback)] interface MutationObserver { - void observe(Node target, optional MutationObserverInit options); + void observe(Node target, MutationObserverInit options); void disconnect(); + sequence takeRecords(); }; -[Callback] -interface MutationCallback { - void handleEvent(MutationRecord[] mutations, MutationObserver observer); -}; +callback MutationCallback = void (sequence mutations, MutationObserver observer); dictionary MutationObserverInit { - // Mutation types - boolean childList; // If true, mutations affecting node’s childNodes are included. - boolean attributes; // If true, mutations affecting element’s attributes are included. - boolean characterData; // If true, mutations affecting the value of CharacterData nodes are included. - // [Note: If none of the known mutation types is specified, an Error is thrown] - - // Subtree observation - boolean subtree; // If true, the observed set of nodes for this registration should include - // descendants of MutationTarget (behavior described below). - - // Old values - boolean attributeOldValue; - // If true, MutationRecords describing changes to attributes should - // contain the value of the attribute before the change. If true - // without attribute: true specified, an Error is thrown. - - boolean characterDataOldValue; - // If true, MutationRecords describing changes to - // CharacterData nodes should contain the value - // of the node before the change. If true without - // characterData: true, an Error is thrown. - - // Filtering - DOMString[] attributeFilter; - // If provided, only changes to attributes with localName equaling - // one of the provided strings will be delivered. If provided without - // attribute: true, an Error is thrown. + boolean childList = false; + boolean attributes = false; + boolean characterData = false; + boolean subtree = false; + boolean attributeOldValue = false; + boolean characterDataOldValue = false; + sequence attributeFilter; }; interface MutationRecord { - // Mutation type: one of 'childList', 'attributes', or 'characterData' readonly attribute DOMString type; - - // For childList and attributes, target is the owner node affected. - // For CharacterData, target is the node affected. readonly attribute Node target; - - // For type == 'childList', Sequence of added and removed nodes in this operation. readonly attribute NodeList addedNodes; readonly attribute NodeList removedNodes; - - // For type == 'childList', The siblings in childNodes immediately preceding following the first - // and last nodes added and/or removed. - readonly attribute Node previousSibling; - readonly attribute Node nextSibling; - - // For type == 'attribute', the name and namespaceURI of the attribute affected - readonly attribute DOMString attributeName; - readonly attribute DOMString attributeNamespace; - - // For type == 'attribute' or 'characterData', if requested, the value immediately - // preceding the mutation. - readonly attribute DOMString oldValue; + readonly attribute Node? previousSibling; + readonly attribute Node? nextSibling; + readonly attribute DOMString? attributeName; + readonly attribute DOMString? attributeNamespace; + readonly attribute DOMString? oldValue; }; interface Node : EventTarget { @@ -184,29 +180,30 @@ interface Node : EventTarget { readonly attribute Node? previousSibling; readonly attribute Node? nextSibling; - const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01; - const unsigned short DOCUMENT_POSITION_PRECEDING = 0x02; - const unsigned short DOCUMENT_POSITION_FOLLOWING = 0x04; - const unsigned short DOCUMENT_POSITION_CONTAINS = 0x08; - const unsigned short DOCUMENT_POSITION_CONTAINED_BY = 0x10; - const unsigned short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; // historical - unsigned short compareDocumentPosition(Node other); - boolean contains(Node? other); - attribute DOMString? nodeValue; attribute DOMString? textContent; - Node insertBefore(Node node, Node? child); - Node appendChild(Node node); - Node replaceChild(Node node, Node child); - Node removeChild(Node child); void normalize(); Node cloneNode(optional boolean deep); boolean isEqualNode(Node? node); - DOMString lookupPrefix(DOMString? namespace); - DOMString lookupNamespaceURI(DOMString? prefix); + const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01; + const unsigned short DOCUMENT_POSITION_PRECEDING = 0x02; + const unsigned short DOCUMENT_POSITION_FOLLOWING = 0x04; + const unsigned short DOCUMENT_POSITION_CONTAINS = 0x08; + const unsigned short DOCUMENT_POSITION_CONTAINED_BY = 0x10; + const unsigned short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; + unsigned short compareDocumentPosition(Node other); + boolean contains(Node? other); + + DOMString? lookupPrefix(DOMString? namespace); + DOMString? lookupNamespaceURI(DOMString? prefix); boolean isDefaultNamespace(DOMString? namespace); + + Node insertBefore(Node node, Node? child); + Node appendChild(Node node); + Node replaceChild(Node node, Node child); + Node removeChild(Node child); }; [Constructor] @@ -220,9 +217,9 @@ interface Document : Node { readonly attribute DocumentType? doctype; readonly attribute Element? documentElement; - NodeList getElementsByTagName(DOMString qualifiedName); - NodeList getElementsByTagNameNS(DOMString? namespace, DOMString localName); - NodeList getElementsByClassName(DOMString classNames); + HTMLCollection getElementsByTagName(DOMString localName); + HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName); + HTMLCollection getElementsByClassName(DOMString classNames); Element? getElementById(DOMString elementId); Element createElement(DOMString localName); @@ -235,47 +232,33 @@ interface Document : Node { Node importNode(Node node, optional boolean deep); Node adoptNode(Node node); - Event createEvent(DOMString eventInterfaceName); + Event createEvent(DOMString interface); Range createRange(); + // NodeFilter.SHOW_ALL = 0xFFFFFFFF NodeIterator createNodeIterator(Node root, optional unsigned long whatToShow, optional NodeFilter? filter); TreeWalker createTreeWalker(Node root, optional unsigned long whatToShow, optional NodeFilter? filter); - - // NEW - // Invalid WebIDL https://www.w3.org/Bugs/Public/show_bug.cgi?id=15346 - //void prepend((Node or DOMString)... nodes); - //void append((Node or DOMString)... nodes); }; interface XMLDocument : Document {}; interface DOMImplementation { - boolean hasFeature(DOMString feature, [TreatNullAs=EmptyString] DOMString version); - DocumentType createDocumentType(DOMString qualifiedName, DOMString publicId, DOMString systemId); - XMLDocument createDocument(DOMString? namespace, DOMString qualifiedName, DocumentType? doctype); - Document createHTMLDocument(DOMString title); + XMLDocument createDocument(DOMString? namespace, [TreatNullAs=EmptyString] DOMString qualifiedName, DocumentType? doctype); + Document createHTMLDocument(optional DOMString title); + + boolean hasFeature(DOMString feature, [TreatNullAs=EmptyString] DOMString version); }; +[Constructor] interface DocumentFragment : Node { - // NEW - // Invalid WebIDL https://www.w3.org/Bugs/Public/show_bug.cgi?id=15346 - //void prepend((Node or DOMString)... nodes); - //void append((Node or DOMString)... nodes); }; interface DocumentType : Node { readonly attribute DOMString name; readonly attribute DOMString publicId; readonly attribute DOMString systemId; - - // NEW - // Invalid WebIDL https://www.w3.org/Bugs/Public/show_bug.cgi?id=15346 - //void before((Node or DOMString)... nodes); - //void after((Node or DOMString)... nodes); - //void replace((Node or DOMString)... nodes); - void remove(); }; interface Element : Node { @@ -298,34 +281,18 @@ interface Element : Node { boolean hasAttribute(DOMString name); boolean hasAttributeNS(DOMString? namespace, DOMString localName); - NodeList getElementsByTagName(DOMString qualifiedName); - NodeList getElementsByTagNameNS(DOMString? namespace, DOMString localName); - NodeList getElementsByClassName(DOMString classNames); - - readonly attribute HTMLCollection children; - readonly attribute Element? firstElementChild; - readonly attribute Element? lastElementChild; - readonly attribute Element? previousElementSibling; - readonly attribute Element? nextElementSibling; - readonly attribute unsigned long childElementCount; - - // NEW - // Invalid WebIDL https://www.w3.org/Bugs/Public/show_bug.cgi?id=15346 - //void prepend((Node or DOMString)... nodes); - //void append((Node or DOMString)... nodes); - //void before((Node or DOMString)... nodes); - //void after((Node or DOMString)... nodes); - //void replace((Node or DOMString)... nodes); - void remove(); + HTMLCollection getElementsByTagName(DOMString localName); + HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName); + HTMLCollection getElementsByClassName(DOMString classNames); }; interface Attr { - readonly attribute DOMString name; + readonly attribute DOMString localName; attribute DOMString value; + readonly attribute DOMString name; readonly attribute DOMString? namespaceURI; readonly attribute DOMString? prefix; - readonly attribute DOMString localName; }; interface CharacterData : Node { @@ -336,15 +303,9 @@ interface CharacterData : Node { void insertData(unsigned long offset, DOMString data); void deleteData(unsigned long offset, unsigned long count); void replaceData(unsigned long offset, unsigned long count, DOMString data); - - // NEW - // Invalid WebIDL https://www.w3.org/Bugs/Public/show_bug.cgi?id=15346 - //void before((Node or DOMString)... nodes); - //void after((Node or DOMString)... nodes); - //void replace((Node or DOMString)... nodes); - void remove(); }; +[Constructor(optional DOMString data)] interface Text : CharacterData { Text splitText(unsigned long offset); readonly attribute DOMString wholeText; @@ -354,9 +315,11 @@ interface ProcessingInstruction : CharacterData { readonly attribute DOMString target; }; +[Constructor(optional DOMString data)] interface Comment : CharacterData { }; +[Constructor] interface Range { readonly attribute Node startContainer; readonly attribute unsigned long startOffset; @@ -428,8 +391,7 @@ interface TreeWalker { [Callback] interface NodeFilter { - - // Constants for acceptNode + // Constants for acceptNode() const unsigned short FILTER_ACCEPT = 1; const unsigned short FILTER_REJECT = 2; const unsigned short FILTER_SKIP = 3; @@ -452,6 +414,7 @@ interface NodeFilter { unsigned short acceptNode(Node node); }; +[ArrayClass] interface NodeList { getter Node? item(unsigned long index); readonly attribute unsigned long length; @@ -463,20 +426,14 @@ interface HTMLCollection { getter object? namedItem(DOMString name); // only returns Element }; -interface DOMStringList { - readonly attribute unsigned long length; - getter DOMString? item(unsigned long index); - boolean contains(DOMString string); -}; - interface DOMTokenList { readonly attribute unsigned long length; getter DOMString? item(unsigned long index); boolean contains(DOMString token); - void add(DOMString token); - void remove(DOMString token); - boolean toggle(DOMString token); - stringifier DOMString (); + void add(DOMString... tokens); + void remove(DOMString... tokens); + boolean toggle(DOMString token, optional boolean force); + stringifier; }; interface DOMSettableTokenList : DOMTokenList { @@ -509,8 +466,8 @@ idlArray.add_objects({ ProcessingInstruction: ['xmlDoc.createProcessingInstruction("abc", "def")'], Comment: ['document.createComment("abc")'], Range: ['document.createRange()', 'detachedRange'], - NodeIterator: ['document.createNodeIterator(document.body, NodeFilter.SHOW_ALL, null)'], - TreeWalker: ['document.createTreeWalker(document.body, NodeFilter.SHOW_ALL, null)'], + NodeIterator: ['document.createNodeIterator(document.body, NodeFilter.SHOW_ALL, null, false)'], + TreeWalker: ['document.createTreeWalker(document.body, NodeFilter.SHOW_ALL, null, false)'], NodeList: ['document.querySelectorAll("script")'], HTMLCollection: ['document.body.children'], DOMTokenList: ['document.body.classList'], diff --git a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_ChildNode-remove.js b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/ChildNode-remove.js similarity index 100% rename from dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_ChildNode-remove.js rename to dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/ChildNode-remove.js diff --git a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/Makefile.in b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/Makefile.in index 1d1a5905db5f..ad9a8f0de635 100644 --- a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/Makefile.in +++ b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/Makefile.in @@ -17,7 +17,6 @@ MOCHITEST_FILES := \ test_CharacterData-insertData.html \ test_CharacterData-remove.html \ test_CharacterData-replaceData.html \ - test_ChildNode-remove.js \ test_Document-adoptNode.html \ test_Document-createComment.html \ test_Document-createElement.html \ @@ -27,6 +26,7 @@ MOCHITEST_FILES := \ test_Document-createProcessingInstruction-literal-1.xhtml \ test_Document-createProcessingInstruction-literal-2.xhtml \ test_Document-createProcessingInstruction.xhtml \ + test_Document-createTreeWalker.html \ test_Document-getElementById.html \ test_Document-getElementsByTagName.html \ test_Document-getElementsByTagNameNS.html \ @@ -47,6 +47,7 @@ MOCHITEST_FILES := \ test_Event-defaultPrevented.html \ test_Event-initEvent.html \ test_EventTarget-dispatchEvent.html \ + test_Event-type.html \ test_historical.html \ test_interfaces.html \ test_Node-appendChild.html \ @@ -73,6 +74,7 @@ MOCHITEST_FILES := \ case.js \ creators.js \ constants.js \ + ChildNode-remove.js \ Document-createProcessingInstruction.js \ productions.js \ $(NULL) diff --git a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_DOMImplementation-createDocument.html b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_DOMImplementation-createDocument.html index 62a29d64a63f..9c27acb8eddb 100644 --- a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_DOMImplementation-createDocument.html +++ b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_DOMImplementation-createDocument.html @@ -18,7 +18,9 @@ test(function() { * the doctype argument * the expected exception, or null if none */ + [null, null, false, new TypeError()], [null, null, null, null], + [null, undefined, null, null], [null, "", null, null], [null, "foo", null, null], [null, "1foo", null, "INVALID_CHARACTER_ERR"], @@ -33,6 +35,22 @@ test(function() { [null, "xml:foo", null, "NAMESPACE_ERR"], [null, "xmlns:foo", null, "NAMESPACE_ERR"], [null, "xmlfoo:bar", null, "NAMESPACE_ERR"], + [undefined, null, undefined, null], + [undefined, undefined, undefined, null], + [undefined, "", undefined, null], + [undefined, "foo", undefined, null], + [undefined, "1foo", undefined, "INVALID_CHARACTER_ERR"], + [undefined, "f1oo", undefined, null], + [undefined, "foo1", undefined, null], + [undefined, ":foo", undefined, "NAMESPACE_ERR"], + [undefined, "f:oo", undefined, "NAMESPACE_ERR"], + [undefined, "foo:", undefined, "NAMESPACE_ERR"], + [undefined, "xml", undefined, null], + [undefined, "xmlns", undefined, "NAMESPACE_ERR"], + [undefined, "xmlfoo", undefined, null], + [undefined, "xml:foo", undefined, "NAMESPACE_ERR"], + [undefined, "xmlns:foo", undefined, "NAMESPACE_ERR"], + [undefined, "xmlfoo:bar", undefined, "NAMESPACE_ERR"], ["http://example.com/", null, null, null], ["http://example.com/", "", null, null], ["http://example.com/", "foo", null, null], @@ -108,36 +126,28 @@ test(function() { ["foo:", "xml:foo", null, "NAMESPACE_ERR"], ["foo:", "xmlns:foo", null, "NAMESPACE_ERR"], ["foo:", "xmlfoo:bar", null, null], + [null, null, document.implementation.createDocumentType("foo", "", ""), null], + [null, null, document.doctype, null], // This causes a horrible WebKit bug (now fixed in trunk). + [null, null, function() { + var foo = document.implementation.createDocumentType("foo", "", ""); + document.implementation.createDocument(null, null, foo); + return foo; + }(), null], // DOCTYPE already associated with a document. + [null, null, function() { + var bar = document.implementation.createDocument(null, null, null); + return bar.implementation.createDocumentType("bar", "", ""); + }(), null], // DOCTYPE created by a different implementation. + [null, null, function() { + var bar = document.implementation.createDocument(null, null, null); + var magic = bar.implementation.createDocumentType("bar", "", ""); + bar.implementation.createDocument(null, null, magic); + return magic; + }(), null], // DOCTYPE created by a different implementation and already associated with a document. + [null, "foo", document.implementation.createDocumentType("foo", "", ""), null], + ["foo", null, document.implementation.createDocumentType("foo", "", ""), null], + ["foo", "bar", document.implementation.createDocumentType("foo", "", ""), null], ] - try { // XXX merge?! - var tempTests = tests.concat([ - [null, null, document.implementation.createDocumentType("foo", "", ""), null], - [null, null, document.doctype, "WRONG_DOCUMENT_ERR"], // This causes a horrible WebKit bug (now fixed in trunk). - [null, null, function() { - var foo = document.implementation.createDocumentType("foo", "", ""); - document.implementation.createDocument(null, null, foo); - return foo; - }(), "WRONG_DOCUMENT_ERR"], // DOCTYPE already associated with a document. - [null, null, function() { - var bar = document.implementation.createDocument(null, null, null); - return bar.implementation.createDocumentType("bar", "", ""); - }(), null], // DOCTYPE created by a different implementation. - [null, null, function() { - var bar = document.implementation.createDocument(null, null, null); - var magic = bar.implementation.createDocumentType("bar", "", ""); - bar.implementation.createDocument(null, null, magic); - return magic; - }(), "WRONG_DOCUMENT_ERR"], // DOCTYPE created by a different implementation and already associated with a document. - [null, "foo", document.implementation.createDocumentType("foo", "", ""), null], - ["foo", null, document.implementation.createDocumentType("foo", "", ""), null], - ["foo", "bar", document.implementation.createDocumentType("foo", "", ""), null], - ]); - tests = tempTests; - } catch (e) { - assert_unreached() - } - tests.forEach(function(t, i) { test(function() { var namespace = t[0], qualifiedName = t[1], doctype = t[2], expected = t[3] @@ -147,14 +157,31 @@ test(function() { var doc = document.implementation.createDocument(namespace, qualifiedName, doctype) assert_equals(doc.nodeType, Node.DOCUMENT_NODE) assert_equals(doc.nodeType, doc.DOCUMENT_NODE) - if (qualifiedName === "" || qualifiedName === null) { + var omitRootElement = qualifiedName === null || String(qualifiedName) === "" + if (omitRootElement) { assert_equals(doc.documentElement, null) } else { - assert_not_equals(doc.documentElement, null) - assert_equals(doc.documentElement.nodeType, Node.ELEMENT_NODE) + var element = doc.documentElement + assert_not_equals(element, null) + assert_equals(element.nodeType, Node.ELEMENT_NODE) + assert_equals(element.ownerDocument, doc) + var qualified = String(qualifiedName), names = [] + if (qualified.indexOf(":") >= 0) { + names = qualified.split(":", 2) + } else { + names = [null, qualified] + } + assert_equals(element.prefix, names[0]) + assert_equals(element.localName, names[1]) + assert_equals(element.namespaceURI, namespace === undefined ? null : namespace) } - assert_equals(doc.doctype, doctype) - assert_equals(doc.childNodes.length, !!qualifiedName + !!doctype) + if (!doctype) { + assert_equals(doc.doctype, null) + } else { + assert_equals(doc.doctype, doctype) + assert_equals(doc.doctype.ownerDocument, doc) + } + assert_equals(doc.childNodes.length, !omitRootElement + !!doctype) } }, "createDocument test " + i + ": " + t.map(function(el) { return format_value(el) })) }) diff --git a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Document-createElement.html b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Document-createElement.html index 7fc6b8a8c088..677a781965a7 100644 --- a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Document-createElement.html +++ b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Document-createElement.html @@ -10,6 +10,18 @@
+ +
+ diff --git a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Document-getElementsByTagName.html b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Document-getElementsByTagName.html index 26161ac4facd..8a5303a442f7 100644 --- a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Document-getElementsByTagName.html +++ b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Document-getElementsByTagName.html @@ -5,22 +5,71 @@
+

 
+
+
+ diff --git a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Node-isEqualNode.xhtml b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Node-isEqualNode.xhtml index 09bbc8ca8f32..d33406a74cf3 100644 --- a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Node-isEqualNode.xhtml +++ b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Node-isEqualNode.xhtml @@ -50,6 +50,7 @@ function iframeLoaded() { assert_true(doc1.doctype.isEqualNode(doc2.doctype), "doc1.doctype.isEqualNode(doc2.doctype)") assert_true(doc1.isEqualNode(doc2), "doc1.isEqualNode(doc2)") }) + internalSubset.done() } } diff --git a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_NodeFilter-constants.html b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_NodeFilter-constants.html index 637fb5907d77..7d2090b9c1b8 100644 --- a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_NodeFilter-constants.html +++ b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_NodeFilter-constants.html @@ -7,7 +7,6 @@ diff --git a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-commonAncestorContainer.html b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-commonAncestorContainer.html index 3dc4a61f43a2..f0a3e451cd01 100644 --- a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-commonAncestorContainer.html +++ b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-commonAncestorContainer.html @@ -7,9 +7,7 @@ test(function() { var range = document.createRange(); range.detach(); - assert_throws("INVALID_STATE_ERR", function() { - range.commonAncestorContainer(); - }, "Detached Range must throw INVALID_STATE_ERR on accessing commonAncestorContainer"); + assert_equals(range.commonAncestorContainer, document); }, "Detached Range") test(function() { var df = document.createDocumentFragment(); diff --git a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-comparePoint.html b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-comparePoint.html index cb6b9c5fb13e..2b289095e922 100644 --- a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-comparePoint.html +++ b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-comparePoint.html @@ -8,7 +8,7 @@ test(function() { var r = document.createRange(); r.detach() - assert_throws("INVALID_STATE_ERR", function() { r.comparePoint(document.body, 0) }) + assert_equals(r.comparePoint(document.body, 0), 1) }) test(function() { var r = document.createRange(); diff --git a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-detach.html b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-detach.html index 0600a3d25630..87be46ad5e1f 100644 --- a/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-detach.html +++ b/dom/imptests/webapps/DOMCore/tests/submissions/Ms2ger/test_Range-detach.html @@ -8,6 +8,6 @@ test(function() { var r = document.createRange(); r.detach() - assert_throws("INVALID_STATE_ERR", function() { r.detach() }) + r.detach() }) diff --git a/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/Makefile.in b/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/Makefile.in index 8e92e56305f3..e2a781d7d482 100644 --- a/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/Makefile.in +++ b/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/Makefile.in @@ -10,6 +10,7 @@ relativesrcdir := @relativesrcdir@ include $(DEPTH)/config/autoconf.mk MOCHITEST_FILES := \ + test_FormData-append.html \ test_interfaces.html \ test_setrequestheader-invalid-arguments.htm \ $(NULL) diff --git a/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_FormData-append.html b/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_FormData-append.html new file mode 100644 index 000000000000..b05c5169b54a --- /dev/null +++ b/dom/imptests/webapps/XMLHttpRequest/tests/submissions/Ms2ger/test_FormData-append.html @@ -0,0 +1,14 @@ + + +FormData.append + + + +
+ From 870eab27c7fd369eeb06cc696199f5df89dd5c3f Mon Sep 17 00:00:00 2001 From: Aryeh Gregor Date: Thu, 21 Mar 2013 14:55:08 +0200 Subject: [PATCH 73/73] Bug 851916 part 2 - createHTMLDocument() should work with no arguments; r=bz --- content/base/src/DOMImplementation.cpp | 34 +++++++++++-------- content/base/src/DOMImplementation.h | 2 +- .../tests/approved/test_interfaces.html.json | 1 - .../tests/submissions/Ms2ger/Makefile.in | 1 - ...mplementation-createHTMLDocument.html.json | 3 -- dom/webidl/DOMImplementation.webidl | 2 +- 6 files changed, 21 insertions(+), 22 deletions(-) delete mode 100644 dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_DOMImplementation-createHTMLDocument.html.json diff --git a/content/base/src/DOMImplementation.cpp b/content/base/src/DOMImplementation.cpp index fb12d8d4a983..52c73e8a1429 100644 --- a/content/base/src/DOMImplementation.cpp +++ b/content/base/src/DOMImplementation.cpp @@ -211,20 +211,22 @@ DOMImplementation::CreateHTMLDocument(const nsAString& aTitle, rv = root->AppendChildTo(head, false); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr title; - rv = doc->CreateElem(NS_LITERAL_STRING("title"), nullptr, kNameSpaceID_XHTML, - getter_AddRefs(title)); - NS_ENSURE_SUCCESS(rv, rv); - rv = head->AppendChildTo(title, false); - NS_ENSURE_SUCCESS(rv, rv); + if (!DOMStringIsNull(aTitle)) { + nsCOMPtr title; + rv = doc->CreateElem(NS_LITERAL_STRING("title"), nullptr, + kNameSpaceID_XHTML, getter_AddRefs(title)); + NS_ENSURE_SUCCESS(rv, rv); + rv = head->AppendChildTo(title, false); + NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr titleText; - rv = NS_NewTextNode(getter_AddRefs(titleText), doc->NodeInfoManager()); - NS_ENSURE_SUCCESS(rv, rv); - rv = titleText->SetText(aTitle, false); - NS_ENSURE_SUCCESS(rv, rv); - rv = title->AppendChildTo(titleText, false); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr titleText; + rv = NS_NewTextNode(getter_AddRefs(titleText), doc->NodeInfoManager()); + NS_ENSURE_SUCCESS(rv, rv); + rv = titleText->SetText(aTitle, false); + NS_ENSURE_SUCCESS(rv, rv); + rv = title->AppendChildTo(titleText, false); + NS_ENSURE_SUCCESS(rv, rv); + } nsCOMPtr body; rv = doc->CreateElem(NS_LITERAL_STRING("body"), nullptr, kNameSpaceID_XHTML, @@ -241,12 +243,14 @@ DOMImplementation::CreateHTMLDocument(const nsAString& aTitle, } already_AddRefed -DOMImplementation::CreateHTMLDocument(const nsAString& aTitle, +DOMImplementation::CreateHTMLDocument(const Optional& aTitle, ErrorResult& aRv) { nsCOMPtr document; nsCOMPtr domDocument; - aRv = CreateHTMLDocument(aTitle, getter_AddRefs(document), + aRv = CreateHTMLDocument(aTitle.WasPassed() ? aTitle.Value() + : NullString(), + getter_AddRefs(document), getter_AddRefs(domDocument)); return document.forget(); } diff --git a/content/base/src/DOMImplementation.h b/content/base/src/DOMImplementation.h index 28b6673dd314..bf6b4ab3c6f6 100644 --- a/content/base/src/DOMImplementation.h +++ b/content/base/src/DOMImplementation.h @@ -73,7 +73,7 @@ public: ErrorResult& aRv); already_AddRefed - CreateHTMLDocument(const nsAString& aTitle, ErrorResult& aRv); + CreateHTMLDocument(const Optional& aTitle, ErrorResult& aRv); private: nsresult CreateDocument(const nsAString& aNamespaceURI, diff --git a/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json b/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json index fc7c5d1d4524..6961d4a9eba8 100644 --- a/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json +++ b/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json @@ -63,7 +63,6 @@ "Document interface: calling prepend(union) on xmlDoc with too few arguments must throw TypeError": true, "Document interface: xmlDoc must inherit property \"append\" with the proper type (29)": true, "Document interface: calling append(union) on xmlDoc with too few arguments must throw TypeError": true, - "DOMImplementation interface: operation createHTMLDocument(DOMString)": true, "DocumentFragment interface: attribute children": true, "DocumentFragment interface: attribute firstElementChild": true, "DocumentFragment interface: attribute lastElementChild": true, diff --git a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/Makefile.in b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/Makefile.in index 324622368dc4..c54065605978 100644 --- a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/Makefile.in +++ b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/Makefile.in @@ -11,7 +11,6 @@ include $(DEPTH)/config/autoconf.mk MOCHITEST_FILES := \ test_CharacterData-remove.html.json \ - test_DOMImplementation-createHTMLDocument.html.json \ test_Document-createElementNS.html.json \ test_Document-getElementsByTagName.html.json \ test_DocumentType-remove.html.json \ diff --git a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_DOMImplementation-createHTMLDocument.html.json b/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_DOMImplementation-createHTMLDocument.html.json deleted file mode 100644 index 93ba2a326ab5..000000000000 --- a/dom/imptests/failures/webapps/DOMCore/tests/submissions/Ms2ger/test_DOMImplementation-createHTMLDocument.html.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Missing title argument": true -} diff --git a/dom/webidl/DOMImplementation.webidl b/dom/webidl/DOMImplementation.webidl index e3021a9233f9..f7fa91c422ad 100644 --- a/dom/webidl/DOMImplementation.webidl +++ b/dom/webidl/DOMImplementation.webidl @@ -23,5 +23,5 @@ interface DOMImplementation { [TreatNullAs=EmptyString] DOMString qualifiedName, DocumentType? doctype); [Throws] - Document createHTMLDocument(DOMString title); + Document createHTMLDocument(optional DOMString title); };