diff --git a/browser/components/preferences/fonts.xul b/browser/components/preferences/fonts.xul index f4ceeb2d689..d4d15f322ff 100644 --- a/browser/components/preferences/fonts.xul +++ b/browser/components/preferences/fonts.xul @@ -48,8 +48,6 @@ - - @@ -65,6 +63,7 @@ + @@ -72,10 +71,8 @@ - - - + diff --git a/browser/locales/en-US/chrome/browser/preferences/fonts.dtd b/browser/locales/en-US/chrome/browser/preferences/fonts.dtd index f243cba3d7c..f2847edfd03 100644 --- a/browser/locales/en-US/chrome/browser/preferences/fonts.dtd +++ b/browser/locales/en-US/chrome/browser/preferences/fonts.dtd @@ -21,18 +21,17 @@ - - + + - - - + diff --git a/configure.in b/configure.in index af352a191cc..f37e7721c96 100644 --- a/configure.in +++ b/configure.in @@ -3843,6 +3843,7 @@ MOZ_PAY= MOZ_AUDIO_CHANNEL_MANAGER= NSS_NO_LIBPKIX= MOZ_CONTENT_SANDBOX= +MOZ_GMP_SANDBOX= JSGC_USE_EXACT_ROOTING=1 JSGC_GENERATIONAL= @@ -6394,6 +6395,28 @@ fi AC_SUBST(MOZ_CONTENT_SANDBOX) +dnl ======================================================== +dnl = Gecko Media Plugin sandboxing +dnl ======================================================== +case $OS_TARGET in +WINNT) + MOZ_GMP_SANDBOX=1 + ;; +Linux) + case $CPU_ARCH in + x86_64|x86) + MOZ_GMP_SANDBOX=1 + ;; + esac + ;; +esac + +if test -n "$MOZ_GMP_SANDBOX"; then + AC_DEFINE(MOZ_GMP_SANDBOX) +fi + +AC_SUBST(MOZ_GMP_SANDBOX) + dnl ======================================================== dnl = dnl = Module specific options diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index da8f18e041b..3d48508c81a 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -2170,6 +2170,13 @@ public: */ static bool IsContentInsertionPoint(const nsIContent* aContent); + + /** + * Returns whether the children of the provided content are + * nodes that are distributed to Shadow DOM insertion points. + */ + static bool HasDistributedChildren(nsIContent* aContent); + /** * Returns whether a given header is forbidden for an XHR or fetch * request. diff --git a/content/base/src/FragmentOrElement.cpp b/content/base/src/FragmentOrElement.cpp index e3627d51a7e..e2be514e15b 100644 --- a/content/base/src/FragmentOrElement.cpp +++ b/content/base/src/FragmentOrElement.cpp @@ -152,21 +152,23 @@ nsIContent::FindFirstNonChromeOnlyAccessContent() const nsIContent* nsIContent::GetFlattenedTreeParent() const { - nsIContent* parent = nullptr; + nsIContent* parent = GetParent(); - nsTArray* destInsertionPoints = GetExistingDestInsertionPoints(); - if (destInsertionPoints && !destInsertionPoints->IsEmpty()) { - // This node was distributed into an insertion point. The last node in - // the list of destination insertion insertion points is where this node - // appears in the composed tree (see Shadow DOM spec). - nsIContent* lastInsertionPoint = destInsertionPoints->LastElement(); - parent = lastInsertionPoint->GetParent(); + if (nsContentUtils::HasDistributedChildren(parent)) { + // This node is distributed to insertion points, thus we + // need to consult the destination insertion points list to + // figure out where this node was inserted in the flattened tree. + // It may be the case that |parent| distributes its children + // but the child does not match any insertion points, thus + // the flattened tree parent is nullptr. + nsTArray* destInsertionPoints = GetExistingDestInsertionPoints(); + parent = destInsertionPoints && !destInsertionPoints->IsEmpty() ? + destInsertionPoints->LastElement()->GetParent() : nullptr; } else if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { - parent = GetXBLInsertionParent(); - } - - if (!parent) { - parent = GetParent(); + nsIContent* insertionParent = GetXBLInsertionParent(); + if (insertionParent) { + parent = insertionParent; + } } // Shadow roots never shows up in the flattened tree. Return the host diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 858e5f68014..341634b3951 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -40,6 +40,7 @@ #include "mozilla/dom/HTMLMediaElement.h" #include "mozilla/dom/HTMLTemplateElement.h" #include "mozilla/dom/HTMLContentElement.h" +#include "mozilla/dom/HTMLShadowElement.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/TextDecoder.h" #include "mozilla/dom/TouchEvent.h" @@ -6874,6 +6875,45 @@ nsContentUtils::IsContentInsertionPoint(const nsIContent* aContent) return false; } +// static +bool +nsContentUtils::HasDistributedChildren(nsIContent* aContent) +{ + if (!aContent) { + return false; + } + + if (aContent->GetShadowRoot()) { + // Children of a shadow root host are distributed + // to content insertion points in the shadow root. + return true; + } + + ShadowRoot* shadow = ShadowRoot::FromNode(aContent); + if (shadow) { + // Children of a shadow root are distributed to + // the shadow insertion point of the younger shadow root. + return shadow->GetYoungerShadow(); + } + + HTMLShadowElement* shadowEl = HTMLShadowElement::FromContent(aContent); + if (shadowEl && shadowEl->IsInsertionPoint()) { + // Children of a shadow insertion points are distributed + // to the insertion points in the older shadow root. + return shadow->GetOlderShadow(); + } + + HTMLContentElement* contentEl = HTMLContentElement::FromContent(aContent); + if (contentEl && contentEl->IsInsertionPoint()) { + // Children of a content insertion point are distributed to the + // content insertion point if the content insertion point does + // not match any nodes (fallback content). + return contentEl->MatchedNodes().IsEmpty(); + } + + return false; +} + // static bool nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader) diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index c127f6b8d0c..47be89d54e5 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -2028,7 +2028,6 @@ GK_ATOM(zh_tw, "zh-tw") GK_ATOM(x_cyrillic, "x-cyrillic") GK_ATOM(he, "he") GK_ATOM(ar, "ar") -GK_ATOM(x_baltic, "x-baltic") GK_ATOM(x_devanagari, "x-devanagari") GK_ATOM(x_tamil, "x-tamil") GK_ATOM(x_armn, "x-armn") @@ -2048,7 +2047,6 @@ GK_ATOM(x_tibt, "x-tibt") // used in gfxGDIFontList.h GK_ATOM(ko_xxx, "ko-xxx") -GK_ATOM(x_central_euro, "x-central-euro") GK_ATOM(x_symbol, "x-symbol") // additional languages that have special case transformations diff --git a/content/html/content/src/HTMLContentElement.h b/content/html/content/src/HTMLContentElement.h index 98648f0ad05..5b457112077 100644 --- a/content/html/content/src/HTMLContentElement.h +++ b/content/html/content/src/HTMLContentElement.h @@ -21,6 +21,8 @@ class HTMLContentElement MOZ_FINAL : public nsGenericHTMLElement public: HTMLContentElement(already_AddRefed& aNodeInfo); + NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLContentElement, content) + // nsISupports NS_DECL_ISUPPORTS_INHERITED diff --git a/content/html/content/src/HTMLShadowElement.h b/content/html/content/src/HTMLShadowElement.h index 00e8cb92764..6e852a1aa84 100644 --- a/content/html/content/src/HTMLShadowElement.h +++ b/content/html/content/src/HTMLShadowElement.h @@ -17,6 +17,8 @@ class HTMLShadowElement MOZ_FINAL : public nsGenericHTMLElement, public: HTMLShadowElement(already_AddRefed& aNodeInfo); + NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLShadowElement, shadow) + // nsISupports NS_DECL_ISUPPORTS_INHERITED diff --git a/content/media/fmp4/MP4Reader.cpp b/content/media/fmp4/MP4Reader.cpp index 0d6c3370570..69a91ec1949 100644 --- a/content/media/fmp4/MP4Reader.cpp +++ b/content/media/fmp4/MP4Reader.cpp @@ -105,6 +105,7 @@ MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder) , mVideo("MP4 video decoder data", Preferences::GetUint("media.mp4-video-decode-ahead", 2)) , mLastReportedNumDecodedFrames(0) , mLayersBackendType(layers::LayersBackend::LAYERS_NONE) + , mTimeRangesMonitor("MP4Reader::TimeRanges") , mDemuxerInitialized(false) , mIsEncrypted(false) { @@ -705,20 +706,30 @@ MP4Reader::Seek(int64_t aTime, return NS_OK; } -nsresult -MP4Reader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) +void +MP4Reader::NotifyDataArrived(const char* aBuffer, uint32_t aLength, + int64_t aOffset) { nsTArray ranges; if (NS_FAILED(mDecoder->GetResource()->GetCachedRanges(ranges))) { - return NS_ERROR_FAILURE; + return; } nsTArray> timeRanges; mDemuxer->ConvertByteRangesToTime(ranges, &timeRanges); - for (size_t i = 0; i < timeRanges.Length(); i++) { - aBuffered->Add((timeRanges[i].start - aStartTime) / 1000000.0, - (timeRanges[i].end - aStartTime) / 1000000.0); + MonitorAutoLock mon(mTimeRangesMonitor); + mTimeRanges = timeRanges; +} + + +nsresult +MP4Reader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) +{ + MonitorAutoLock mon(mTimeRangesMonitor); + for (size_t i = 0; i < mTimeRanges.Length(); i++) { + aBuffered->Add((mTimeRanges[i].start - aStartTime) / 1000000.0, + (mTimeRanges[i].end - aStartTime) / 1000000.0); } return NS_OK; diff --git a/content/media/fmp4/MP4Reader.h b/content/media/fmp4/MP4Reader.h index 73be4e2e173..7045c68272a 100644 --- a/content/media/fmp4/MP4Reader.h +++ b/content/media/fmp4/MP4Reader.h @@ -53,6 +53,9 @@ public: virtual bool IsMediaSeekable() MOZ_OVERRIDE; + virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, + int64_t aOffset) MOZ_OVERRIDE; + virtual nsresult GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime) MOZ_OVERRIDE; @@ -165,6 +168,8 @@ private: layers::LayersBackend mLayersBackendType; nsTArray> mInitDataEncountered; + Monitor mTimeRangesMonitor; + nsTArray> mTimeRanges; // True if we've read the streams' metadata. bool mDemuxerInitialized; diff --git a/content/media/fmp4/PlatformDecoderModule.cpp b/content/media/fmp4/PlatformDecoderModule.cpp index 7b732787101..3acffc29a30 100644 --- a/content/media/fmp4/PlatformDecoderModule.cpp +++ b/content/media/fmp4/PlatformDecoderModule.cpp @@ -93,9 +93,6 @@ PlatformDecoderModule::CreateCDMWrapper(CDMProxy* aProxy, if (!pdm) { return nullptr; } - } else { - NS_WARNING("CDM that decodes not yet supported!"); - return nullptr; } return new EMEDecoderModule(aProxy, diff --git a/content/media/fmp4/eme/EMEAACDecoder.cpp b/content/media/fmp4/eme/EMEAACDecoder.cpp new file mode 100644 index 00000000000..051eaff6c0a --- /dev/null +++ b/content/media/fmp4/eme/EMEAACDecoder.cpp @@ -0,0 +1,319 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 "EMEAACDecoder.h" +#include "mp4_demuxer/DecoderData.h" +#include "mozilla/EMELog.h" +#include "gmp-audio-host.h" +#include "gmp-audio-decode.h" +#include "gmp-audio-samples.h" +#include "GMPAudioHost.h" +#include "GMPAudioDecoderProxy.h" +#include "mozilla/CDMProxy.h" +#include "nsServiceManagerUtils.h" +#include "prsystem.h" + +namespace mozilla { + +EMEAACDecoder::EMEAACDecoder(CDMProxy* aProxy, + const AudioDecoderConfig& aConfig, + MediaTaskQueue* aTaskQueue, + MediaDataDecoderCallback* aCallback) + : mAudioRate(0) + , mAudioBytesPerSample(0) + , mAudioChannels(0) + , mMustRecaptureAudioPosition(true) + , mAudioFrameSum(0) + , mAudioFrameOffset(0) + , mStreamOffset(0) + , mProxy(aProxy) + , mGMP(nullptr) + , mConfig(aConfig) + , mTaskQueue(aTaskQueue) + , mCallback(aCallback) + , mMonitor("EMEAACDecoder") + , mFlushComplete(false) +{ +} + +EMEAACDecoder::~EMEAACDecoder() +{ +} + +nsresult +EMEAACDecoder::Init() +{ + // Note: this runs on the decode task queue. + + MOZ_ASSERT((mConfig.bits_per_sample / 8) == 2); // Demuxer guarantees this. + + mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1"); + MOZ_ASSERT(mMPS); + + nsresult rv = mMPS->GetThread(getter_AddRefs(mGMPThread)); + NS_ENSURE_SUCCESS(rv, rv); + + nsRefPtr task(new InitTask(this)); + rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_SUCCESS(task->mResult, task->mResult); + + return NS_OK; +} + +nsresult +EMEAACDecoder::Input(MP4Sample* aSample) +{ + MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue. + + nsRefPtr task(new DeliverSample(this, aSample)); + nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +EMEAACDecoder::Flush() +{ + MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue. + + { + MonitorAutoLock mon(mMonitor); + mFlushComplete = false; + } + + nsRefPtr task; + task = NS_NewRunnableMethod(this, &EMEAACDecoder::GmpFlush); + nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + + { + MonitorAutoLock mon(mMonitor); + while (!mFlushComplete) { + mon.Wait(); + } + } + + return NS_OK; +} + +nsresult +EMEAACDecoder::Drain() +{ + MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue. + + nsRefPtr task; + task = NS_NewRunnableMethod(this, &EMEAACDecoder::GmpDrain); + nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; +} + +nsresult +EMEAACDecoder::Shutdown() +{ + MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue. + + nsRefPtr task; + task = NS_NewRunnableMethod(this, &EMEAACDecoder::GmpShutdown); + nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; +} + +void +EMEAACDecoder::Decoded(const nsTArray& aPCM, + uint64_t aTimeStamp) +{ + MOZ_ASSERT(IsOnGMPThread()); + + size_t numFrames = aPCM.Length() / mAudioChannels; + nsAutoArrayPtr audioData(new AudioDataValue[aPCM.Length()]); + + for (size_t i = 0; i < aPCM.Length(); ++i) { + audioData[i] = AudioSampleToFloat(aPCM[i]); + } + + if (mMustRecaptureAudioPosition) { + mAudioFrameSum = 0; + auto timestamp = UsecsToFrames(aTimeStamp, mAudioRate); + if (!timestamp.isValid()) { + NS_WARNING("Invalid timestamp"); + mCallback->Error(); + return; + } + mAudioFrameOffset = timestamp.value(); + MOZ_ASSERT(mAudioFrameOffset >= 0); + mMustRecaptureAudioPosition = false; + } + + auto timestamp = FramesToUsecs(mAudioFrameOffset + mAudioFrameSum, mAudioRate); + if (!timestamp.isValid()) { + NS_WARNING("Invalid timestamp on audio samples"); + mCallback->Error(); + return; + } + mAudioFrameSum += numFrames; + + auto duration = FramesToUsecs(numFrames, mAudioRate); + if (!duration.isValid()) { + NS_WARNING("Invalid duration on audio samples"); + mCallback->Error(); + return; + } + + nsAutoPtr audio(new AudioData(mStreamOffset, + timestamp.value(), + duration.value(), + numFrames, + audioData.forget(), + mAudioChannels)); + + #ifdef LOG_SAMPLE_DECODE + LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u", + timestamp, duration, currentLength); + #endif + + mCallback->Output(audio.forget()); +} + +void +EMEAACDecoder::InputDataExhausted() +{ + MOZ_ASSERT(IsOnGMPThread()); + mCallback->InputExhausted(); +} + +void +EMEAACDecoder::DrainComplete() +{ + MOZ_ASSERT(IsOnGMPThread()); + mCallback->DrainComplete(); +} + +void +EMEAACDecoder::ResetComplete() +{ + MOZ_ASSERT(IsOnGMPThread()); + mMustRecaptureAudioPosition = true; + { + MonitorAutoLock mon(mMonitor); + mFlushComplete = true; + mon.NotifyAll(); + } +} + +void +EMEAACDecoder::Error(GMPErr aErr) +{ + MOZ_ASSERT(IsOnGMPThread()); + mCallback->Error(); + GmpShutdown(); +} + +void +EMEAACDecoder::Terminated() +{ + MOZ_ASSERT(IsOnGMPThread()); + GmpShutdown(); +} + +nsresult +EMEAACDecoder::GmpInit() +{ + MOZ_ASSERT(IsOnGMPThread()); + + nsTArray tags; + tags.AppendElement(NS_LITERAL_CSTRING("aac")); + tags.AppendElement(NS_ConvertUTF16toUTF8(mProxy->KeySystem())); + nsresult rv = mMPS->GetGMPAudioDecoder(&tags, + mProxy->GetOrigin(), + &mGMP); + NS_ENSURE_SUCCESS(rv, rv); + MOZ_ASSERT(mGMP); + + mAudioRate = mConfig.samples_per_second; + mAudioBytesPerSample = mConfig.bits_per_sample / 8; + mAudioChannels = mConfig.channel_count; + + nsTArray extraData; + extraData.AppendElements(&mConfig.audio_specific_config[0], + mConfig.audio_specific_config.length()); + + mGMP->InitDecode(kGMPAudioCodecAAC, + mAudioChannels, + mConfig.bits_per_sample, + mAudioRate, + extraData, + this); + + return NS_OK; +} + +nsresult +EMEAACDecoder::GmpInput(MP4Sample* aSample) +{ + MOZ_ASSERT(IsOnGMPThread()); + nsAutoPtr sample(aSample); + if (!mGMP) { + mCallback->Error(); + return NS_ERROR_FAILURE; + } + + if (sample->crypto.valid) { + CDMCaps::AutoLock caps(mProxy->Capabilites()); + MOZ_ASSERT(caps.CanDecryptAndDecodeAudio()); + const auto& keyid = sample->crypto.key; + if (!caps.IsKeyUsable(keyid)) { + // DeliverSample assumes responsibility for deleting aSample. + nsRefPtr task(new DeliverSample(this, sample.forget())); + caps.CallWhenKeyUsable(keyid, task, mGMPThread); + return NS_OK; + } + } + + gmp::GMPAudioSamplesImpl samples(sample); + mGMP->Decode(samples); + + mStreamOffset = sample->byte_offset; + + return NS_OK; +} + +void +EMEAACDecoder::GmpFlush() +{ + MOZ_ASSERT(IsOnGMPThread()); + if (!mGMP || NS_FAILED(mGMP->Reset())) { + // Abort the flush... + MonitorAutoLock mon(mMonitor); + mFlushComplete = true; + mon.NotifyAll(); + } +} + +void +EMEAACDecoder::GmpDrain() +{ + MOZ_ASSERT(IsOnGMPThread()); + if (!mGMP || NS_FAILED(mGMP->Drain())) { + mCallback->DrainComplete(); + } +} + +void +EMEAACDecoder::GmpShutdown() +{ + MOZ_ASSERT(IsOnGMPThread()); + if (!mGMP) { + return; + } + mGMP->Close(); + mGMP = nullptr; +} + +} // namespace mozilla diff --git a/content/media/fmp4/eme/EMEAACDecoder.h b/content/media/fmp4/eme/EMEAACDecoder.h new file mode 100644 index 00000000000..807b4d475e3 --- /dev/null +++ b/content/media/fmp4/eme/EMEAACDecoder.h @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 EMEAACDecoder_h_ +#define EMEAACDecoder_h_ + +#include "PlatformDecoderModule.h" +#include "mp4_demuxer/DecoderData.h" +#include "mozIGeckoMediaPluginService.h" +#include "nsServiceManagerUtils.h" +#include "GMPAudioHost.h" +#include "GMPAudioDecoderProxy.h" + +namespace mozilla { + +class EMEAACDecoder : public MediaDataDecoder + , public GMPAudioDecoderProxyCallback +{ + typedef mp4_demuxer::MP4Sample MP4Sample; + typedef mp4_demuxer::AudioDecoderConfig AudioDecoderConfig; +public: + EMEAACDecoder(CDMProxy* aProxy, + const AudioDecoderConfig& aConfig, + MediaTaskQueue* aTaskQueue, + MediaDataDecoderCallback* aCallback); + + ~EMEAACDecoder(); + + // MediaDataDecoder implementation. + virtual nsresult Init() MOZ_OVERRIDE; + virtual nsresult Input(MP4Sample* aSample) MOZ_OVERRIDE; + virtual nsresult Flush() MOZ_OVERRIDE; + virtual nsresult Drain() MOZ_OVERRIDE; + virtual nsresult Shutdown() MOZ_OVERRIDE; + + // GMPAudioDecoderProxyCallback implementation. + virtual void Decoded(const nsTArray& aPCM, + uint64_t aTimeStamp) MOZ_OVERRIDE; + virtual void InputDataExhausted() MOZ_OVERRIDE; + virtual void DrainComplete() MOZ_OVERRIDE; + virtual void ResetComplete() MOZ_OVERRIDE; + virtual void Error(GMPErr aErr) MOZ_OVERRIDE; + virtual void Terminated() MOZ_OVERRIDE; + +private: + + class DeliverSample : public nsRunnable { + public: + DeliverSample(EMEAACDecoder* aDecoder, + mp4_demuxer::MP4Sample* aSample) + : mDecoder(aDecoder) + , mSample(aSample) + {} + + NS_IMETHOD Run() { + mDecoder->GmpInput(mSample.forget()); + return NS_OK; + } + private: + nsRefPtr mDecoder; + nsAutoPtr mSample; + }; + + class InitTask : public nsRunnable { + public: + InitTask(EMEAACDecoder* aDecoder) + : mDecoder(aDecoder) + {} + NS_IMETHOD Run() { + mResult = mDecoder->GmpInit(); + return NS_OK; + } + nsresult mResult; + EMEAACDecoder* mDecoder; + }; + + nsresult GmpInit(); + nsresult GmpInput(MP4Sample* aSample); + void GmpFlush(); + void GmpDrain(); + void GmpShutdown(); + +#ifdef DEBUG + bool IsOnGMPThread() { + return NS_GetCurrentThread() == mGMPThread; + } +#endif + + uint32_t mAudioRate; + uint32_t mAudioBytesPerSample; + uint32_t mAudioChannels; + bool mMustRecaptureAudioPosition; + int64_t mAudioFrameSum; + int64_t mAudioFrameOffset; + int64_t mStreamOffset; + + nsCOMPtr mMPS; + nsCOMPtr mGMPThread; + nsRefPtr mProxy; + GMPAudioDecoderProxy* mGMP; + + const mp4_demuxer::AudioDecoderConfig& mConfig; + nsRefPtr mTaskQueue; + MediaDataDecoderCallback* mCallback; + + Monitor mMonitor; + bool mFlushComplete; +}; + +} // namespace mozilla + +#endif \ No newline at end of file diff --git a/content/media/fmp4/eme/EMEDecoderModule.cpp b/content/media/fmp4/eme/EMEDecoderModule.cpp index 21ea0c726af..65c6d9f555c 100644 --- a/content/media/fmp4/eme/EMEDecoderModule.cpp +++ b/content/media/fmp4/eme/EMEDecoderModule.cpp @@ -18,6 +18,8 @@ #include "MediaTaskQueue.h" #include "SharedThreadPool.h" #include "mozilla/EMELog.h" +#include "EMEH264Decoder.h" +#include "EMEAACDecoder.h" #include namespace mozilla { @@ -199,8 +201,13 @@ EMEDecoderModule::CreateH264Decoder(const VideoDecoderConfig& aConfig, MediaDataDecoderCallback* aCallback) { if (mCDMDecodesVideo) { - NS_WARNING("Support for CDM that decodes video not yet supported"); - return nullptr; + nsRefPtr decoder(new EMEH264Decoder(mProxy, + aConfig, + aLayersBackend, + aImageContainer, + aVideoTaskQueue, + aCallback)); + return decoder.forget(); } nsRefPtr decoder(mPDM->CreateH264Decoder(aConfig, @@ -225,8 +232,11 @@ EMEDecoderModule::CreateAACDecoder(const AudioDecoderConfig& aConfig, MediaDataDecoderCallback* aCallback) { if (mCDMDecodesAudio) { - NS_WARNING("Support for CDM that decodes audio not yet supported"); - return nullptr; + nsRefPtr decoder(new EMEAACDecoder(mProxy, + aConfig, + aAudioTaskQueue, + aCallback)); + return decoder.forget(); } nsRefPtr decoder(mPDM->CreateAACDecoder(aConfig, diff --git a/content/media/fmp4/eme/EMEH264Decoder.cpp b/content/media/fmp4/eme/EMEH264Decoder.cpp new file mode 100644 index 00000000000..23776b5d061 --- /dev/null +++ b/content/media/fmp4/eme/EMEH264Decoder.cpp @@ -0,0 +1,360 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 "EMEH264Decoder.h" +#include "gmp-video-host.h" +#include "gmp-video-decode.h" +#include "gmp-video-frame-i420.h" +#include "gmp-video-frame-encoded.h" +#include "GMPVideoEncodedFrameImpl.h" +#include "mp4_demuxer/AnnexB.h" +#include "mozilla/CDMProxy.h" +#include "nsServiceManagerUtils.h" +#include "prsystem.h" +#include "gfx2DGlue.h" + +namespace mozilla { + +EMEH264Decoder::EMEH264Decoder(CDMProxy* aProxy, + const mp4_demuxer::VideoDecoderConfig& aConfig, + layers::LayersBackend aLayersBackend, + layers::ImageContainer* aImageContainer, + MediaTaskQueue* aTaskQueue, + MediaDataDecoderCallback* aCallback) + : mProxy(aProxy) + , mGMP(nullptr) + , mHost(nullptr) + , mConfig(aConfig) + , mImageContainer(aImageContainer) + , mTaskQueue(aTaskQueue) + , mCallback(aCallback) + , mLastStreamOffset(0) + , mMonitor("EMEH264Decoder") + , mFlushComplete(false) +{ +} + +EMEH264Decoder::~EMEH264Decoder() { +} + +nsresult +EMEH264Decoder::Init() +{ + // Note: this runs on the decode task queue. + + mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1"); + MOZ_ASSERT(mMPS); + + nsresult rv = mMPS->GetThread(getter_AddRefs(mGMPThread)); + NS_ENSURE_SUCCESS(rv, rv); + + nsRefPtr task(new InitTask(this)); + rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_SUCCESS(task->mResult, task->mResult); + + return NS_OK; +} + +nsresult +EMEH264Decoder::Input(MP4Sample* aSample) +{ + MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue. + + nsRefPtr task(new DeliverSample(this, aSample)); + nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +EMEH264Decoder::Flush() +{ + MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue. + + { + MonitorAutoLock mon(mMonitor); + mFlushComplete = false; + } + + nsRefPtr task; + task = NS_NewRunnableMethod(this, &EMEH264Decoder::GmpFlush); + nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + + { + MonitorAutoLock mon(mMonitor); + while (!mFlushComplete) { + mon.Wait(); + } + } + + return NS_OK; +} + +nsresult +EMEH264Decoder::Drain() +{ + MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue. + + nsRefPtr task; + task = NS_NewRunnableMethod(this, &EMEH264Decoder::GmpDrain); + nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; +} + +nsresult +EMEH264Decoder::Shutdown() +{ + MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue. + + nsRefPtr task; + task = NS_NewRunnableMethod(this, &EMEH264Decoder::GmpShutdown); + nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; +} + +void +EMEH264Decoder::Decoded(GMPVideoi420Frame* aDecodedFrame) +{ + MOZ_ASSERT(IsOnGMPThread()); + + VideoData::YCbCrBuffer b; + + auto height = aDecodedFrame->Height(); + auto width = aDecodedFrame->Width(); + + // Y (Y') plane + b.mPlanes[0].mData = aDecodedFrame->Buffer(kGMPYPlane); + b.mPlanes[0].mStride = aDecodedFrame->Stride(kGMPYPlane); + b.mPlanes[0].mHeight = height; + b.mPlanes[0].mWidth = width; + b.mPlanes[0].mOffset = 0; + b.mPlanes[0].mSkip = 0; + + // U plane (Cb) + b.mPlanes[1].mData = aDecodedFrame->Buffer(kGMPUPlane); + b.mPlanes[1].mStride = aDecodedFrame->Stride(kGMPUPlane); + b.mPlanes[1].mHeight = height / 2; + b.mPlanes[1].mWidth = width / 2; + b.mPlanes[1].mOffset = 0; + b.mPlanes[1].mSkip = 0; + + // V plane (Cr) + b.mPlanes[2].mData = aDecodedFrame->Buffer(kGMPVPlane); + b.mPlanes[2].mStride = aDecodedFrame->Stride(kGMPVPlane); + b.mPlanes[2].mHeight = height / 2; + b.mPlanes[2].mWidth = width / 2; + b.mPlanes[2].mOffset = 0; + b.mPlanes[2].mSkip = 0; + + VideoData *v = VideoData::Create(mVideoInfo, + mImageContainer, + mLastStreamOffset, + aDecodedFrame->Timestamp(), + aDecodedFrame->Duration(), + b, + false, + -1, + ToIntRect(mPictureRegion)); + aDecodedFrame->Destroy(); + mCallback->Output(v); +} + +void +EMEH264Decoder::ReceivedDecodedReferenceFrame(const uint64_t aPictureId) +{ + // Ignore. +} + +void +EMEH264Decoder::ReceivedDecodedFrame(const uint64_t aPictureId) +{ + // Ignore. +} + +void +EMEH264Decoder::InputDataExhausted() +{ + MOZ_ASSERT(IsOnGMPThread()); + mCallback->InputExhausted(); +} + +void +EMEH264Decoder::DrainComplete() +{ + MOZ_ASSERT(IsOnGMPThread()); + mCallback->DrainComplete(); +} + +void +EMEH264Decoder::ResetComplete() +{ + MOZ_ASSERT(IsOnGMPThread()); + { + MonitorAutoLock mon(mMonitor); + mFlushComplete = true; + mon.NotifyAll(); + } +} + +void +EMEH264Decoder::Error(GMPErr aErr) +{ + MOZ_ASSERT(IsOnGMPThread()); + mCallback->Error(); + GmpShutdown(); +} + +void +EMEH264Decoder::Terminated() +{ + MOZ_ASSERT(IsOnGMPThread()); + + NS_WARNING("H.264 GMP decoder terminated."); + GmpShutdown(); +} + +nsresult +EMEH264Decoder::GmpInit() +{ + MOZ_ASSERT(IsOnGMPThread()); + + nsTArray tags; + tags.AppendElement(NS_LITERAL_CSTRING("h264")); + tags.AppendElement(NS_ConvertUTF16toUTF8(mProxy->KeySystem())); + nsresult rv = mMPS->GetGMPVideoDecoder(&tags, + mProxy->GetOrigin(), + &mHost, + &mGMP); + NS_ENSURE_SUCCESS(rv, rv); + MOZ_ASSERT(mHost && mGMP); + + GMPVideoCodec codec; + memset(&codec, 0, sizeof(codec)); + + codec.mGMPApiVersion = kGMPVersion33; + + codec.mCodecType = kGMPVideoCodecH264; + codec.mWidth = mConfig.display_width; + codec.mHeight = mConfig.display_height; + + nsTArray codecSpecific; + codecSpecific.AppendElement(0); // mPacketizationMode. + codecSpecific.AppendElements(mConfig.extra_data.begin(), + mConfig.extra_data.length()); + + rv = mGMP->InitDecode(codec, + codecSpecific, + this, + PR_GetNumberOfProcessors()); + NS_ENSURE_SUCCESS(rv, rv); + + mVideoInfo.mDisplay = nsIntSize(mConfig.display_width, mConfig.display_height); + mVideoInfo.mHasVideo = true; + mPictureRegion = nsIntRect(0, 0, mConfig.display_width, mConfig.display_height); + + return NS_OK; +} + +nsresult +EMEH264Decoder::GmpInput(MP4Sample* aSample) +{ + MOZ_ASSERT(IsOnGMPThread()); + + nsAutoPtr sample(aSample); + if (!mGMP) { + mCallback->Error(); + return NS_ERROR_FAILURE; + } + + if (sample->crypto.valid) { + CDMCaps::AutoLock caps(mProxy->Capabilites()); + MOZ_ASSERT(caps.CanDecryptAndDecodeVideo()); + const auto& keyid = sample->crypto.key; + if (!caps.IsKeyUsable(keyid)) { + nsRefPtr task(new DeliverSample(this, sample.forget())); + caps.CallWhenKeyUsable(keyid, task, mGMPThread); + return NS_OK; + } + } + + + mLastStreamOffset = sample->byte_offset; + + GMPVideoFrame* ftmp = nullptr; + GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp); + if (GMP_FAILED(err)) { + mCallback->Error(); + return NS_ERROR_FAILURE; + } + + gmp::GMPVideoEncodedFrameImpl* frame = static_cast(ftmp); + err = frame->CreateEmptyFrame(sample->size); + if (GMP_FAILED(err)) { + mCallback->Error(); + return NS_ERROR_FAILURE; + } + + memcpy(frame->Buffer(), sample->data, frame->Size()); + + frame->SetEncodedWidth(mConfig.display_width); + frame->SetEncodedHeight(mConfig.display_height); + frame->SetTimeStamp(sample->composition_timestamp); + frame->SetCompleteFrame(true); + frame->SetDuration(sample->duration); + if (sample->crypto.valid) { + frame->InitCrypto(sample->crypto); + } + frame->SetFrameType(sample->is_sync_point ? kGMPKeyFrame : kGMPDeltaFrame); + frame->SetBufferType(GMP_BufferLength32); + + nsTArray info; // No codec specific per-frame info to pass. + nsresult rv = mGMP->Decode(frame, false, info, 0); + if (NS_FAILED(rv)) { + mCallback->Error(); + return rv; + } + + return NS_OK; +} + +void +EMEH264Decoder::GmpFlush() +{ + MOZ_ASSERT(IsOnGMPThread()); + if (!mGMP || NS_FAILED(mGMP->Reset())) { + // Abort the flush... + MonitorAutoLock mon(mMonitor); + mFlushComplete = true; + mon.NotifyAll(); + } +} + +void +EMEH264Decoder::GmpDrain() +{ + MOZ_ASSERT(IsOnGMPThread()); + if (!mGMP || NS_FAILED(mGMP->Drain())) { + mCallback->DrainComplete(); + } +} + +void +EMEH264Decoder::GmpShutdown() +{ + MOZ_ASSERT(IsOnGMPThread()); + if (!mGMP) { + return; + } + mGMP->Close(); + mGMP = nullptr; +} + +} // namespace mozilla diff --git a/content/media/fmp4/eme/EMEH264Decoder.h b/content/media/fmp4/eme/EMEH264Decoder.h new file mode 100644 index 00000000000..220695522d3 --- /dev/null +++ b/content/media/fmp4/eme/EMEH264Decoder.h @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 EMEH264Decoder_h_ +#define EMEH264Decoder_h_ + +#include "PlatformDecoderModule.h" +#include "mp4_demuxer/DecoderData.h" +#include "ImageContainer.h" +#include "GMPVideoDecoderProxy.h" +#include "mozIGeckoMediaPluginService.h" + +namespace mozilla { + +class CDMProxy; +class MediaTaskQueue; + +class EMEH264Decoder : public MediaDataDecoder + , public GMPVideoDecoderCallbackProxy +{ + typedef mp4_demuxer::MP4Sample MP4Sample; +public: + EMEH264Decoder(CDMProxy* aProxy, + const mp4_demuxer::VideoDecoderConfig& aConfig, + layers::LayersBackend aLayersBackend, + layers::ImageContainer* aImageContainer, + MediaTaskQueue* aTaskQueue, + MediaDataDecoderCallback* aCallback); + ~EMEH264Decoder(); + + // MediaDataDecoder + virtual nsresult Init() MOZ_OVERRIDE; + virtual nsresult Input(MP4Sample* aSample) MOZ_OVERRIDE; + virtual nsresult Flush() MOZ_OVERRIDE; + virtual nsresult Drain() MOZ_OVERRIDE; + virtual nsresult Shutdown() MOZ_OVERRIDE; + + // GMPVideoDecoderProxyCallback + virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) MOZ_OVERRIDE; + virtual void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) MOZ_OVERRIDE; + virtual void ReceivedDecodedFrame(const uint64_t aPictureId) MOZ_OVERRIDE; + virtual void InputDataExhausted() MOZ_OVERRIDE; + virtual void DrainComplete() MOZ_OVERRIDE; + virtual void ResetComplete() MOZ_OVERRIDE; + virtual void Error(GMPErr aErr) MOZ_OVERRIDE; + virtual void Terminated() MOZ_OVERRIDE; + +private: + + nsresult GmpInit(); + nsresult GmpInput(MP4Sample* aSample); + void GmpFlush(); + void GmpDrain(); + void GmpShutdown(); + +#ifdef DEBUG + bool IsOnGMPThread() { + return NS_GetCurrentThread() == mGMPThread; + } +#endif + + class DeliverSample : public nsRunnable { + public: + DeliverSample(EMEH264Decoder* aDecoder, + mp4_demuxer::MP4Sample* aSample) + : mDecoder(aDecoder) + , mSample(aSample) + {} + + NS_IMETHOD Run() { + mDecoder->GmpInput(mSample.forget()); + return NS_OK; + } + private: + nsRefPtr mDecoder; + nsAutoPtr mSample; + }; + + class InitTask : public nsRunnable { + public: + InitTask(EMEH264Decoder* aDecoder) + : mDecoder(aDecoder) + {} + NS_IMETHOD Run() { + mResult = mDecoder->GmpInit(); + return NS_OK; + } + nsresult mResult; + EMEH264Decoder* mDecoder; + }; + + nsCOMPtr mMPS; + nsCOMPtr mGMPThread; + nsRefPtr mProxy; + GMPVideoDecoderProxy* mGMP; + GMPVideoHost* mHost; + + VideoInfo mVideoInfo; + nsIntRect mPictureRegion; + const mp4_demuxer::VideoDecoderConfig& mConfig; + nsRefPtr mImageContainer; + nsRefPtr mTaskQueue; + MediaDataDecoderCallback* mCallback; + int64_t mLastStreamOffset; + Monitor mMonitor; + bool mFlushComplete; +}; + +} + +#endif // EMEH264Decoder_h_ diff --git a/content/media/fmp4/moz.build b/content/media/fmp4/moz.build index a01735fe79c..d26f6f1b983 100644 --- a/content/media/fmp4/moz.build +++ b/content/media/fmp4/moz.build @@ -5,7 +5,9 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXPORTS += [ + 'eme/EMEAACDecoder.h', 'eme/EMEDecoderModule.h', + 'eme/EMEH264Decoder.h', 'MP4Decoder.h', 'MP4Reader.h', 'PlatformDecoderModule.h', @@ -13,7 +15,9 @@ EXPORTS += [ UNIFIED_SOURCES += [ 'BlankDecoderModule.cpp', + 'eme/EMEAACDecoder.cpp', 'eme/EMEDecoderModule.cpp', + 'eme/EMEH264Decoder.cpp', 'PlatformDecoderModule.cpp', ] @@ -58,6 +62,12 @@ if CONFIG['MOZ_APPLEMEDIA']: '-framework AudioToolbox', ] +include('/ipc/chromium/chromium-config.mozbuild') + +LOCAL_INCLUDES += [ + '../base', +] + FINAL_LIBRARY = 'xul' FAIL_ON_WARNINGS = True diff --git a/content/media/gmp/GMPChild.cpp b/content/media/gmp/GMPChild.cpp index 0adf7034e7e..d0780dff02c 100644 --- a/content/media/gmp/GMPChild.cpp +++ b/content/media/gmp/GMPChild.cpp @@ -27,6 +27,8 @@ using mozilla::dom::CrashReporterChild; #if defined(XP_WIN) #define TARGET_SANDBOX_EXPORTS #include "mozilla/sandboxTarget.h" +#elif defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX) +#include "mozilla/Sandbox.h" #endif namespace mozilla { @@ -99,6 +101,13 @@ GMPChild::LoadPluginLibrary(const std::string& aPluginPath) nsAutoCString nativePath; libFile->GetNativePath(nativePath); + +#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX) + // Enable sandboxing here -- we know the plugin file's path, but + // this process's execution hasn't been affected by its content yet. + mozilla::SetMediaPluginSandbox(nativePath.get()); +#endif + mLib = PR_LoadLibrary(nativePath.get()); if (!mLib) { return false; @@ -110,7 +119,7 @@ GMPChild::LoadPluginLibrary(const std::string& aPluginPath) } auto platformAPI = new GMPPlatformAPI(); - InitPlatformAPI(*platformAPI); + InitPlatformAPI(*platformAPI, this); if (initFunc(platformAPI) != GMPNoErr) { return false; @@ -301,6 +310,33 @@ GMPChild::RecvPGMPDecryptorConstructor(PGMPDecryptorChild* aActor) return true; } +PGMPTimerChild* +GMPChild::AllocPGMPTimerChild() +{ + return new GMPTimerChild(this); +} + +bool +GMPChild::DeallocPGMPTimerChild(PGMPTimerChild* aActor) +{ + MOZ_ASSERT(mTimerChild == static_cast(aActor)); + mTimerChild = nullptr; + return true; +} + +GMPTimerChild* +GMPChild::GetGMPTimers() +{ + if (!mTimerChild) { + PGMPTimerChild* sc = SendPGMPTimerConstructor(); + if (!sc) { + return nullptr; + } + mTimerChild = static_cast(sc); + } + return mTimerChild; +} + bool GMPChild::RecvCrashPluginNow() { diff --git a/content/media/gmp/GMPChild.h b/content/media/gmp/GMPChild.h index 93c054d7bcd..dd453cde405 100644 --- a/content/media/gmp/GMPChild.h +++ b/content/media/gmp/GMPChild.h @@ -8,6 +8,7 @@ #include "mozilla/gmp/PGMPChild.h" #include "GMPSharedMemManager.h" +#include "GMPTimerChild.h" #include "gmp-entrypoints.h" #include "prlink.h" @@ -28,6 +29,9 @@ public: bool LoadPluginLibrary(const std::string& aPluginPath); MessageLoop* GMPMessageLoop(); + // Main thread only. + GMPTimerChild* GetGMPTimers(); + // GMPSharedMem virtual void CheckThread() MOZ_OVERRIDE; @@ -51,11 +55,16 @@ private: virtual bool DeallocPGMPAudioDecoderChild(PGMPAudioDecoderChild* aActor) MOZ_OVERRIDE; virtual bool RecvPGMPAudioDecoderConstructor(PGMPAudioDecoderChild* aActor) MOZ_OVERRIDE; + virtual PGMPTimerChild* AllocPGMPTimerChild() MOZ_OVERRIDE; + virtual bool DeallocPGMPTimerChild(PGMPTimerChild* aActor) MOZ_OVERRIDE; + virtual bool RecvCrashPluginNow() MOZ_OVERRIDE; virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; virtual void ProcessingError(Result aWhat) MOZ_OVERRIDE; + nsRefPtr mTimerChild; + PRLibrary* mLib; GMPGetAPIFunc mGetAPIFunc; MessageLoop* mGMPMessageLoop; diff --git a/content/media/gmp/GMPDecryptorChild.cpp b/content/media/gmp/GMPDecryptorChild.cpp index 892f29b7751..d0647207fb4 100644 --- a/content/media/gmp/GMPDecryptorChild.cpp +++ b/content/media/gmp/GMPDecryptorChild.cpp @@ -149,8 +149,13 @@ GMPDecryptorChild::Decrypted(GMPBuffer* aBuffer, GMPErr aResult) { MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); + if (!aBuffer) { + NS_WARNING("GMPDecryptorCallback passed bull GMPBuffer"); + return; + } auto buffer = static_cast(aBuffer); SendDecrypted(buffer->mId, aResult, buffer->mData); + delete buffer; } void @@ -307,6 +312,8 @@ GMPDecryptorChild::RecvDecrypt(const uint32_t& aId, GMPEncryptedBufferDataImpl metadata(aMetadata); + // Note: the GMPBufferImpl created here is deleted when the GMP passes + // it back in the Decrypted() callback above. mSession->Decrypt(new GMPBufferImpl(aId, aBuffer), &metadata); return true; } diff --git a/content/media/gmp/GMPDecryptorChild.h b/content/media/gmp/GMPDecryptorChild.h index ecc6229f50e..6e9a9eb4a88 100644 --- a/content/media/gmp/GMPDecryptorChild.h +++ b/content/media/gmp/GMPDecryptorChild.h @@ -120,7 +120,7 @@ private: #ifdef DEBUG GMPChild* mPlugin; -#endif +#endif }; } // namespace gmp diff --git a/content/media/gmp/GMPParent.cpp b/content/media/gmp/GMPParent.cpp index 791a333b71f..74aaa6a2f15 100644 --- a/content/media/gmp/GMPParent.cpp +++ b/content/media/gmp/GMPParent.cpp @@ -16,6 +16,7 @@ #include "mozIGeckoMediaPluginService.h" #include "mozilla/unused.h" #include "nsIObserverService.h" +#include "GMPTimerParent.h" #include "runnable_utils.h" #include "mozilla/dom/CrashReporterParent.h" @@ -92,7 +93,7 @@ GMPParent::Init(GeckoMediaPluginService *aService, nsIFile* aPluginDir) if (NS_FAILED(rv)) { return rv; } - LOGD(("%s::%s: %p for %s", __CLASS__, __FUNCTION__, this, + LOGD(("%s::%s: %p for %s", __CLASS__, __FUNCTION__, this, NS_LossyConvertUTF16toASCII(leafname).get())); MOZ_ASSERT(leafname.Length() > 4); @@ -328,7 +329,6 @@ GMPParent::State() const return mState; } -#ifdef DEBUG // Not changing to use mService since we'll be removing it nsIThread* GMPParent::GMPThread() @@ -350,7 +350,6 @@ GMPParent::GMPThread() return mGMPThread; } -#endif bool GMPParent::SupportsAPI(const nsCString& aAPI, const nsCString& aTag) @@ -618,6 +617,28 @@ GMPParent::DeallocPGMPAudioDecoderParent(PGMPAudioDecoderParent* aActor) return true; } +bool +GMPParent::RecvPGMPTimerConstructor(PGMPTimerParent* actor) +{ + return true; +} + +PGMPTimerParent* +GMPParent::AllocPGMPTimerParent() +{ + GMPTimerParent* p = new GMPTimerParent(GMPThread()); + NS_ADDREF(p); // Released in DeallocPGMPTimerParent. + return p; +} + +bool +GMPParent::DeallocPGMPTimerParent(PGMPTimerParent* aActor) +{ + GMPTimerParent* p = static_cast(aActor); + NS_RELEASE(p); + return true; +} + nsresult ParseNextRecord(nsILineInputStream* aLineInputStream, const nsCString& aPrefix, diff --git a/content/media/gmp/GMPParent.h b/content/media/gmp/GMPParent.h index d513b82aebf..d21c2696470 100644 --- a/content/media/gmp/GMPParent.h +++ b/content/media/gmp/GMPParent.h @@ -12,6 +12,7 @@ #include "GMPDecryptorParent.h" #include "GMPVideoDecoderParent.h" #include "GMPVideoEncoderParent.h" +#include "GMPTimerParent.h" #include "mozilla/gmp/PGMPParent.h" #include "nsCOMPtr.h" #include "nscore.h" @@ -95,9 +96,7 @@ public: void AudioDecoderDestroyed(GMPAudioDecoderParent* aDecoder); GMPState State() const; -#ifdef DEBUG nsIThread* GMPThread(); -#endif // A GMP can either be a single instance shared across all origins (like // in the OpenH264 case), or we can require a new plugin instance for every @@ -143,7 +142,7 @@ private: virtual PGMPVideoDecoderParent* AllocPGMPVideoDecoderParent() MOZ_OVERRIDE; virtual bool DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor) MOZ_OVERRIDE; - + virtual PGMPVideoEncoderParent* AllocPGMPVideoEncoderParent() MOZ_OVERRIDE; virtual bool DeallocPGMPVideoEncoderParent(PGMPVideoEncoderParent* aActor) MOZ_OVERRIDE; @@ -153,6 +152,10 @@ private: virtual PGMPAudioDecoderParent* AllocPGMPAudioDecoderParent() MOZ_OVERRIDE; virtual bool DeallocPGMPAudioDecoderParent(PGMPAudioDecoderParent* aActor) MOZ_OVERRIDE; + virtual bool RecvPGMPTimerConstructor(PGMPTimerParent* actor) MOZ_OVERRIDE; + virtual PGMPTimerParent* AllocPGMPTimerParent() MOZ_OVERRIDE; + virtual bool DeallocPGMPTimerParent(PGMPTimerParent* aActor) MOZ_OVERRIDE; + GMPState mState; nsCOMPtr mDirectory; // plugin directory on disk nsString mName; // base name of plugin on disk, UTF-16 because used for paths @@ -168,9 +171,7 @@ private: nsTArray> mVideoEncoders; nsTArray> mDecryptors; nsTArray> mAudioDecoders; -#ifdef DEBUG nsCOMPtr mGMPThread; -#endif // Origin the plugin is assigned to, or empty if the the plugin is not // assigned to an origin. nsAutoString mOrigin; diff --git a/content/media/gmp/GMPPlatform.cpp b/content/media/gmp/GMPPlatform.cpp index 3ba7ac5c3e0..e58b7bd3eb4 100644 --- a/content/media/gmp/GMPPlatform.cpp +++ b/content/media/gmp/GMPPlatform.cpp @@ -4,13 +4,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "GMPPlatform.h" +#include "GMPTimerChild.h" #include "mozilla/Monitor.h" #include "nsAutoPtr.h" +#include "GMPChild.h" +#include namespace mozilla { namespace gmp { static MessageLoop* sMainLoop = nullptr; +static GMPChild* sChild = nullptr; // We just need a refcounted wrapper for GMPTask objects. class Runnable MOZ_FINAL @@ -141,12 +145,30 @@ CreateMutex(GMPMutex** aMutex) return GMPNoErr; } +GMPErr +SetTimerOnMainThread(GMPTask* aTask, int64_t aTimeoutMS) +{ + GMPTimerChild* timers = sChild->GetGMPTimers(); + NS_ENSURE_TRUE(timers, GMPGenericErr); + return timers->SetTimer(aTask, aTimeoutMS); +} + +GMPErr +GetClock(GMPTimestamp* aOutTime) +{ + *aOutTime = time(0) * 1000; + return GMPNoErr; +} + void -InitPlatformAPI(GMPPlatformAPI& aPlatformAPI) +InitPlatformAPI(GMPPlatformAPI& aPlatformAPI, GMPChild* aChild) { if (!sMainLoop) { sMainLoop = MessageLoop::current(); } + if (!sChild) { + sChild = aChild; + } aPlatformAPI.version = 0; aPlatformAPI.createthread = &CreateThread; @@ -154,18 +176,20 @@ InitPlatformAPI(GMPPlatformAPI& aPlatformAPI) aPlatformAPI.syncrunonmainthread = &SyncRunOnMainThread; aPlatformAPI.createmutex = &CreateMutex; aPlatformAPI.createrecord = nullptr; - aPlatformAPI.settimer = nullptr; - aPlatformAPI.getcurrenttime = nullptr; + aPlatformAPI.settimer = &SetTimerOnMainThread; + aPlatformAPI.getcurrenttime = &GetClock; } GMPThreadImpl::GMPThreadImpl() : mMutex("GMPThreadImpl"), mThread("GMPThread") { + MOZ_COUNT_CTOR(GMPThread); } GMPThreadImpl::~GMPThreadImpl() { + MOZ_COUNT_DTOR(GMPThread); } void @@ -189,19 +213,30 @@ GMPThreadImpl::Post(GMPTask* aTask) void GMPThreadImpl::Join() { - MutexAutoLock lock(mMutex); - if (mThread.IsRunning()) { - mThread.Stop(); + { + MutexAutoLock lock(mMutex); + if (mThread.IsRunning()) { + mThread.Stop(); + } } + delete this; } GMPMutexImpl::GMPMutexImpl() : mMutex("gmp-mutex") { + MOZ_COUNT_CTOR(GMPMutexImpl); } GMPMutexImpl::~GMPMutexImpl() { + MOZ_COUNT_DTOR(GMPMutexImpl); +} + +void +GMPMutexImpl::Destroy() +{ + delete this; } void diff --git a/content/media/gmp/GMPPlatform.h b/content/media/gmp/GMPPlatform.h index af901d6d848..9323d53d91d 100644 --- a/content/media/gmp/GMPPlatform.h +++ b/content/media/gmp/GMPPlatform.h @@ -15,7 +15,9 @@ namespace gmp { class GMPChild; -void InitPlatformAPI(GMPPlatformAPI& aPlatformAPI); +void InitPlatformAPI(GMPPlatformAPI& aPlatformAPI, GMPChild* aChild); + +GMPErr RunOnMainThread(GMPTask* aTask); class GMPThreadImpl : public GMPThread { @@ -41,6 +43,7 @@ public: // GMPMutex virtual void Acquire() MOZ_OVERRIDE; virtual void Release() MOZ_OVERRIDE; + virtual void Destroy() MOZ_OVERRIDE; private: Mutex mMutex; diff --git a/content/media/gmp/GMPTimerChild.cpp b/content/media/gmp/GMPTimerChild.cpp new file mode 100644 index 00000000000..0b2b55c79e1 --- /dev/null +++ b/content/media/gmp/GMPTimerChild.cpp @@ -0,0 +1,67 @@ +/* -*- 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/. */ + +#include "GMPTimerChild.h" +#include "GMPPlatform.h" +#include "GMPChild.h" + +#define MAX_NUM_TIMERS 1000 + +namespace mozilla { +namespace gmp { + +GMPTimerChild::GMPTimerChild(GMPChild* aPlugin) + : mTimerCount(1) + , mPlugin(aPlugin) +{ + MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); +} + +GMPTimerChild::~GMPTimerChild() +{ + MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); +} + +GMPErr +GMPTimerChild::SetTimer(GMPTask* aTask, int64_t aTimeoutMS) +{ + if (!aTask) { + NS_WARNING("Tried to set timer with null task!"); + return GMPGenericErr; + } + + if (mPlugin->GMPMessageLoop() != MessageLoop::current()) { + NS_WARNING("Tried to set GMP timer on non-main thread."); + return GMPGenericErr; + } + + if (mTimers.Count() > MAX_NUM_TIMERS) { + return GMPQuotaExceededErr; + } + uint32_t timerId = mTimerCount; + mTimers.Put(timerId, aTask); + mTimerCount++; + + if (!SendSetTimer(timerId, aTimeoutMS)) { + return GMPGenericErr; + } + return GMPNoErr; +} + +bool +GMPTimerChild::RecvTimerExpired(const uint32_t& aTimerId) +{ + MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); + + GMPTask* task = mTimers.Get(aTimerId); + mTimers.Remove(aTimerId); + if (task) { + RunOnMainThread(task); + } + return true; +} + +} // namespace gmp +} // namespace mozilla diff --git a/content/media/gmp/GMPTimerChild.h b/content/media/gmp/GMPTimerChild.h new file mode 100644 index 00000000000..eb153dbd9f7 --- /dev/null +++ b/content/media/gmp/GMPTimerChild.h @@ -0,0 +1,46 @@ +/* -*- 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 GMPTimerChild_h_ +#define GMPTimerChild_h_ + +#include "mozilla/gmp/PGMPTimerChild.h" +#include "mozilla/Monitor.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "gmp-errors.h" +#include "gmp-platform.h" + +namespace mozilla { +namespace gmp { + +class GMPChild; + +class GMPTimerChild : public PGMPTimerChild +{ +public: + NS_INLINE_DECL_REFCOUNTING(GMPTimerChild) + + GMPTimerChild(GMPChild* aPlugin); + + GMPErr SetTimer(GMPTask* aTask, int64_t aTimeoutMS); + +protected: + // GMPTimerChild + virtual bool RecvTimerExpired(const uint32_t& aTimerId) MOZ_OVERRIDE; + +private: + ~GMPTimerChild(); + + nsDataHashtable mTimers; + uint32_t mTimerCount; + + GMPChild* mPlugin; +}; + +} // namespace gmp +} // namespace mozilla + +#endif // GMPTimerChild_h_ diff --git a/content/media/gmp/GMPTimerParent.cpp b/content/media/gmp/GMPTimerParent.cpp new file mode 100644 index 00000000000..69423d00b66 --- /dev/null +++ b/content/media/gmp/GMPTimerParent.cpp @@ -0,0 +1,88 @@ +/* -*- 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/. */ + +#include "GMPTimerParent.h" +#include "nsComponentManagerUtils.h" +#include "mozilla/unused.h" + +namespace mozilla { +namespace gmp { + +GMPTimerParent::GMPTimerParent(nsIThread* aGMPThread) + : mGMPThread(aGMPThread) +{ +} + +bool +GMPTimerParent::RecvSetTimer(const uint32_t& aTimerId, + const uint32_t& aTimeoutMs) +{ + MOZ_ASSERT(mGMPThread == NS_GetCurrentThread()); + + nsresult rv; + nsAutoPtr ctx(new Context()); + ctx->mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); + NS_ENSURE_SUCCESS(rv, true); + + ctx->mId = aTimerId; + rv = ctx->mTimer->SetTarget(mGMPThread); + NS_ENSURE_SUCCESS(rv, true); + ctx->mParent = this; + + rv = ctx->mTimer->InitWithFuncCallback(&GMPTimerParent::GMPTimerExpired, + ctx, + aTimeoutMs, + nsITimer::TYPE_ONE_SHOT); + NS_ENSURE_SUCCESS(rv, true); + + mTimers.PutEntry(ctx.forget()); + + return true; +} + +/*static */ +PLDHashOperator +GMPTimerParent::CancelTimers(nsPtrHashKey* aContext, void* aClosure) +{ + auto context = aContext->GetKey(); + context->mTimer->Cancel(); + delete context; + return PL_DHASH_NEXT; +} + +void +GMPTimerParent::ActorDestroy(ActorDestroyReason aWhy) +{ + MOZ_ASSERT(mGMPThread == NS_GetCurrentThread()); + mTimers.EnumerateEntries(GMPTimerParent::CancelTimers, nullptr); + mTimers.Clear(); +} + +/* static */ +void +GMPTimerParent::GMPTimerExpired(nsITimer *aTimer, void *aClosure) +{ + MOZ_ASSERT(aClosure); + nsAutoPtr ctx(static_cast(aClosure)); + MOZ_ASSERT(ctx->mParent); + if (ctx->mParent) { + ctx->mParent->TimerExpired(ctx); + } +} + +void +GMPTimerParent::TimerExpired(Context* aContext) +{ + MOZ_ASSERT(mGMPThread == NS_GetCurrentThread()); + + uint32_t id = aContext->mId; + mTimers.RemoveEntry(aContext); + if (id) { + unused << SendTimerExpired(id); + } +} + +} // namespace gmp +} // namespace mozilla diff --git a/content/media/gmp/GMPTimerParent.h b/content/media/gmp/GMPTimerParent.h new file mode 100644 index 00000000000..06385a6d1d2 --- /dev/null +++ b/content/media/gmp/GMPTimerParent.h @@ -0,0 +1,61 @@ +/* -*- 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 GMPTimerParent_h_ +#define GMPTimerParent_h_ + +#include "mozilla/gmp/PGMPTimerParent.h" +#include "nsITimer.h" +#include "nsCOMPtr.h" +#include "nsClassHashtable.h" +#include "nsHashKeys.h" +#include "nsAutoPtr.h" +#include "mozilla/Monitor.h" +#include "nsIThread.h" + +namespace mozilla { +namespace gmp { + +class GMPTimerParent : public PGMPTimerParent { +public: + NS_INLINE_DECL_REFCOUNTING(GMPTimerParent) + GMPTimerParent(nsIThread* aGMPThread); + +protected: + virtual bool RecvSetTimer(const uint32_t& aTimerId, + const uint32_t& aTimeoutMs) MOZ_OVERRIDE; + virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; + +private: + ~GMPTimerParent() {} + + static void GMPTimerExpired(nsITimer *aTimer, void *aClosure); + + struct Context { + Context() { + MOZ_COUNT_CTOR(Context); + } + ~Context() { + MOZ_COUNT_DTOR(Context); + } + nsCOMPtr mTimer; + nsRefPtr mParent; // Note: live timers keep the GMPTimerParent alive. + uint32_t mId; + }; + + static PLDHashOperator + CancelTimers(nsPtrHashKey* aContext, void* aClosure); + + void TimerExpired(Context* aContext); + + nsTHashtable> mTimers; + + nsCOMPtr mGMPThread; +}; + +} // namespace gmp +} // namespace mozilla + +#endif // GMPTimerParent_h_ diff --git a/content/media/gmp/PGMP.ipdl b/content/media/gmp/PGMP.ipdl index 80bebacdeb6..86dfe0bf2ca 100644 --- a/content/media/gmp/PGMP.ipdl +++ b/content/media/gmp/PGMP.ipdl @@ -8,6 +8,7 @@ include protocol PGMPVideoEncoder; include protocol PCrashReporter; include protocol PGMPDecryptor; include protocol PGMPAudioDecoder; +include protocol PGMPTimer; using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h"; @@ -21,9 +22,11 @@ intr protocol PGMP manages PGMPVideoDecoder; manages PGMPVideoEncoder; manages PCrashReporter; + manages PGMPTimer; parent: async PCrashReporter(NativeThreadId tid); + async PGMPTimer(); child: async PGMPAudioDecoder(); diff --git a/content/media/gmp/PGMPTimer.ipdl b/content/media/gmp/PGMPTimer.ipdl new file mode 100644 index 00000000000..fdcf694c0c7 --- /dev/null +++ b/content/media/gmp/PGMPTimer.ipdl @@ -0,0 +1,22 @@ +/* -*- 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/. */ + +include protocol PGMP; + +namespace mozilla { +namespace gmp { + +async protocol PGMPTimer +{ + manager PGMP; +child: + TimerExpired(uint32_t aTimerId); +parent: + SetTimer(uint32_t aTimerId, uint32_t aTimeoutMs); + __delete__(); +}; + +} // namespace gmp +} // namespace mozilla diff --git a/content/media/gmp/gmp-api/gmp-decryption.h b/content/media/gmp/gmp-api/gmp-decryption.h index 67de939f24e..7e3460a1d1c 100644 --- a/content/media/gmp/gmp-api/gmp-decryption.h +++ b/content/media/gmp/gmp-api/gmp-decryption.h @@ -257,6 +257,9 @@ public: // same GMPBuffer object and return it to Gecko by calling Decrypted(), // with the GMPNoErr successcode. If decryption fails, call Decrypted() // with a failure code, and an error event will fire on the media element. + // Note: When Decrypted() is called and aBuffer is passed back, aBuffer + // is deleted. Don't forget to call Decrypted(), as otherwise aBuffer's + // memory will leak! virtual void Decrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata) = 0; diff --git a/content/media/gmp/gmp-api/gmp-platform.h b/content/media/gmp/gmp-api/gmp-platform.h index ada2a449086..feffe0133da 100644 --- a/content/media/gmp/gmp-api/gmp-platform.h +++ b/content/media/gmp/gmp-api/gmp-platform.h @@ -41,7 +41,7 @@ class GMPTask { public: - virtual void Destroy() = 0; + virtual void Destroy() = 0; // Deletes object. virtual ~GMPTask() {} virtual void Run() = 0; }; @@ -50,7 +50,7 @@ class GMPThread { public: virtual ~GMPThread() {} virtual void Post(GMPTask* aTask) = 0; - virtual void Join() = 0; + virtual void Join() = 0; // Deletes object after join completes. }; class GMPMutex { @@ -58,6 +58,7 @@ public: virtual ~GMPMutex() {} virtual void Acquire() = 0; virtual void Release() = 0; + virtual void Destroy() = 0; // Deletes object. }; // Time is defined as the number of milliseconds since the @@ -72,6 +73,8 @@ typedef GMPErr (*GMPCreateRecordPtr)(const char* aRecordName, uint32_t aRecordNameSize, GMPRecord** aOutRecord, GMPRecordClient* aClient); + +// Call on main thread only. typedef GMPErr (*GMPSetTimerOnMainThreadPtr)(GMPTask* aTask, int64_t aTimeoutMS); typedef GMPErr (*GMPGetCurrentTimePtr)(GMPTimestamp* aOutTime); diff --git a/content/media/gmp/moz.build b/content/media/gmp/moz.build index c93fc8d6f45..2bf2083d1ee 100644 --- a/content/media/gmp/moz.build +++ b/content/media/gmp/moz.build @@ -46,6 +46,8 @@ EXPORTS += [ 'GMPProcessParent.h', 'GMPService.h', 'GMPSharedMemManager.h', + 'GMPTimerChild.h', + 'GMPTimerParent.h', 'GMPVideoDecoderChild.h', 'GMPVideoDecoderParent.h', 'GMPVideoDecoderProxy.h', @@ -72,6 +74,8 @@ UNIFIED_SOURCES += [ 'GMPProcessParent.cpp', 'GMPService.cpp', 'GMPSharedMemManager.cpp', + 'GMPTimerChild.cpp', + 'GMPTimerParent.cpp', 'GMPVideoDecoderChild.cpp', 'GMPVideoDecoderParent.cpp', 'GMPVideoEncodedFrameImpl.cpp', @@ -87,6 +91,7 @@ IPDL_SOURCES += [ 'PGMP.ipdl', 'PGMPAudioDecoder.ipdl', 'PGMPDecryptor.ipdl', + 'PGMPTimer.ipdl', 'PGMPVideoDecoder.ipdl', 'PGMPVideoEncoder.ipdl', ] diff --git a/content/media/mediasource/SourceBuffer.cpp b/content/media/mediasource/SourceBuffer.cpp index d2efdc535cc..58682ce3fe7 100644 --- a/content/media/mediasource/SourceBuffer.cpp +++ b/content/media/mediasource/SourceBuffer.cpp @@ -21,6 +21,7 @@ #include "nsThreadUtils.h" #include "prlog.h" #include "SubBufferDecoder.h" +#include "mozilla/Preferences.h" struct JSContext; class JSObject; @@ -166,8 +167,19 @@ public: } uint32_t chunk_size = BigEndian::readUint32(aData); - return chunk_size > 8 && aData[4] == 'f' && aData[5] == 't' && - aData[6] == 'y' && aData[7] == 'p'; + if (chunk_size < 8) { + return false; + } + + if (Preferences::GetBool("media.mediasource.allow_init_moov", false)) { + if (aData[4] == 'm' && aData[5] == 'o' && aData[6] == 'o' && + aData[7] == 'v') { + return true; + } + } + + return aData[4] == 'f' && aData[5] == 't' && aData[6] == 'y' && + aData[7] == 'p'; } }; diff --git a/content/media/test/test_mediatrack_consuming_mediastream.html b/content/media/test/test_mediatrack_consuming_mediastream.html index 38499b7c860..54a12ee1ed9 100644 --- a/content/media/test/test_mediatrack_consuming_mediastream.html +++ b/content/media/test/test_mediatrack_consuming_mediastream.html @@ -48,9 +48,7 @@ function startTest() { function checkTrackRemoved() { if (isPlaying) { - is(audioOnremovetrack, 1, 'Calls of onremovetrack on audioTracks should be 1.'); is(element.audioTracks.length, 0, 'The length of audioTracks should be 0.'); - is(videoOnremovetrack, 1, 'Calls of onremovetrack on videoTracks should be 1.'); is(element.videoTracks.length, 0, 'The length of videoTracks should be 0.'); } } diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index a7802568cd9..06128991445 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -358,6 +358,8 @@ DOMInterfaces = { 'URL': 'documentURIFromJS' } }, +# Note: we still need the worker descriptor here because +# XMLHttpRequest.send() uses it. { 'nativeType': 'JSObject', 'workers': True, @@ -827,14 +829,10 @@ DOMInterfaces = { 'nativeType': 'mozilla::dom::HTMLCanvasPrintState', }, -'MozChannel': [ -{ +'MozChannel': { 'nativeType': 'nsIChannel', 'notflattened': True }, -{ - 'workers': True, -}], 'MozCellBroadcast': { 'nativeType': 'mozilla::dom::CellBroadcast', diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index bf21e3ebbb5..4a7f25cc327 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -78,9 +78,7 @@ def idlTypeNeedsCycleCollection(type): def wantsAddProperty(desc): - return (desc.concrete and - desc.wrapperCache and - not desc.interface.getExtendedAttribute("Global")) + return (desc.concrete and desc.wrapperCache and not desc.isGlobal()) # We'll want to insert the indent at the beginnings of lines, but we @@ -372,7 +370,7 @@ class CGDOMJSClass(CGThing): JS_NULL_CLASS_EXT, JS_NULL_OBJECT_OPS """ - if self.descriptor.interface.getExtendedAttribute("Global"): + if self.descriptor.isGlobal(): classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_IMPLEMENTS_BARRIERS" traceHook = "JS_GlobalObjectTraceHook" reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS" @@ -415,7 +413,7 @@ JS_NULL_OBJECT_OPS newResolveHook = "(JSResolveOp)" + NEWRESOLVE_HOOK_NAME classFlags += " | JSCLASS_NEW_RESOLVE" enumerateHook = ENUMERATE_HOOK_NAME - elif self.descriptor.interface.getExtendedAttribute("Global"): + elif self.descriptor.isGlobal(): newResolveHook = "(JSResolveOp) mozilla::dom::ResolveGlobal" classFlags += " | JSCLASS_NEW_RESOLVE" enumerateHook = "mozilla::dom::EnumerateGlobal" @@ -1521,7 +1519,7 @@ def finalizeHook(descriptor, hookName, freeOp): finalize += "ClearWrapper(self, self);\n" if descriptor.interface.getExtendedAttribute('OverrideBuiltins'): finalize += "self->mExpandoAndGeneration.expando = JS::UndefinedValue();\n" - if descriptor.interface.getExtendedAttribute("Global"): + if descriptor.isGlobal(): finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), obj);\n" % freeOp finalize += ("AddForDeferredFinalization<%s, %s >(self);\n" % (descriptor.nativeType, DeferredFinalizeSmartPtr(descriptor))) @@ -2034,6 +2032,11 @@ def methodLength(method): return min(overloadLength(arguments) for retType, arguments in signatures) +def isMaybeExposedIn(member, descriptor): + # All we can say for sure is that if this is a worker descriptor + # and member is only exposed in windows, then it's not exposed. + return not descriptor.workers or member.exposureSet != set(["Window"]) + class MethodDefiner(PropertyDefiner): """ A class for defining methods on a prototype object. @@ -2051,7 +2054,8 @@ class MethodDefiner(PropertyDefiner): methods = [m for m in descriptor.interface.members if m.isMethod() and m.isStatic() == static and MemberIsUnforgeable(m, descriptor) == unforgeable and - not m.isIdentifierLess()] + not m.isIdentifierLess() and + isMaybeExposedIn(m, descriptor)] else: methods = [] self.chrome = [] @@ -2265,7 +2269,8 @@ class AttrDefiner(PropertyDefiner): if descriptor.interface.hasInterfacePrototypeObject() or static: attributes = [m for m in descriptor.interface.members if m.isAttr() and m.isStatic() == static and - MemberIsUnforgeable(m, descriptor) == unforgeable] + MemberIsUnforgeable(m, descriptor) == unforgeable and + isMaybeExposedIn(m, descriptor)] else: attributes = [] self.chrome = [m for m in attributes if isChromeOnly(m)] @@ -2348,7 +2353,8 @@ class ConstDefiner(PropertyDefiner): def __init__(self, descriptor, name): PropertyDefiner.__init__(self, descriptor, name) self.name = name - constants = [m for m in descriptor.interface.members if m.isConst()] + constants = [m for m in descriptor.interface.members if m.isConst() and + isMaybeExposedIn(m, descriptor)] self.chrome = [m for m in constants if isChromeOnly(m)] self.regular = [m for m in constants if not isChromeOnly(m)] @@ -2599,7 +2605,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): interfaceClass = "nullptr" interfaceCache = "nullptr" - isGlobal = self.descriptor.interface.getExtendedAttribute("Global") is not None + isGlobal = self.descriptor.isGlobal() is not None if not isGlobal and self.properties.hasNonChromeOnly(): properties = "&sNativeProperties" elif self.properties.hasNonChromeOnly(): @@ -7323,7 +7329,7 @@ class CGNewResolveHook(CGAbstractBindingMethod): """)) def definition_body(self): - if self.descriptor.interface.getExtendedAttribute("Global"): + if self.descriptor.isGlobal(): # Resolve standard classes prefix = dedent(""" if (!ResolveGlobal(cx, obj, id, objp)) { @@ -7372,7 +7378,7 @@ class CGEnumerateHook(CGAbstractBindingMethod): """)) def definition_body(self): - if self.descriptor.interface.getExtendedAttribute("Global"): + if self.descriptor.isGlobal(): # Enumerate standard classes prefix = dedent(""" if (!EnumerateGlobal(cx, obj)) { @@ -10471,6 +10477,8 @@ class CGDescriptor(CGThing): for m in descriptor.interface.members: if m.isMethod() and m.identifier.name == 'queryInterface': continue + if not isMaybeExposedIn(m, descriptor): + continue if m.isMethod() and m == descriptor.operations['Jsonifier']: hasJsonifier = True hasMethod = descriptor.needsSpecialGenericOps() @@ -10626,9 +10634,7 @@ class CGDescriptor(CGThing): if ((descriptor.interface.hasInterfaceObject() or descriptor.interface.getNavigatorProperty()) and not descriptor.interface.isExternal() and - descriptor.isExposedConditionally() and - # Workers stuff is never conditional - not descriptor.workers): + descriptor.isExposedConditionally()): cgThings.append(CGConstructorEnabled(descriptor)) if (descriptor.interface.hasMembersInSlots() and @@ -10666,7 +10672,7 @@ class CGDescriptor(CGThing): if descriptor.interface.hasMembersInSlots(): cgThings.append(CGUpdateMemberSlotsMethod(descriptor)) - if descriptor.interface.getExtendedAttribute("Global"): + if descriptor.isGlobal(): assert descriptor.wrapperCache cgThings.append(CGWrapGlobalMethod(descriptor, properties)) elif descriptor.wrapperCache: @@ -11325,6 +11331,48 @@ class CGDictionary(CGThing): return all(isTypeCopyConstructible(m.type) for m in dictionary.members) +class CGRegisterWorkerBindings(CGAbstractMethod): + def __init__(self, config): + CGAbstractMethod.__init__(self, None, 'RegisterWorkerBindings', 'bool', + [Argument('JSContext*', 'aCx'), + Argument('JS::Handle', 'aObj')]) + self.config = config + + def definition_body(self): + # We have to be a bit careful: Some of the interfaces we want to expose + # in workers only have one descriptor, while others have both a worker + # and a non-worker descriptor. When both are present we want the worker + # descriptor, but otherwise we want whatever descriptor we've got. + descriptors = self.config.getDescriptors(hasInterfaceObject=True, + isExposedInAllWorkers=True, + register=True, + skipGen=False, + workers=True) + workerDescriptorIfaceNames = set(d.interface.identifier.name for + d in descriptors) + descriptors.extend( + filter( + lambda d: d.interface.identifier.name not in workerDescriptorIfaceNames, + self.config.getDescriptors(hasInterfaceObject=True, + isExposedInAllWorkers=True, + register=True, + skipGen=False, + workers=False))) + conditions = [] + for desc in descriptors: + bindingNS = toBindingNamespace(desc.name) + condition = "!%s::GetConstructorObject(aCx, aObj)" % bindingNS + if desc.isExposedConditionally(): + condition = ( + "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + + condition) + conditions.append(condition) + lines = [CGIfWrapper(CGGeneric("return false;\n"), condition) for + condition in conditions] + lines.append(CGGeneric("return true;\n")) + return CGList(lines, "\n").define() + + class CGRegisterProtos(CGAbstractMethod): def __init__(self, config): CGAbstractMethod.__init__(self, None, 'Register', 'void', @@ -13716,6 +13764,33 @@ class GlobalGenRoots(): # Done. return curr + @staticmethod + def RegisterWorkerBindings(config): + + # TODO - Generate the methods we want + curr = CGRegisterWorkerBindings(config) + + # Wrap all of that in our namespaces. + curr = CGNamespace.build(['mozilla', 'dom'], + CGWrapper(curr, post='\n')) + curr = CGWrapper(curr, post='\n') + + # Add the includes + defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface) + for desc in config.getDescriptors(hasInterfaceObject=True, + register=True, + isExposedInAllWorkers=True, + skipGen=False)] + + curr = CGHeaders([], [], [], [], [], defineIncludes, + 'RegisterWorkerBindings', curr) + + # Add include guards. + curr = CGIncludeGuard('RegisterWorkerBindings', curr) + + # Done. + return curr + @staticmethod def UnionTypes(config): diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py index 6fca0b42d28..0fa5b5328f8 100644 --- a/dom/bindings/Configuration.py +++ b/dom/bindings/Configuration.py @@ -138,6 +138,8 @@ class Configuration: getter = lambda x: x.interface.isJSImplemented() elif key == 'isNavigatorProperty': getter = lambda x: x.interface.getNavigatorProperty() != None + elif key == 'isExposedInAllWorkers': + getter = lambda x: not x.interface.isExternal() and "Worker" in x.interface._exposureGlobalNames else: # Have to watch out: just closing over "key" is not enough, # since we're about to mutate its value @@ -563,6 +565,14 @@ class Descriptor(DescriptorProvider): """ return self.hasXPConnectImpls or self.interface.isOnGlobalProtoChain() + def isGlobal(self): + """ + Returns true if this is the primary interface for a global object + of some sort. + """ + return (self.interface.getExtendedAttribute("Global") or + self.interface.getExtendedAttribute("PrimaryGlobal")) + # Some utility methods def getTypesFromDescriptor(descriptor): """ diff --git a/dom/bindings/mozwebidlcodegen/__init__.py b/dom/bindings/mozwebidlcodegen/__init__.py index 21e1674ef23..0975101ad79 100644 --- a/dom/bindings/mozwebidlcodegen/__init__.py +++ b/dom/bindings/mozwebidlcodegen/__init__.py @@ -131,6 +131,7 @@ class WebIDLCodegenManager(LoggingMixin): 'GeneratedAtomList.h', 'PrototypeList.h', 'RegisterBindings.h', + 'RegisterWorkerBindings.h', 'UnionConversions.h', 'UnionTypes.h', } @@ -138,6 +139,7 @@ class WebIDLCodegenManager(LoggingMixin): # Global parser derived definition files. GLOBAL_DEFINE_FILES = { 'RegisterBindings.cpp', + 'RegisterWorkerBindings.cpp', 'UnionTypes.cpp', 'PrototypeList.cpp', } diff --git a/dom/bindings/parser/WebIDL.py b/dom/bindings/parser/WebIDL.py index a314ca7a1ab..acd8952d019 100644 --- a/dom/bindings/parser/WebIDL.py +++ b/dom/bindings/parser/WebIDL.py @@ -9,6 +9,7 @@ import re import os import traceback import math +from collections import defaultdict # Machinery @@ -219,6 +220,12 @@ class IDLScope(IDLObject): self._name = None self._dict = {} + self.globalNames = set() + # A mapping from global name to the set of global interfaces + # that have that global name. + self.globalNameMapping = defaultdict(set) + self.primaryGlobalAttr = None + self.primaryGlobalName = None def __str__(self): return self.QName() @@ -482,24 +489,85 @@ class IDLExternalInterface(IDLObjectWithIdentifier): def _getDependentObjects(self): return set() +class IDLPartialInterface(IDLObject): + def __init__(self, location, name, members, nonPartialInterface): + assert isinstance(name, IDLUnresolvedIdentifier) + + IDLObject.__init__(self, location) + self.identifier = name + self.members = members + # propagatedExtendedAttrs are the ones that should get + # propagated to our non-partial interface. + self.propagatedExtendedAttrs = [] + self._nonPartialInterface = nonPartialInterface + self._finished = False + nonPartialInterface.addPartialInterface(self) + + def addExtendedAttributes(self, attrs): + for attr in attrs: + identifier = attr.identifier() + + if identifier in ["Constructor", "NamedConstructor"]: + self.propagatedExtendedAttrs.append(attr) + elif identifier == "Exposed": + # This just gets propagated to all our members. + for member in self.members: + if len(member._exposureGlobalNames) != 0: + raise WebIDLError("[Exposed] specified on both a " + "partial interface member and on the " + "partial interface itself", + [member.location, attr.location]) + member.addExtendedAttributes([attr]) + else: + raise WebIDLError("Unknown extended attribute %s on partial " + "interface" % identifier, + [attr.location]) + + def finish(self, scope): + if self._finished: + return + self._finished = True + # Need to make sure our non-partial interface gets finished so it can + # report cases when we only have partial interfaces. + self._nonPartialInterface.finish(scope) + + def validate(self): + pass + + +def convertExposedAttrToGlobalNameSet(exposedAttr, targetSet): + assert len(targetSet) == 0 + if exposedAttr.hasValue(): + targetSet.add(exposedAttr.value()) + else: + assert exposedAttr.hasArgs() + targetSet.update(exposedAttr.args()) + +def globalNameSetToExposureSet(globalScope, nameSet, exposureSet): + for name in nameSet: + exposureSet.update(globalScope.globalNameMapping[name]) + class IDLInterface(IDLObjectWithScope): def __init__(self, location, parentScope, name, parent, members, - isPartial): + isKnownNonPartial): assert isinstance(parentScope, IDLScope) assert isinstance(name, IDLUnresolvedIdentifier) - assert not isPartial or not parent + assert isKnownNonPartial or not parent + assert isKnownNonPartial or len(members) == 0 self.parent = None self._callback = False self._finished = False self.members = [] + self._partialInterfaces = [] + self._extendedAttrDict = {} # namedConstructors needs deterministic ordering because bindings code # outputs the constructs in the order that namedConstructors enumerates # them. self.namedConstructors = list() self.implementedInterfaces = set() self._consequential = False - self._isPartial = True + self._isKnownNonPartial = False # self.interfacesBasedOnSelf is the set of interfaces that inherit from # self or have self as a consequential interface, including self itself. # Used for distinguishability checking. @@ -514,14 +582,16 @@ class IDLInterface(IDLObjectWithScope): self.totalMembersInSlots = 0 # Tracking of the number of own own members we have in slots self._ownMembersInSlots = 0 + # _exposureGlobalNames are the global names listed in our [Exposed] + # extended attribute. exposureSet is the exposure set as defined in the + # Web IDL spec: it contains interface names. + self._exposureGlobalNames = set() + self.exposureSet = set() IDLObjectWithScope.__init__(self, location, parentScope, name) - if not isPartial: + if isKnownNonPartial: self.setNonPartial(location, parent, members) - else: - # Just remember our members for now - self.members = members def __str__(self): return "Interface '%s'" % self.identifier.name @@ -553,11 +623,42 @@ class IDLInterface(IDLObjectWithScope): self._finished = True - if self._isPartial: + if not self._isKnownNonPartial: raise WebIDLError("Interface %s does not have a non-partial " "declaration" % self.identifier.name, [self.location]) + # Verify that our [Exposed] value, if any, makes sense. + for globalName in self._exposureGlobalNames: + if globalName not in scope.globalNames: + raise WebIDLError("Unknown [Exposed] value %s" % globalName, + [self.location]) + + if len(self._exposureGlobalNames) == 0: + self._exposureGlobalNames.add(scope.primaryGlobalName) + + globalNameSetToExposureSet(scope, self._exposureGlobalNames, + self.exposureSet) + + # Now go ahead and merge in our partial interfaces. + for partial in self._partialInterfaces: + partial.finish(scope) + self.addExtendedAttributes(partial.propagatedExtendedAttrs) + self.members.extend(partial.members) + + # Now that we've merged in our partial interfaces, set the + # _exposureGlobalNames on any members that don't have it set yet. Note + # that any partial interfaces that had [Exposed] set have already set up + # _exposureGlobalNames on all the members coming from them, so this is + # just implementing the "members default to interface that defined them" + # and "partial interfaces default to interface they're a partial for" + # rules from the spec. + for m in self.members: + # If m, or the partial interface m came from, had [Exposed] + # specified, it already has a nonempty exposure global names set. + if len(m._exposureGlobalNames) == 0: + m._exposureGlobalNames.update(self._exposureGlobalNames) + assert not self.parent or isinstance(self.parent, IDLIdentifierPlaceholder) parent = self.parent.finish(scope) if self.parent else None if parent and isinstance(parent, IDLExternalInterface): @@ -595,8 +696,10 @@ class IDLInterface(IDLObjectWithScope): self.totalMembersInSlots = self.parent.totalMembersInSlots - # Interfaces with [Global] must not have anything inherit from them - if self.parent.getExtendedAttribute("Global"): + # Interfaces with [Global] or [PrimaryGlobal] must not + # have anything inherit from them + if (self.parent.getExtendedAttribute("Global") or + self.parent.getExtendedAttribute("PrimaryGlobal")): # Note: This is not a self.parent.isOnGlobalProtoChain() check # because ancestors of a [Global] interface can have other # descendants. @@ -604,6 +707,14 @@ class IDLInterface(IDLObjectWithScope): "inheriting from it", [self.location, self.parent.location]) + # Make sure that we're not exposed in places where our parent is not + if not self.exposureSet.issubset(self.parent.exposureSet): + raise WebIDLError("Interface %s is exposed in globals where its " + "parent interface %s is not exposed." % + (self.identifier.name, + self.parent.identifier.name), + [self.location, self.parent.location]) + # 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. @@ -649,6 +760,14 @@ class IDLInterface(IDLObjectWithScope): for member in self.members: member.finish(scope) + # Now that we've finished our members, which has updated their exposure + # sets, make sure they aren't exposed in places where we are not. + for member in self.members: + if not member.exposureSet.issubset(self.exposureSet): + raise WebIDLError("Interface member has larger exposure set " + "than the interface itself", + [member.location, self.location]) + ctor = self.ctor() if ctor is not None: ctor.finish(scope) @@ -669,6 +788,12 @@ class IDLInterface(IDLObjectWithScope): key=lambda x: x.identifier.name): # Flag the interface as being someone's consequential interface iface.setIsConsequentialInterfaceOf(self) + # Verify that we're not exposed somewhere where iface is not exposed + if not self.exposureSet.issubset(iface.exposureSet): + raise WebIDLError("Interface %s is exposed in globals where its " + "consequential interface %s is not exposed." % + (self.identifier.name, iface.identifier.name), + [self.location, iface.location]) additionalMembers = iface.originalMembers; for additionalMember in additionalMembers: for member in self.members: @@ -891,6 +1016,12 @@ class IDLInterface(IDLObjectWithScope): attr = fowardAttr putForwards = attr.getExtendedAttribute("PutForwards") + if (self.getExtendedAttribute("Pref") and + self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])): + raise WebIDLError("[Pref] used on an member that is not %s-only" % + self.parentScope.primaryGlobalName, + [self.location]) + def isInterface(self): return True @@ -948,7 +1079,6 @@ class IDLInterface(IDLObjectWithScope): return not self.isCallback() and self.getUserData('hasConcreteDescendant', False) def addExtendedAttributes(self, attrs): - self._extendedAttrDict = {} for attr in attrs: identifier = attr.identifier() @@ -1049,9 +1179,29 @@ class IDLInterface(IDLObjectWithScope): "an interface with inherited interfaces", [attr.location, self.location]) elif identifier == "Global": + if attr.hasValue(): + self.globalNames = [ attr.value() ] + elif attr.hasArgs(): + self.globalNames = attr.args() + else: + self.globalNames = [ self.identifier.name ] + self.parentScope.globalNames.update(self.globalNames) + for globalName in self.globalNames: + self.parentScope.globalNameMapping[globalName].add(self.identifier.name) + self._isOnGlobalProtoChain = True + elif identifier == "PrimaryGlobal": if not attr.noArguments(): - raise WebIDLError("[Global] must take no arguments", + raise WebIDLError("[PrimaryGlobal] must take no arguments", [attr.location]) + if self.parentScope.primaryGlobalAttr is not None: + raise WebIDLError( + "[PrimaryGlobal] specified twice", + [attr.location, + self.parentScope.primaryGlobalAttr.location]) + self.parentScope.primaryGlobalAttr = attr + self.parentScope.primaryGlobalName = self.identifier.name + self.parentScope.globalNames.add(self.identifier.name) + self.parentScope.globalNameMapping[self.identifier.name].add(self.identifier.name) self._isOnGlobalProtoChain = True elif (identifier == "NeedNewResolve" or identifier == "OverrideBuiltins" or @@ -1063,6 +1213,9 @@ class IDLInterface(IDLObjectWithScope): if not attr.noArguments(): raise WebIDLError("[%s] must take no arguments" % identifier, [attr.location]) + elif identifier == "Exposed": + convertExposedAttrToGlobalNameSet(attr, + self._exposureGlobalNames) elif (identifier == "Pref" or identifier == "JSImplementation" or identifier == "HeaderFile" or @@ -1139,11 +1292,11 @@ class IDLInterface(IDLObjectWithScope): def setNonPartial(self, location, parent, members): assert not parent or isinstance(parent, IDLIdentifierPlaceholder) - if not self._isPartial: + if self._isKnownNonPartial: raise WebIDLError("Two non-partial definitions for the " "same interface", [location, self.location]) - self._isPartial = False + self._isKnownNonPartial = True # Now make it look like we were parsed at this new location, since # that's the place where the interface is "really" defined self.location = location @@ -1152,6 +1305,10 @@ class IDLInterface(IDLObjectWithScope): # Put the new members at the beginning self.members = members + self.members + def addPartialInterface(self, partial): + assert self.identifier.name == partial.identifier.name + self._partialInterfaces.append(partial) + def getJSImplementation(self): classId = self.getExtendedAttribute("JSImplementation") if not classId: @@ -1535,6 +1692,9 @@ class IDLType(IDLObject): raise TypeError("Can't tell whether a generic type is or is not " "distinguishable from other things") + def isExposedInAllOf(self, exposureSet): + return True + class IDLUnresolvedType(IDLType): """ Unresolved types are interface types @@ -1842,6 +2002,9 @@ class IDLMozMapType(IDLType): return (other.isPrimitive() or other.isString() or other.isEnum() or other.isDate() or other.isNonCallbackInterface() or other.isSequence()) + def isExposedInAllOf(self, exposureSet): + return self.inner.unroll().isExposedInAllOf(exposureSet) + def _getDependentObjects(self): return self.inner._getDependentObjects() @@ -1952,6 +2115,14 @@ class IDLUnionType(IDLType): return False return True + def isExposedInAllOf(self, exposureSet): + # We could have different member types in different globals. Just make sure that each thing in exposureSet has one of our member types exposed in it. + for globalName in exposureSet: + if not any(t.unroll().isExposedInAllOf(set([globalName])) for t + in self.flatMemberTypes): + return False + return True + def _getDependentObjects(self): return set(self.memberTypes) @@ -2291,6 +2462,20 @@ class IDLWrapperType(IDLType): assert other.isObject() return False + def isExposedInAllOf(self, exposureSet): + if not self.isInterface(): + return True + iface = self.inner + if iface.isExternal(): + # Let's say true, though ideally we'd only do this when + # exposureSet contains the primary global's name. + return True + if (iface.identifier.name == "Promise" and + # Check the internal type + not self._promiseInnerType.unroll().isExposedInAllOf(exposureSet)): + return False + return iface.exposureSet.issuperset(exposureSet) + def _getDependentObjects(self): # NB: The codegen for an interface type depends on # a) That the identifier is in fact an interface (as opposed to @@ -2805,6 +2990,11 @@ class IDLInterfaceMember(IDLObjectWithIdentifier): IDLObjectWithIdentifier.__init__(self, location, None, identifier) self.tag = tag self._extendedAttrDict = {} + # _exposureGlobalNames are the global names listed in our [Exposed] + # extended attribute. exposureSet is the exposure set as defined in the + # Web IDL spec: it contains interface names. + self._exposureGlobalNames = set() + self.exposureSet = set() def isMethod(self): return self.tag == IDLInterfaceMember.Tags.Method @@ -2827,6 +3017,22 @@ class IDLInterfaceMember(IDLObjectWithIdentifier): def getExtendedAttribute(self, name): return self._extendedAttrDict.get(name, None) + def finish(self, scope): + for globalName in self._exposureGlobalNames: + if globalName not in scope.globalNames: + raise WebIDLError("Unknown [Exposed] value %s" % globalName, + [self.location]) + globalNameSetToExposureSet(scope, self._exposureGlobalNames, + self.exposureSet) + self._scope = scope + + def validate(self): + if (self.getExtendedAttribute("Pref") and + self.exposureSet != set([self._scope.primaryGlobalName])): + raise WebIDLError("[Pref] used on an interface member that is not " + "%s-only" % self._scope.primaryGlobalName, + [self.location]) + class IDLConst(IDLInterfaceMember): def __init__(self, location, identifier, type, value): IDLInterfaceMember.__init__(self, location, identifier, @@ -2847,6 +3053,8 @@ class IDLConst(IDLInterfaceMember): return "'%s' const '%s'" % (self.type, self.identifier) def finish(self, scope): + IDLInterfaceMember.finish(self, scope) + if not self.type.isComplete(): type = self.type.complete(scope) if not type.isPrimitive() and not type.isString(): @@ -2865,7 +3073,23 @@ class IDLConst(IDLInterfaceMember): self.value = coercedValue def validate(self): - pass + IDLInterfaceMember.validate(self) + + def handleExtendedAttribute(self, attr): + identifier = attr.identifier() + if identifier == "Exposed": + convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) + elif (identifier == "Pref" or + identifier == "ChromeOnly" or + identifier == "Func" or + identifier == "AvailableIn" or + identifier == "CheckPermissions"): + # Known attributes that we don't need to do anything with here + pass + else: + raise WebIDLError("Unknown extended attribute %s on constant" % identifier, + [attr.location]) + IDLInterfaceMember.handleExtendedAttribute(self, attr) def _getDependentObjects(self): return set([self.type, self.value]) @@ -2903,6 +3127,8 @@ class IDLAttribute(IDLInterfaceMember): return "'%s' attribute '%s'" % (self.type, self.identifier) def finish(self, scope): + IDLInterfaceMember.finish(self, scope) + if not self.type.isComplete(): t = self.type.complete(scope) @@ -2961,6 +3187,8 @@ class IDLAttribute(IDLInterfaceMember): [self.location]) def validate(self): + IDLInterfaceMember.validate(self) + if ((self.getExtendedAttribute("Cached") or self.getExtendedAttribute("StoreInSlot")) and not self.getExtendedAttribute("Constant") and @@ -2976,6 +3204,10 @@ class IDLAttribute(IDLInterfaceMember): "sequence-valued, dictionary-valued, and " "MozMap-valued attributes", [self.location]) + if not self.type.unroll().isExposedInAllOf(self.exposureSet): + raise WebIDLError("Attribute returns a type that is not exposed " + "everywhere where the attribute is exposed", + [self.location]) def handleExtendedAttribute(self, attr): identifier = attr.identifier() @@ -3078,6 +3310,8 @@ class IDLAttribute(IDLInterfaceMember): raise WebIDLError("[LenientThis] is not allowed in combination " "with [%s]" % identifier, [attr.location, self.location]) + elif identifier == "Exposed": + convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) elif (identifier == "Pref" or identifier == "SetterThrows" or identifier == "Pure" or @@ -3494,6 +3728,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope): self._overloads] def finish(self, scope): + IDLInterfaceMember.finish(self, scope) + if self.getExtendedAttribute("FeatureDetectible"): if not (self.getExtendedAttribute("Func") or self.getExtendedAttribute("AvailableIn") or @@ -3577,6 +3813,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope): if len(self.signaturesForArgCount(i)) != 0 ] def validate(self): + IDLInterfaceMember.validate(self) + # Make sure our overloads are properly distinguishable and don't have # different argument types before the distinguishing args. for argCount in self.allowedArgCounts: @@ -3596,6 +3834,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope): distinguishingIndex), [self.location, overload.location]) + for overload in self._overloads: + if not overload.returnType.unroll().isExposedInAllOf(self.exposureSet): + raise WebIDLError("Overload returns a type that is not exposed " + "everywhere where the method is exposed", + [overload.location]) + def overloadsForArgCount(self, argc): return [overload for overload in self._overloads if len(overload.arguments) == argc or @@ -3674,6 +3918,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope): raise WebIDLError("[LenientFloat] used on an operation with no " "restricted float type arguments", [attr.location, self.location]) + elif identifier == "Exposed": + convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) elif (identifier == "Pure" or identifier == "CrossOriginCallable" or identifier == "WebGLHandlesContextLoss" or @@ -3920,6 +4166,45 @@ class Tokenizer(object): lextab='webidllex', reflags=re.DOTALL) +class SqueakyCleanLogger(object): + errorWhitelist = [ + # Web IDL defines the WHITESPACE token, but doesn't actually + # use it ... so far. + "Token 'WHITESPACE' defined, but not used", + # And that means we have an unused token + "There is 1 unused token", + # Web IDL defines a OtherOrComma rule that's only used in + # ExtendedAttributeInner, which we don't use yet. + "Rule 'OtherOrComma' defined, but not used", + # And an unused rule + "There is 1 unused rule", + # And the OtherOrComma grammar symbol is unreachable. + "Symbol 'OtherOrComma' is unreachable", + # Which means the Other symbol is unreachable. + "Symbol 'Other' is unreachable", + ] + def __init__(self): + self.errors = [] + def debug(self, msg, *args, **kwargs): + pass + info = debug + def warning(self, msg, *args, **kwargs): + if msg == "%s:%d: Rule '%s' defined, but not used": + # Munge things so we don't have to hardcode filenames and + # line numbers in our whitelist. + whitelistmsg = "Rule '%s' defined, but not used" + whitelistargs = args[2:] + else: + whitelistmsg = msg + whitelistargs = args + if (whitelistmsg % whitelistargs) not in SqueakyCleanLogger.errorWhitelist: + self.errors.append(msg % args) + error = warning + + def reportGrammarErrors(self): + if self.errors: + raise WebIDLError("\n".join(self.errors), []) + class Parser(Tokenizer): def getLocation(self, p, i): return Location(self.lexer, p.lineno(i), p.lexpos(i), self._filename) @@ -3995,10 +4280,11 @@ class Parser(Tokenizer): parent = p[3] try: - if self.globalScope()._lookupIdentifier(identifier): - p[0] = self.globalScope()._lookupIdentifier(identifier) + existingObj = self.globalScope()._lookupIdentifier(identifier) + if existingObj: + p[0] = existingObj if not isinstance(p[0], IDLInterface): - raise WebIDLError("Partial interface has the same name as " + raise WebIDLError("Interface has the same name as " "non-interface object", [location, p[0].location]) p[0].setNonPartial(location, parent, members) @@ -4009,7 +4295,7 @@ class Parser(Tokenizer): pass p[0] = IDLInterface(location, self.globalScope(), identifier, parent, - members, isPartial=False) + members, isKnownNonPartial=True) def p_InterfaceForwardDecl(self, p): """ @@ -4042,26 +4328,26 @@ class Parser(Tokenizer): identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3]) members = p[5] + nonPartialInterface = None try: - if self.globalScope()._lookupIdentifier(identifier): - p[0] = self.globalScope()._lookupIdentifier(identifier) - if not isinstance(p[0], IDLInterface): + nonPartialInterface = self.globalScope()._lookupIdentifier(identifier) + if nonPartialInterface: + if not isinstance(nonPartialInterface, IDLInterface): raise WebIDLError("Partial interface has the same name as " "non-interface object", - [location, p[0].location]) - # Just throw our members into the existing IDLInterface. If we - # have extended attributes, those will get added to it - # automatically. - p[0].members.extend(members) - return + [location, nonPartialInterface.location]) except Exception, ex: if isinstance(ex, WebIDLError): raise ex pass - p[0] = IDLInterface(location, self.globalScope(), identifier, None, - members, isPartial=True) - pass + if not nonPartialInterface: + nonPartialInterface = IDLInterface(location, self.globalScope(), + identifier, None, + [], isKnownNonPartial=False) + partialInterface = IDLPartialInterface(location, identifier, members, + nonPartialInterface) + p[0] = partialInterface def p_Inheritance(self, p): """ @@ -4726,6 +5012,7 @@ class Parser(Tokenizer): | ExtendedAttributeArgList | ExtendedAttributeIdent | ExtendedAttributeNamedArgList + | ExtendedAttributeIdentList """ p[0] = IDLExtendedAttribute(self.getLocation(p, 1), p[1]) @@ -5200,6 +5487,34 @@ class Parser(Tokenizer): """ p[0] = (p[1], p[3], p[5]) + def p_ExtendedAttributeIdentList(self, p): + """ + ExtendedAttributeIdentList : IDENTIFIER EQUALS LPAREN IdentifierList RPAREN + """ + p[0] = (p[1], p[4]) + + def p_IdentifierList(self, p): + """ + IdentifierList : IDENTIFIER Identifiers + """ + idents = list(p[2]) + idents.insert(0, p[1]) + p[0] = idents + + def p_IdentifiersList(self, p): + """ + Identifiers : COMMA IDENTIFIER Identifiers + """ + idents = list(p[3]) + idents.insert(0, p[2]) + p[0] = idents + + def p_IdentifiersEmpty(self, p): + """ + Identifiers : + """ + p[0] = [] + def p_error(self, p): if not p: raise WebIDLError("Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both", @@ -5209,10 +5524,12 @@ class Parser(Tokenizer): def __init__(self, outputdir='', lexer=None): Tokenizer.__init__(self, outputdir, lexer) + + logger = SqueakyCleanLogger() self.parser = yacc.yacc(module=self, outputdir=outputdir, tabmodule='webidlyacc', - errorlog=yacc.NullLogger() + errorlog=logger # Pickling the grammar is a speedup in # some cases (older Python?) but a # significant slowdown in others. @@ -5220,7 +5537,13 @@ class Parser(Tokenizer): # becomes a speedup again. # , picklefile='WebIDLGrammar.pkl' ) + logger.reportGrammarErrors() + self._globalScope = IDLScope(BuiltinLocation(""), None, None) + # To make our test harness work, pretend like we have a primary global already. Note that we _don't_ set _globalScope.primaryGlobalAttr, so we'll still be able to detect multiple PrimaryGlobal extended attributes. + self._globalScope.primaryGlobalName = "FakeTestPrimaryGlobal" + self._globalScope.globalNames.add("FakeTestPrimaryGlobal") + self._globalScope.globalNameMapping["FakeTestPrimaryGlobal"].add("FakeTestPrimaryGlobal") self._installBuiltins(self._globalScope) self._productions = [] diff --git a/dom/bindings/parser/tests/test_exposed_extended_attribute.py b/dom/bindings/parser/tests/test_exposed_extended_attribute.py new file mode 100644 index 00000000000..48957098bfe --- /dev/null +++ b/dom/bindings/parser/tests/test_exposed_extended_attribute.py @@ -0,0 +1,222 @@ +import WebIDL + +def WebIDLTest(parser, harness): + parser.parse(""" + [PrimaryGlobal] interface Foo {}; + [Global=(Bar1,Bar2)] interface Bar {}; + [Global=Baz2] interface Baz {}; + + [Exposed=(Foo,Bar1)] + interface Iface { + void method1(); + + [Exposed=Bar1] + readonly attribute any attr; + }; + + [Exposed=Foo] + partial interface Iface { + void method2(); + }; + """) + + results = parser.finish() + + harness.check(len(results), 5, "Should know about five things"); + iface = results[3] + harness.ok(isinstance(iface, WebIDL.IDLInterface), + "Should have an interface here"); + members = iface.members + harness.check(len(members), 3, "Should have three members") + + harness.ok(members[0].exposureSet == set(["Foo", "Bar"]), + "method1 should have the right exposure set") + harness.ok(members[0]._exposureGlobalNames == set(["Foo", "Bar1"]), + "method1 should have the right exposure global names") + + harness.ok(members[1].exposureSet == set(["Bar"]), + "attr should have the right exposure set") + harness.ok(members[1]._exposureGlobalNames == set(["Bar1"]), + "attr should have the right exposure global names") + + harness.ok(members[2].exposureSet == set(["Foo"]), + "method2 should have the right exposure set") + harness.ok(members[2]._exposureGlobalNames == set(["Foo"]), + "method2 should have the right exposure global names") + + harness.ok(iface.exposureSet == set(["Foo", "Bar"]), + "Iface should have the right exposure set") + harness.ok(iface._exposureGlobalNames == set(["Foo", "Bar1"]), + "Iface should have the right exposure global names") + + parser = parser.reset() + parser.parse(""" + [PrimaryGlobal] interface Foo {}; + [Global=(Bar1,Bar2)] interface Bar {}; + [Global=Baz2] interface Baz {}; + + interface Iface2 { + void method3(); + }; + """) + results = parser.finish() + + harness.check(len(results), 4, "Should know about four things"); + iface = results[3] + harness.ok(isinstance(iface, WebIDL.IDLInterface), + "Should have an interface here"); + members = iface.members + harness.check(len(members), 1, "Should have one member") + + harness.ok(members[0].exposureSet == set(["Foo"]), + "method3 should have the right exposure set") + harness.ok(members[0]._exposureGlobalNames == set(["Foo"]), + "method3 should have the right exposure global names") + + harness.ok(iface.exposureSet == set(["Foo"]), + "Iface2 should have the right exposure set") + harness.ok(iface._exposureGlobalNames == set(["Foo"]), + "Iface2 should have the right exposure global names") + + parser = parser.reset() + parser.parse(""" + [PrimaryGlobal] interface Foo {}; + [Global=(Bar1,Bar2)] interface Bar {}; + [Global=Baz2] interface Baz {}; + + [Exposed=Foo] + interface Iface3 { + void method4(); + }; + + [Exposed=(Foo,Bar1)] + interface Mixin { + void method5(); + }; + + Iface3 implements Mixin; + """) + results = parser.finish() + harness.check(len(results), 6, "Should know about six things"); + iface = results[3] + harness.ok(isinstance(iface, WebIDL.IDLInterface), + "Should have an interface here"); + members = iface.members + harness.check(len(members), 2, "Should have two members") + + harness.ok(members[0].exposureSet == set(["Foo"]), + "method4 should have the right exposure set") + harness.ok(members[0]._exposureGlobalNames == set(["Foo"]), + "method4 should have the right exposure global names") + + harness.ok(members[1].exposureSet == set(["Foo", "Bar"]), + "method5 should have the right exposure set") + harness.ok(members[1]._exposureGlobalNames == set(["Foo", "Bar1"]), + "method5 should have the right exposure global names") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [Exposed=Foo] + interface Bar { + }; + """) + + results = parser.finish() + except Exception,x: + threw = True + + harness.ok(threw, "Should have thrown on invalid Exposed value on interface.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Bar { + [Exposed=Foo] + readonly attribute bool attr; + }; + """) + + results = parser.finish() + except Exception,x: + threw = True + + harness.ok(threw, "Should have thrown on invalid Exposed value on attribute.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Bar { + [Exposed=Foo] + void operation(); + }; + """) + + results = parser.finish() + except Exception,x: + threw = True + + harness.ok(threw, "Should have thrown on invalid Exposed value on operation.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + interface Bar { + [Exposed=Foo] + const long constant = 5; + }; + """) + + results = parser.finish() + except Exception,x: + threw = True + + harness.ok(threw, "Should have thrown on invalid Exposed value on constant.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [Global] interface Foo {}; + [Global] interface Bar {}; + + [Exposed=Foo] + interface Baz { + [Exposed=Bar] + void method(); + }; + """) + + results = parser.finish() + except Exception,x: + threw = True + + harness.ok(threw, "Should have thrown on member exposed where its interface is not.") + + parser = parser.reset() + threw = False + try: + parser.parse(""" + [Global] interface Foo {}; + [Global] interface Bar {}; + + [Exposed=Foo] + interface Baz { + void method(); + }; + + [Exposed=Bar] + interface Mixin {}; + + Baz implements Mixin; + """) + + results = parser.finish() + except Exception,x: + threw = True + + harness.ok(threw, "Should have thrown on LHS of implements being exposed where RHS is not.") diff --git a/dom/bindings/parser/tests/test_interface.py b/dom/bindings/parser/tests/test_interface.py index 5b76bc6ac18..e8ed67b54b3 100644 --- a/dom/bindings/parser/tests/test_interface.py +++ b/dom/bindings/parser/tests/test_interface.py @@ -201,8 +201,8 @@ def WebIDLTest(parser, harness): }; """); results = parser.finish(); - harness.check(len(results), 1, - "Should have one result with partial interface") + harness.check(len(results), 2, + "Should have two results with partial interface") iface = results[0] harness.check(len(iface.members), 3, "Should have three members with partial interface") @@ -231,9 +231,9 @@ def WebIDLTest(parser, harness): }; """); results = parser.finish(); - harness.check(len(results), 1, - "Should have one result with reversed partial interface") - iface = results[0] + harness.check(len(results), 2, + "Should have two results with reversed partial interface") + iface = results[1] harness.check(len(iface.members), 3, "Should have three members with reversed partial interface") harness.check(iface.members[0].identifier.name, "x", diff --git a/dom/canvas/WebGLContextGL.cpp b/dom/canvas/WebGLContextGL.cpp index 788885ed3e3..27e9b6d95a9 100644 --- a/dom/canvas/WebGLContextGL.cpp +++ b/dom/canvas/WebGLContextGL.cpp @@ -580,10 +580,6 @@ WebGLContext::CopyTexSubImage2D(GLenum target, if (yoffset + height > texHeight || yoffset + height < 0) return ErrorInvalidValue("copyTexSubImage2D: yoffset+height is too large"); - GLenum webGLFormat = imageInfo.WebGLFormat(); - if (IsGLDepthFormat(webGLFormat) || IsGLDepthStencilFormat(webGLFormat)) - return ErrorInvalidOperation("copyTexSubImage2D: a base internal format of DEPTH_COMPONENT or DEPTH_STENCIL isn't supported"); - if (mBoundFramebuffer) { if (!mBoundFramebuffer->CheckAndInitializeAttachments()) return ErrorInvalidFramebufferOperation("copyTexSubImage2D: incomplete framebuffer"); @@ -597,6 +593,7 @@ WebGLContext::CopyTexSubImage2D(GLenum target, ClearBackbufferIfNeeded(); } + GLenum webGLFormat = imageInfo.WebGLFormat(); bool texFormatRequiresAlpha = FormatHasAlpha(webGLFormat); bool fboFormatHasAlpha = mBoundFramebuffer ? mBoundFramebuffer->ColorAttachment(0).HasAlpha() : bool(gl->GetPixelFormat().alpha > 0); diff --git a/dom/canvas/WebGLContextValidate.cpp b/dom/canvas/WebGLContextValidate.cpp index 0f5eefa4226..35024b8217a 100644 --- a/dom/canvas/WebGLContextValidate.cpp +++ b/dom/canvas/WebGLContextValidate.cpp @@ -527,6 +527,31 @@ WebGLContext::ValidateTexImageFormat(GLenum format, WebGLTexImageFunc func) return true; } + /* WEBGL_depth_texture added formats */ + if (format == LOCAL_GL_DEPTH_COMPONENT || + format == LOCAL_GL_DEPTH_STENCIL) + { + if (!IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture)) { + ErrorInvalidEnum("%s: invalid format %s: need WEBGL_depth_texture enabled", + InfoFrom(func), EnumName(format)); + return false; + } + + // If WEBGL_depth_texture is enabled, then it is not allowed to be used with the + // texSubImage, copyTexImage, or copyTexSubImage methods + if (func == WebGLTexImageFunc::TexSubImage || + func == WebGLTexImageFunc::CopyTexImage || + func == WebGLTexImageFunc::CopyTexSubImage) + { + ErrorInvalidOperation("%s: format %s is not supported", InfoFrom(func), EnumName(format)); + return false; + } + + return true; + } + + // Needs to be below the depth_texture check because an invalid operation + // error needs to be generated instead of invalid enum. /* Only core formats are valid for CopyTex(Sub)?Image */ // TODO: Revisit this once color_buffer_(half_)?float lands if (IsCopyFunc(func)) { @@ -534,17 +559,6 @@ WebGLContext::ValidateTexImageFormat(GLenum format, WebGLTexImageFunc func) return false; } - /* WEBGL_depth_texture added formats */ - if (format == LOCAL_GL_DEPTH_COMPONENT || - format == LOCAL_GL_DEPTH_STENCIL) - { - bool validFormat = IsExtensionEnabled(WebGLExtensionID::WEBGL_depth_texture); - if (!validFormat) - ErrorInvalidEnum("%s: invalid format %s: need WEBGL_depth_texture enabled", - InfoFrom(func), WebGLContext::EnumName(format)); - return validFormat; - } - /* EXT_sRGB added formats */ if (format == LOCAL_GL_SRGB || format == LOCAL_GL_SRGB_ALPHA) diff --git a/dom/encoding/encodingsgroups.properties b/dom/encoding/encodingsgroups.properties index 16e4b11e02b..3e9cffe5a67 100644 --- a/dom/encoding/encodingsgroups.properties +++ b/dom/encoding/encodingsgroups.properties @@ -14,29 +14,29 @@ HZ-GB-2312=zh-CN IBM866=x-cyrillic ISO-2022-JP=ja ISO-8859-3=x-western -ISO-8859-4=x-baltic +ISO-8859-4=x-western ISO-8859-5=x-cyrillic ISO-8859-6=ar ISO-8859-7=el ISO-8859-8=he ISO-8859-8-I=he ISO-8859-10=x-western -ISO-8859-13=x-baltic +ISO-8859-13=x-western ISO-8859-14=x-western ISO-8859-15=x-western -ISO-8859-16=x-central-euro -ISO-8859-2=x-central-euro +ISO-8859-16=x-western +ISO-8859-2=x-western KOI8-R=x-cyrillic KOI8-U=x-cyrillic Shift_JIS=ja -windows-1250=x-central-euro +windows-1250=x-western windows-1251=x-cyrillic windows-1252=x-western windows-1253=el -windows-1254=tr +windows-1254=x-western windows-1255=he windows-1256=ar -windows-1257=x-baltic +windows-1257=x-western windows-1258=x-western windows-874=th x-mac-cyrillic=x-cyrillic diff --git a/dom/fmradio/FMRadioService.cpp b/dom/fmradio/FMRadioService.cpp index 2664ee36e6b..f37bbd7bf70 100644 --- a/dom/fmradio/FMRadioService.cpp +++ b/dom/fmradio/FMRadioService.cpp @@ -6,6 +6,7 @@ #include "FMRadioService.h" #include "mozilla/Hal.h" +#include "mozilla/ClearOnShutdown.h" #include "nsIAudioManager.h" #include "AudioManager.h" #include "nsDOMClassInfo.h" @@ -832,6 +833,7 @@ FMRadioService::Singleton() if (!sFMRadioService) { sFMRadioService = new FMRadioService(); + ClearOnShutdown(&sFMRadioService); } return sFMRadioService; diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index c9808a08be4..5cda5fb80ca 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -918,7 +918,7 @@ ContentChild::RecvSetProcessSandbox() // at some point; see bug 880808. #if defined(MOZ_CONTENT_SANDBOX) #if defined(XP_LINUX) - SetCurrentProcessSandbox(); + SetContentProcessSandbox(); #elif defined(XP_WIN) mozilla::SandboxTarget::Instance()->StartSandbox(); #endif diff --git a/dom/webidl/AbstractWorker.webidl b/dom/webidl/AbstractWorker.webidl index b453b48b782..0a2adba68e9 100644 --- a/dom/webidl/AbstractWorker.webidl +++ b/dom/webidl/AbstractWorker.webidl @@ -4,7 +4,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ -[NoInterfaceObject] +[NoInterfaceObject, Exposed=(Window,Worker)] interface AbstractWorker { attribute EventHandler onerror; }; diff --git a/dom/webidl/Console.webidl b/dom/webidl/Console.webidl index 9684129f09c..35587689cad 100644 --- a/dom/webidl/Console.webidl +++ b/dom/webidl/Console.webidl @@ -4,7 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -[ChromeOnly] +[ChromeOnly, + Exposed=(Window,Worker)] interface Console { void log(any... data); void info(any... data); diff --git a/dom/webidl/DOMException.webidl b/dom/webidl/DOMException.webidl index d039cfc1d64..2e083286098 100644 --- a/dom/webidl/DOMException.webidl +++ b/dom/webidl/DOMException.webidl @@ -15,7 +15,8 @@ interface StackFrame; -[NoInterfaceObject] +[NoInterfaceObject, + Exposed=(Window,Worker)] interface ExceptionMembers { // A custom message set by the thrower. LenientThis so it can be @@ -72,7 +73,8 @@ Exception implements ExceptionMembers; // XXXkhuey this is an 'exception', not an interface, but we don't have any // parser or codegen mechanisms for dealing with exceptions. -[ExceptionClass] +[ExceptionClass, + Exposed=(Window, Worker)] interface DOMException { const unsigned short INDEX_SIZE_ERR = 1; const unsigned short DOMSTRING_SIZE_ERR = 2; // historical diff --git a/dom/webidl/DataStore.webidl b/dom/webidl/DataStore.webidl index e992b65ab77..25abbe03800 100644 --- a/dom/webidl/DataStore.webidl +++ b/dom/webidl/DataStore.webidl @@ -12,7 +12,8 @@ typedef (DOMString or unsigned long) DataStoreKey; // JS codes implemented by the DataStoreImpl WebIDL. [Func="Navigator::HasDataStoreSupport", - ChromeConstructor] + ChromeConstructor, + Exposed=(Window,Worker)] interface DataStore : EventTarget { // Returns the label of the DataSource. [GetterThrows] @@ -65,8 +66,9 @@ partial interface DataStore { // which currently plays a role of C++ proxy directing to the // JS codes implemented by the DataStoreCursorImpl WebIDL. -[Pref="dom.datastore.enabled", - ChromeConstructor] +[Func="Navigator::HasDataStoreSupport", + ChromeConstructor, + Exposed=(Window,Worker)] interface DataStoreCursor { // the DataStore [GetterThrows] diff --git a/dom/webidl/DedicatedWorkerGlobalScope.webidl b/dom/webidl/DedicatedWorkerGlobalScope.webidl index 83a200f3c90..26dca58dabf 100644 --- a/dom/webidl/DedicatedWorkerGlobalScope.webidl +++ b/dom/webidl/DedicatedWorkerGlobalScope.webidl @@ -12,7 +12,8 @@ * this document. */ -[Global, Func="mozilla::dom::workers::DedicatedWorkerGlobalScope::Visible"] +[Global=(Worker,DedicatedWorker), + Exposed=DedicatedWorker] interface DedicatedWorkerGlobalScope : WorkerGlobalScope { [Throws] void postMessage(any message, optional sequence transfer); diff --git a/dom/webidl/Event.webidl b/dom/webidl/Event.webidl index 30c9120bdd3..3ec1985b6e8 100644 --- a/dom/webidl/Event.webidl +++ b/dom/webidl/Event.webidl @@ -10,7 +10,8 @@ * liability, trademark and document use rules apply. */ -[Constructor(DOMString type, optional EventInit eventInitDict)] +[Constructor(DOMString type, optional EventInit eventInitDict), + Exposed=(Window,Worker)] interface Event { [Pure] readonly attribute DOMString type; diff --git a/dom/webidl/EventTarget.webidl b/dom/webidl/EventTarget.webidl index ae6cad31c39..0dbf0ed0fd5 100644 --- a/dom/webidl/EventTarget.webidl +++ b/dom/webidl/EventTarget.webidl @@ -10,6 +10,7 @@ * liability, trademark and document use rules apply. */ +[Exposed=(Window,Worker)] interface EventTarget { /* Passing null for wantsUntrusted means "default behavior", which differs in content and chrome. In content that default boolean diff --git a/dom/webidl/FileReaderSync.webidl b/dom/webidl/FileReaderSync.webidl index 340506b17dc..17a0c8f8897 100644 --- a/dom/webidl/FileReaderSync.webidl +++ b/dom/webidl/FileReaderSync.webidl @@ -12,7 +12,8 @@ interface Blob; -[Constructor] +[Constructor, + Exposed=Worker] interface FileReaderSync { // Synchronously return strings diff --git a/dom/webidl/Headers.webidl b/dom/webidl/Headers.webidl index a0b6783d524..a240e37295a 100644 --- a/dom/webidl/Headers.webidl +++ b/dom/webidl/Headers.webidl @@ -19,7 +19,7 @@ enum HeadersGuardEnum { }; [Constructor(optional HeadersInit init), - // FIXME: Exposed=Window,Worker, + Exposed=(Window,Worker), Func="mozilla::dom::Headers::PrefEnabled"] interface Headers { [Throws] void append(ByteString name, ByteString value); diff --git a/dom/webidl/ImageData.webidl b/dom/webidl/ImageData.webidl index dae457594d8..f6000bd0595 100644 --- a/dom/webidl/ImageData.webidl +++ b/dom/webidl/ImageData.webidl @@ -11,7 +11,8 @@ */ [Constructor(unsigned long sw, unsigned long sh), - Constructor(Uint8ClampedArray data, unsigned long sw, optional unsigned long sh)] + Constructor(Uint8ClampedArray data, unsigned long sw, optional unsigned long sh), + Exposed=(Window,Worker)] interface ImageData { [Constant] readonly attribute unsigned long width; diff --git a/dom/webidl/InstallEvent.webidl b/dom/webidl/InstallEvent.webidl index 9c6d4034917..525c8438142 100644 --- a/dom/webidl/InstallEvent.webidl +++ b/dom/webidl/InstallEvent.webidl @@ -10,7 +10,11 @@ // While not explicitly restricted to ServiceWorkerGlobalScope, it probably // should be. https://github.com/slightlyoff/ServiceWorker/issues/254 [Constructor(DOMString type, optional InstallEventInit eventInitDict), - Func="mozilla::dom::workers::ServiceWorkerEventsVisible"] + Func="mozilla::dom::workers::ServiceWorkerEventsVisible", + // XXXbz I have no idea where this should be exposed. The spec makes + // no sense. But since it returns a ServiceWorker and that's only + // exposed in Window, let's say Window. + Exposed=Window] interface InstallEvent : InstallPhaseEvent { // The currently active worker for this scope when this worker is asked to // install itself. diff --git a/dom/webidl/InstallPhaseEvent.webidl b/dom/webidl/InstallPhaseEvent.webidl index f79aafe3c4d..93da4e6db98 100644 --- a/dom/webidl/InstallPhaseEvent.webidl +++ b/dom/webidl/InstallPhaseEvent.webidl @@ -10,7 +10,8 @@ // While not explicitly restricted to ServiceWorkerGlobalScope, it probably // should be. https://github.com/slightlyoff/ServiceWorker/issues/254 [Constructor(DOMString type, optional EventInit eventInitDict), - Func="mozilla::dom::workers::ServiceWorkerEventsVisible"] + Func="mozilla::dom::workers::ServiceWorkerEventsVisible", + Exposed=(ServiceWorker,Window)] interface InstallPhaseEvent : Event { // https://github.com/slightlyoff/ServiceWorker/issues/261 void waitUntil(Promise p); diff --git a/dom/webidl/LegacyQueryInterface.webidl b/dom/webidl/LegacyQueryInterface.webidl index 024ce566171..0e5380ed323 100644 --- a/dom/webidl/LegacyQueryInterface.webidl +++ b/dom/webidl/LegacyQueryInterface.webidl @@ -7,10 +7,14 @@ interface nsISupports; interface IID; -[NoInterfaceObject] +[NoInterfaceObject, + // Need Exposed here, because this is a mixin onto things like Event + // that are exposed in workers. + Exposed=(Window,Worker)] interface LegacyQueryInterface { // Legacy QueryInterface, only exposed to chrome or XBL code on the // main thread. + [Exposed=Window] nsISupports queryInterface(IID iid); }; diff --git a/dom/webidl/MessageEvent.webidl b/dom/webidl/MessageEvent.webidl index 3cc615b5bfe..3aa22905863 100644 --- a/dom/webidl/MessageEvent.webidl +++ b/dom/webidl/MessageEvent.webidl @@ -9,7 +9,8 @@ interface WindowProxy; -[Constructor(DOMString type, optional MessageEventInit eventInitDict)] +[Constructor(DOMString type, optional MessageEventInit eventInitDict), + Exposed=(Window,Worker)] interface MessageEvent : Event { /** * Custom data associated with this event. diff --git a/dom/webidl/MessagePort.webidl b/dom/webidl/MessagePort.webidl index 16f94377577..af9d33213b1 100644 --- a/dom/webidl/MessagePort.webidl +++ b/dom/webidl/MessagePort.webidl @@ -7,6 +7,7 @@ * http://www.whatwg.org/specs/web-apps/current-work/#channel-messaging */ +[Exposed=(Window,Worker)] interface MessagePort : EventTarget { [Throws] void postMessage(any message, optional sequence transferable); diff --git a/dom/webidl/MessagePortList.webidl b/dom/webidl/MessagePortList.webidl index 0483cb16010..aa858480ec2 100644 --- a/dom/webidl/MessagePortList.webidl +++ b/dom/webidl/MessagePortList.webidl @@ -4,7 +4,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ -[NoInterfaceObject, ArrayClass] +[NoInterfaceObject, ArrayClass, Exposed=(Window,Worker)] interface MessagePortList { readonly attribute unsigned long length; getter MessagePort? item(unsigned long index); diff --git a/dom/webidl/Navigator.webidl b/dom/webidl/Navigator.webidl index 99e0c7abf28..b9cccf0cfae 100644 --- a/dom/webidl/Navigator.webidl +++ b/dom/webidl/Navigator.webidl @@ -30,7 +30,7 @@ Navigator implements NavigatorContentUtils; Navigator implements NavigatorStorageUtils; Navigator implements NavigatorFeatures; -[NoInterfaceObject] +[NoInterfaceObject, Exposed=(Window,Worker)] interface NavigatorID { // WebKit/Blink/Trident/Presto support this (hardcoded "Mozilla"). [Constant] @@ -53,10 +53,11 @@ interface NavigatorID { [NoInterfaceObject] interface NavigatorLanguage { readonly attribute DOMString? language; - [Pure, Cached, Frozen] readonly attribute sequence languages; + [Pure, Cached, Frozen] + readonly attribute sequence languages; }; -[NoInterfaceObject] +[NoInterfaceObject, Exposed=(Window,Worker)] interface NavigatorOnLine { readonly attribute boolean onLine; }; @@ -120,7 +121,8 @@ interface NavigatorBattery { Navigator implements NavigatorBattery; // https://wiki.mozilla.org/WebAPI/DataStore -[NoInterfaceObject] +[NoInterfaceObject, + Exposed=(Window,Worker)] interface NavigatorDataStore { [Throws, NewObject, Func="Navigator::HasDataStoreSupport"] Promise> getDataStores(DOMString name, diff --git a/dom/webidl/Promise.webidl b/dom/webidl/Promise.webidl index 0786ba2bd16..8b9ac25cea0 100644 --- a/dom/webidl/Promise.webidl +++ b/dom/webidl/Promise.webidl @@ -16,7 +16,8 @@ callback PromiseInit = void (object resolve, object reject); callback AnyCallback = any (any value); // REMOVE THE RELEVANT ENTRY FROM test_interfaces.html WHEN THIS IS IMPLEMENTED IN JS. -[Constructor(PromiseInit init)] +[Constructor(PromiseInit init), + Exposed=(Window,Worker)] // Need to escape "Promise" so it's treated as an identifier. interface _Promise { // TODO bug 875289 - static Promise fulfill(any value); diff --git a/dom/webidl/ServiceWorker.webidl b/dom/webidl/ServiceWorker.webidl index d3f9100a6f6..ad0b02ea83a 100644 --- a/dom/webidl/ServiceWorker.webidl +++ b/dom/webidl/ServiceWorker.webidl @@ -10,7 +10,10 @@ // Still unclear what should be subclassed. // https://github.com/slightlyoff/ServiceWorker/issues/189 -[Pref="dom.serviceWorkers.enabled"] +[Pref="dom.serviceWorkers.enabled", + // XXXbz I have no idea where this should be exposed. The spec makes + // no sense. But since it's got a pref, let's say window. + Exposed=Window] interface ServiceWorker : EventTarget { readonly attribute DOMString scope; readonly attribute DOMString url; diff --git a/dom/webidl/ServiceWorkerContainer.webidl b/dom/webidl/ServiceWorkerContainer.webidl index dd8dd7f439e..126db64620a 100644 --- a/dom/webidl/ServiceWorkerContainer.webidl +++ b/dom/webidl/ServiceWorkerContainer.webidl @@ -37,15 +37,14 @@ interface ServiceWorkerContainer { }; // Testing only. -[ChromeOnly, Pref="dom.serviceWorkers.testing.enabled"] partial interface ServiceWorkerContainer { - [Throws] + [Throws,Pref="dom.serviceWorkers.testing.enabled"] Promise clearAllServiceWorkerData(); - [Throws] + [Throws,Pref="dom.serviceWorkers.testing.enabled"] DOMString getScopeForUrl(DOMString url); - [Throws] + [Throws,Pref="dom.serviceWorkers.testing.enabled"] DOMString getControllingWorkerScriptURLForPath(DOMString path); }; diff --git a/dom/webidl/ServiceWorkerGlobalScope.webidl b/dom/webidl/ServiceWorkerGlobalScope.webidl index b9c3911a0d4..5f2518a4f70 100644 --- a/dom/webidl/ServiceWorkerGlobalScope.webidl +++ b/dom/webidl/ServiceWorkerGlobalScope.webidl @@ -10,10 +10,8 @@ * this document. */ -// The Pref controls exposure in general, the Func restricts it to inside the -// ServiceWorkerGlobalScope (itself). -[Global, Func="mozilla::dom::workers::ServiceWorkerGlobalScope::Visible", - Pref="dom.serviceWorkers.enabled"] +[Global=(Worker,ServiceWorker), + Exposed=ServiceWorker] interface ServiceWorkerGlobalScope : WorkerGlobalScope { // FIXME(nsm): Bug 982725 // readonly attribute CacheList caches; diff --git a/dom/webidl/SharedWorkerGlobalScope.webidl b/dom/webidl/SharedWorkerGlobalScope.webidl index 06dee655f4d..f3e8daf4245 100644 --- a/dom/webidl/SharedWorkerGlobalScope.webidl +++ b/dom/webidl/SharedWorkerGlobalScope.webidl @@ -12,7 +12,8 @@ * this document. */ -[Global, Func="mozilla::dom::workers::SharedWorkerGlobalScope::Visible"] +[Global=(Worker,SharedWorker), + Exposed=SharedWorker] interface SharedWorkerGlobalScope : WorkerGlobalScope { readonly attribute DOMString name; attribute EventHandler onconnect; diff --git a/dom/webidl/TextDecoder.webidl b/dom/webidl/TextDecoder.webidl index 104d43834ad..36f75b46b58 100644 --- a/dom/webidl/TextDecoder.webidl +++ b/dom/webidl/TextDecoder.webidl @@ -10,7 +10,8 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ -[Constructor(optional DOMString label = "utf-8", optional TextDecoderOptions options)] +[Constructor(optional DOMString label = "utf-8", optional TextDecoderOptions options), + Exposed=(Window,Worker)] interface TextDecoder { [Constant] readonly attribute DOMString encoding; diff --git a/dom/webidl/TextEncoder.webidl b/dom/webidl/TextEncoder.webidl index b71df237bda..0629ef050b3 100644 --- a/dom/webidl/TextEncoder.webidl +++ b/dom/webidl/TextEncoder.webidl @@ -10,7 +10,8 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ -[Constructor(optional DOMString utfLabel = "utf-8")] +[Constructor(optional DOMString utfLabel = "utf-8"), + Exposed=(Window,Worker)] interface TextEncoder { [Constant] readonly attribute DOMString encoding; diff --git a/dom/webidl/URL.webidl b/dom/webidl/URL.webidl index 66e8f4b296c..042fcbae6a8 100644 --- a/dom/webidl/URL.webidl +++ b/dom/webidl/URL.webidl @@ -14,7 +14,8 @@ // [Constructor(DOMString url, optional (URL or DOMString) base = "about:blank")] [Constructor(DOMString url, URL base), - Constructor(DOMString url, optional DOMString base = "about:blank")] + Constructor(DOMString url, optional DOMString base = "about:blank"), + Exposed=(Window,Worker)] interface URL { }; URL implements URLUtils; diff --git a/dom/webidl/URLSearchParams.webidl b/dom/webidl/URLSearchParams.webidl index 74ef3aa889a..d1924234672 100644 --- a/dom/webidl/URLSearchParams.webidl +++ b/dom/webidl/URLSearchParams.webidl @@ -14,7 +14,8 @@ */ [Constructor(optional DOMString init = ""), - Constructor(URLSearchParams init)] + Constructor(URLSearchParams init), + Exposed=(Window,Worker)] interface URLSearchParams { void append(DOMString name, DOMString value); void delete(DOMString name); diff --git a/dom/webidl/URLUtils.webidl b/dom/webidl/URLUtils.webidl index 2e56c20429c..05cb644e6c5 100644 --- a/dom/webidl/URLUtils.webidl +++ b/dom/webidl/URLUtils.webidl @@ -13,7 +13,8 @@ * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0. */ -[NoInterfaceObject] +[NoInterfaceObject, + Exposed=(Window, Worker)] interface URLUtilsNoSearchParams { // Bug 824857: no support for stringifier attributes yet. // stringifier attribute DOMString href; @@ -47,7 +48,8 @@ interface URLUtilsNoSearchParams { stringifier; }; -[NoInterfaceObject] +[NoInterfaceObject, + Exposed=(Window, Worker)] interface URLUtils : URLUtilsNoSearchParams { attribute URLSearchParams searchParams; diff --git a/dom/webidl/URLUtilsReadOnly.webidl b/dom/webidl/URLUtilsReadOnly.webidl index 0c526429141..eaba2c815e3 100644 --- a/dom/webidl/URLUtilsReadOnly.webidl +++ b/dom/webidl/URLUtilsReadOnly.webidl @@ -13,7 +13,8 @@ * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0. */ -[NoInterfaceObject] +[NoInterfaceObject, + Exposed=(Window, Worker)] interface URLUtilsReadOnly { stringifier; readonly attribute DOMString href; diff --git a/dom/webidl/Window.webidl b/dom/webidl/Window.webidl index 33b4f3332cc..9b1ecb37871 100644 --- a/dom/webidl/Window.webidl +++ b/dom/webidl/Window.webidl @@ -24,7 +24,7 @@ interface nsIDOMCrypto; typedef any Transferable; // http://www.whatwg.org/specs/web-apps/current-work/ -[Global, NeedNewResolve] +[PrimaryGlobal, NeedNewResolve] /*sealed*/ interface Window : EventTarget { // the current browsing context [Unforgeable, Throws, @@ -90,7 +90,7 @@ Window implements GlobalEventHandlers; Window implements WindowEventHandlers; // http://www.whatwg.org/specs/web-apps/current-work/ -[NoInterfaceObject] +[NoInterfaceObject, Exposed=(Window,Worker)] interface WindowTimers { [Throws] long setTimeout(Function handler, optional long timeout = 0, any... arguments); [Throws] long setTimeout(DOMString handler, optional long timeout = 0, any... unused); @@ -102,7 +102,7 @@ interface WindowTimers { Window implements WindowTimers; // http://www.whatwg.org/specs/web-apps/current-work/ -[NoInterfaceObject] +[NoInterfaceObject, Exposed=(Window,Worker)] interface WindowBase64 { [Throws] DOMString btoa(DOMString btoa); [Throws] DOMString atob(DOMString atob); diff --git a/dom/webidl/Worker.webidl b/dom/webidl/Worker.webidl index b64b92d3424..c2b01e6c266 100644 --- a/dom/webidl/Worker.webidl +++ b/dom/webidl/Worker.webidl @@ -13,7 +13,8 @@ */ [Constructor(DOMString scriptURL), - Func="mozilla::dom::workers::WorkerPrivate::WorkerAvailable"] + Func="mozilla::dom::workers::WorkerPrivate::WorkerAvailable", + Exposed=(Window,Worker)] interface Worker : EventTarget { void terminate(); @@ -26,6 +27,7 @@ interface Worker : EventTarget { Worker implements AbstractWorker; [Constructor(DOMString scriptURL), - Func="mozilla::dom::workers::ChromeWorkerPrivate::WorkerAvailable"] + Func="mozilla::dom::workers::ChromeWorkerPrivate::WorkerAvailable", + Exposed=(Window,Worker)] interface ChromeWorker : Worker { }; diff --git a/dom/webidl/WorkerGlobalScope.webidl b/dom/webidl/WorkerGlobalScope.webidl index c2b79a39318..ecd5542ef26 100644 --- a/dom/webidl/WorkerGlobalScope.webidl +++ b/dom/webidl/WorkerGlobalScope.webidl @@ -12,6 +12,7 @@ * this document. */ +[Exposed=Worker] interface WorkerGlobalScope : EventTarget { readonly attribute WorkerGlobalScope self; diff --git a/dom/webidl/WorkerLocation.webidl b/dom/webidl/WorkerLocation.webidl index 9b4a71d0074..7e27b09c08c 100644 --- a/dom/webidl/WorkerLocation.webidl +++ b/dom/webidl/WorkerLocation.webidl @@ -12,5 +12,6 @@ * this document. */ +[Exposed=Worker] interface WorkerLocation { }; WorkerLocation implements URLUtilsReadOnly; diff --git a/dom/webidl/WorkerNavigator.webidl b/dom/webidl/WorkerNavigator.webidl index 54b0b23a733..a0be8137fa0 100644 --- a/dom/webidl/WorkerNavigator.webidl +++ b/dom/webidl/WorkerNavigator.webidl @@ -2,6 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +[Exposed=Worker] interface WorkerNavigator { }; diff --git a/dom/webidl/XMLHttpRequest.webidl b/dom/webidl/XMLHttpRequest.webidl index 19e1024b04d..83d10b029d9 100644 --- a/dom/webidl/XMLHttpRequest.webidl +++ b/dom/webidl/XMLHttpRequest.webidl @@ -54,7 +54,8 @@ dictionary MozXMLHttpRequestParameters // things like this: // c = new(window.ActiveXObject || XMLHttpRequest)("Microsoft.XMLHTTP") // To handle that, we need a constructor that takes a string. - Constructor(DOMString ignored)] + Constructor(DOMString ignored), + Exposed=(Window,Worker)] interface XMLHttpRequest : XMLHttpRequestEventTarget { // event handler attribute EventHandler onreadystatechange; @@ -129,7 +130,7 @@ interface XMLHttpRequest : XMLHttpRequestEventTarget { [Throws] readonly attribute DOMString? responseText; - [Throws=MainThread] + [Throws, Exposed=Window] readonly attribute Document? responseXML; // Mozilla-specific stuff @@ -137,7 +138,7 @@ interface XMLHttpRequest : XMLHttpRequestEventTarget { [ChromeOnly, SetterThrows=Workers] attribute boolean mozBackgroundRequest; - [ChromeOnly] + [ChromeOnly, Exposed=Window] readonly attribute MozChannel? channel; [Throws] diff --git a/dom/webidl/XMLHttpRequestEventTarget.webidl b/dom/webidl/XMLHttpRequestEventTarget.webidl index 6c120649866..b4d26b2a12c 100644 --- a/dom/webidl/XMLHttpRequestEventTarget.webidl +++ b/dom/webidl/XMLHttpRequestEventTarget.webidl @@ -10,7 +10,8 @@ * liability, trademark and document use rules apply. */ -[NoInterfaceObject] +[NoInterfaceObject, + Exposed=(Window,Worker)] interface XMLHttpRequestEventTarget : EventTarget { // event handlers [SetterThrows=Workers, GetterThrows=Workers] diff --git a/dom/webidl/XMLHttpRequestUpload.webidl b/dom/webidl/XMLHttpRequestUpload.webidl index 56bf397878b..002b892c269 100644 --- a/dom/webidl/XMLHttpRequestUpload.webidl +++ b/dom/webidl/XMLHttpRequestUpload.webidl @@ -10,6 +10,7 @@ * liability, trademark and document use rules apply. */ +[Exposed=(Window,Worker)] interface XMLHttpRequestUpload : XMLHttpRequestEventTarget { }; diff --git a/dom/workers/RegisterBindings.cpp b/dom/workers/RegisterBindings.cpp index c4525bb14b7..428dd2f5191 100644 --- a/dom/workers/RegisterBindings.cpp +++ b/dom/workers/RegisterBindings.cpp @@ -10,28 +10,7 @@ #include "jsapi.h" #include "js/OldDebugAPI.h" -#include "mozilla/dom/BindingUtils.h" -#include "mozilla/dom/ConsoleBinding.h" -#include "mozilla/dom/DOMExceptionBinding.h" -#include "mozilla/dom/EventBinding.h" -#include "mozilla/dom/EventHandlerBinding.h" -#include "mozilla/dom/EventTargetBinding.h" -#include "mozilla/dom/FileReaderSyncBinding.h" -#include "mozilla/dom/HeadersBinding.h" -#include "mozilla/dom/ImageData.h" -#include "mozilla/dom/ImageDataBinding.h" -#include "mozilla/dom/MessageEventBinding.h" -#include "mozilla/dom/MessagePortBinding.h" -#include "mozilla/dom/PromiseBinding.h" -#include "mozilla/dom/TextDecoderBinding.h" -#include "mozilla/dom/TextEncoderBinding.h" -#include "mozilla/dom/XMLHttpRequestBinding.h" -#include "mozilla/dom/XMLHttpRequestUploadBinding.h" -#include "mozilla/dom/URLBinding.h" -#include "mozilla/dom/URLSearchParamsBinding.h" -#include "mozilla/dom/WorkerBinding.h" -#include "mozilla/dom/WorkerLocationBinding.h" -#include "mozilla/dom/WorkerNavigatorBinding.h" +#include "mozilla/dom/RegisterWorkerBindings.h" #include "mozilla/OSFileConstants.h" USING_WORKERS_NAMESPACE @@ -40,15 +19,13 @@ using namespace mozilla::dom; bool WorkerPrivate::RegisterBindings(JSContext* aCx, JS::Handle aGlobal) { - JS::Rooted eventTargetProto(aCx, - EventTargetBinding::GetProtoObject(aCx, aGlobal)); - if (!eventTargetProto) { + // Init Web IDL bindings + if (!RegisterWorkerBindings(aCx, aGlobal)) { return false; } if (IsChromeWorker()) { - if (!ChromeWorkerBinding::GetConstructorObject(aCx, aGlobal) || - !DefineChromeWorkerFunctions(aCx, aGlobal) || + if (!DefineChromeWorkerFunctions(aCx, aGlobal) || !DefineOSFileConstants(aCx, aGlobal)) { return false; } @@ -59,29 +36,6 @@ WorkerPrivate::RegisterBindings(JSContext* aCx, JS::Handle aGlobal) return false; } - // Init other paris-bindings. - if (!ConsoleBinding::GetConstructorObject(aCx, aGlobal) || - !DOMExceptionBinding::GetConstructorObject(aCx, aGlobal) || - !EventBinding::GetConstructorObject(aCx, aGlobal) || - !FileReaderSyncBinding_workers::GetConstructorObject(aCx, aGlobal) || - (HeadersBinding::ConstructorEnabled(aCx, aGlobal) && - !HeadersBinding::GetConstructorObject(aCx, aGlobal)) || - !ImageDataBinding::GetConstructorObject(aCx, aGlobal) || - !MessageEventBinding::GetConstructorObject(aCx, aGlobal) || - !MessagePortBinding::GetConstructorObject(aCx, aGlobal) || - !PromiseBinding::GetConstructorObject(aCx, aGlobal) || - !TextDecoderBinding::GetConstructorObject(aCx, aGlobal) || - !TextEncoderBinding::GetConstructorObject(aCx, aGlobal) || - !XMLHttpRequestBinding_workers::GetConstructorObject(aCx, aGlobal) || - !XMLHttpRequestUploadBinding_workers::GetConstructorObject(aCx, aGlobal) || - !URLBinding_workers::GetConstructorObject(aCx, aGlobal) || - !URLSearchParamsBinding::GetConstructorObject(aCx, aGlobal) || - !WorkerBinding::GetConstructorObject(aCx, aGlobal) || - !WorkerLocationBinding_workers::GetConstructorObject(aCx, aGlobal) || - !WorkerNavigatorBinding_workers::GetConstructorObject(aCx, aGlobal)) { - return false; - } - if (!JS_DefineProfilingFunctions(aCx, aGlobal)) { return false; } diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 7e9f174f117..ff5cdb2b8a0 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -3580,11 +3580,17 @@ ChromeWorkerPrivate::Constructor(const GlobalObject& aGlobal, // static bool -ChromeWorkerPrivate::WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */) +ChromeWorkerPrivate::WorkerAvailable(JSContext* aCx, JSObject* /* unused */) { - // Chrome is always allowed to use workers, and content is never allowed to - // use ChromeWorker, so all we have to check is the caller. - return nsContentUtils::ThreadsafeIsCallerChrome(); + // Chrome is always allowed to use workers, and content is never + // allowed to use ChromeWorker, so all we have to check is the + // caller. However, chrome workers apparently might not have a + // system principal, so we have to check for them manually. + if (NS_IsMainThread()) { + return nsContentUtils::IsCallerChrome(); + } + + return GetWorkerPrivateFromContext(aCx)->IsChromeWorker(); } // static diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index e3217bfa6ee..b21ba2ab2a9 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -1191,7 +1191,7 @@ public: ErrorResult& rv); static bool - WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */); + WorkerAvailable(JSContext* aCx, JSObject* /* unused */); private: ChromeWorkerPrivate() MOZ_DELETE; diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 27167ed3be4..ff37cf75cc3 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -281,14 +281,6 @@ DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPri { } -/* static */ bool -DedicatedWorkerGlobalScope::Visible(JSContext* aCx, JSObject* aObj) -{ - DedicatedWorkerGlobalScope* self = nullptr; - nsresult rv = UNWRAP_WORKER_OBJECT(DedicatedWorkerGlobalScope, aObj, self); - return NS_SUCCEEDED(rv) && self; -} - JSObject* DedicatedWorkerGlobalScope::WrapGlobalObject(JSContext* aCx) { @@ -320,14 +312,6 @@ SharedWorkerGlobalScope::SharedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, { } -/* static */ bool -SharedWorkerGlobalScope::Visible(JSContext* aCx, JSObject* aObj) -{ - SharedWorkerGlobalScope* self = nullptr; - nsresult rv = UNWRAP_WORKER_OBJECT(SharedWorkerGlobalScope, aObj, self); - return NS_SUCCEEDED(rv) && self; -} - JSObject* SharedWorkerGlobalScope::WrapGlobalObject(JSContext* aCx) { @@ -349,14 +333,6 @@ ServiceWorkerGlobalScope::ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate { } -/* static */ bool -ServiceWorkerGlobalScope::Visible(JSContext* aCx, JSObject* aObj) -{ - ServiceWorkerGlobalScope* self = nullptr; - nsresult rv = UNWRAP_WORKER_OBJECT(ServiceWorkerGlobalScope, aObj, self); - return NS_SUCCEEDED(rv) && self; -} - JSObject* ServiceWorkerGlobalScope::WrapGlobalObject(JSContext* aCx) { diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index 2f4b0147c5b..2d5541af104 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -124,9 +124,6 @@ class DedicatedWorkerGlobalScope MOZ_FINAL : public WorkerGlobalScope public: DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate); - static bool - Visible(JSContext* aCx, JSObject* aObj); - virtual JSObject* WrapGlobalObject(JSContext* aCx) MOZ_OVERRIDE; @@ -148,9 +145,6 @@ public: SharedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, const nsCString& aName); - static bool - Visible(JSContext* aCx, JSObject* aObj); - virtual JSObject* WrapGlobalObject(JSContext* aCx) MOZ_OVERRIDE; @@ -170,9 +164,6 @@ class ServiceWorkerGlobalScope MOZ_FINAL : public WorkerGlobalScope public: ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, const nsACString& aScope); - static bool - Visible(JSContext* aCx, JSObject* aObj); - virtual JSObject* WrapGlobalObject(JSContext* aCx) MOZ_OVERRIDE; diff --git a/dom/workers/XMLHttpRequest.h b/dom/workers/XMLHttpRequest.h index bb8390dd22a..100f5fad126 100644 --- a/dom/workers/XMLHttpRequest.h +++ b/dom/workers/XMLHttpRequest.h @@ -220,18 +220,6 @@ public: void GetResponseText(nsAString& aResponseText, ErrorResult& aRv); - JSObject* - GetResponseXML() const - { - return nullptr; - } - - JSObject* - GetChannel() const - { - return nullptr; - } - void GetInterface(JSContext* cx, JS::Handle aIID, JS::MutableHandle aRetval, ErrorResult& aRv) diff --git a/dom/workers/test/test_worker_interfaces.js b/dom/workers/test/test_worker_interfaces.js index 42687a42550..de01ac31e6b 100644 --- a/dom/workers/test/test_worker_interfaces.js +++ b/dom/workers/test/test_worker_interfaces.js @@ -30,10 +30,7 @@ var ecmaGlobals = "Float32Array", "Float64Array", "Function", - // NB: We haven't bothered to resolve constants like Infinity and NaN on - // Xrayed windows (which are seen from the XBL scope). We could support - // this if needed with some refactoring. - {name: "Infinity"}, + "Infinity", "Int16Array", "Int32Array", "Int8Array", @@ -43,7 +40,7 @@ var ecmaGlobals = "JSON", "Map", "Math", - {name: "NaN"}, + "NaN", "Number", "Object", "Proxy", @@ -74,10 +71,12 @@ var interfaceNamesInGlobalScope = [ // IMPORTANT: Do not change this list without review from a DOM peer! "Blob", -// IMPORTANT: Do not change this list without review from a DOM peer! - "Console", // IMPORTANT: Do not change this list without review from a DOM peer! "DedicatedWorkerGlobalScope", +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DataStore", b2g: true }, +// IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DataStoreCursor", b2g: true }, // IMPORTANT: Do not change this list without review from a DOM peer! "DOMException", // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp index 936ec42f173..27b527c771b 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -143,6 +143,7 @@ static const char *sExtensionNames[] = { "GL_OES_compressed_ETC1_RGB8_texture", "GL_EXT_draw_range_elements", "GL_EXT_shader_texture_lod", + "GL_NV_fence", nullptr }; @@ -1075,6 +1076,26 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl) } } + if (IsExtensionSupported(NV_fence)) { + SymLoadStruct extSymbols[] = { + { (PRFuncPtr*) &mSymbols.fGenFences, { "GenFencesNV", nullptr } }, + { (PRFuncPtr*) &mSymbols.fDeleteFences, { "DeleteFencesNV", nullptr } }, + { (PRFuncPtr*) &mSymbols.fSetFence, { "SetFenceNV", nullptr } }, + { (PRFuncPtr*) &mSymbols.fTestFence, { "TestFenceNV", nullptr } }, + { (PRFuncPtr*) &mSymbols.fFinishFence, { "FinishFenceNV", nullptr } }, + { (PRFuncPtr*) &mSymbols.fIsFence, { "IsFenceNV", nullptr } }, + { (PRFuncPtr*) &mSymbols.fGetFenceiv, { "GetFenceivNV", nullptr } }, + END_SYMBOLS + }; + + if (!LoadSymbols(&extSymbols[0], trygl, prefix)) { + NS_ERROR("GL supports NV_fence without supplying its functions."); + + MarkExtensionUnsupported(NV_fence); + ClearSymbols(extSymbols); + } + } + // Load developer symbols, don't fail if we can't find them. SymLoadStruct auxSymbols[] = { { (PRFuncPtr*) &mSymbols.fGetTexImage, { "GetTexImage", nullptr } }, diff --git a/gfx/gl/GLContext.h b/gfx/gl/GLContext.h index c502386962d..409c0de8472 100644 --- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -412,6 +412,7 @@ public: OES_compressed_ETC1_RGB8_texture, EXT_draw_range_elements, EXT_shader_texture_lod, + NV_fence, Extensions_Max, Extensions_End }; @@ -2501,6 +2502,67 @@ public: return ret; } +// ----------------------------------------------------------------------------- +// Extension NV_fence +public: + void fGenFences(GLsizei n, GLuint* fences) + { + ASSERT_SYMBOL_PRESENT(fGenFences); + BEFORE_GL_CALL; + mSymbols.fGenFences(n, fences); + AFTER_GL_CALL; + } + + void fDeleteFences(GLsizei n, const GLuint* fences) + { + ASSERT_SYMBOL_PRESENT(fDeleteFences); + BEFORE_GL_CALL; + mSymbols.fDeleteFences(n, fences); + AFTER_GL_CALL; + } + + void fSetFence(GLuint fence, GLenum condition) + { + ASSERT_SYMBOL_PRESENT(fSetFence); + BEFORE_GL_CALL; + mSymbols.fSetFence(fence, condition); + AFTER_GL_CALL; + } + + realGLboolean fTestFence(GLuint fence) + { + ASSERT_SYMBOL_PRESENT(fTestFence); + BEFORE_GL_CALL; + realGLboolean ret = mSymbols.fTestFence(fence); + AFTER_GL_CALL; + return ret; + } + + void fFinishFence(GLuint fence) + { + ASSERT_SYMBOL_PRESENT(fFinishFence); + BEFORE_GL_CALL; + mSymbols.fFinishFence(fence); + AFTER_GL_CALL; + } + + realGLboolean fIsFence(GLuint fence) + { + ASSERT_SYMBOL_PRESENT(fIsFence); + BEFORE_GL_CALL; + realGLboolean ret = mSymbols.fIsFence(fence); + AFTER_GL_CALL; + return ret; + } + + void fGetFenceiv(GLuint fence, GLenum pname, GLint* params) + { + ASSERT_SYMBOL_PRESENT(fGetFenceiv); + BEFORE_GL_CALL; + mSymbols.fGetFenceiv(fence, pname, params); + AFTER_GL_CALL; + } + // ----------------------------------------------------------------------------- // Constructor diff --git a/gfx/gl/GLContextSymbols.h b/gfx/gl/GLContextSymbols.h index 28d2a679d58..711704933a3 100644 --- a/gfx/gl/GLContextSymbols.h +++ b/gfx/gl/GLContextSymbols.h @@ -497,6 +497,22 @@ struct GLContextSymbols // draw_range_elements typedef void (GLAPIENTRY * PFNGLDRAWRANGEELEMENTS) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid* indices); PFNGLDRAWRANGEELEMENTS fDrawRangeElements; + + // NV_fence + typedef void (GLAPIENTRY * pfnGenFencesT) (GLsizei n, GLuint* fences); + pfnGenFencesT fGenFences; + typedef void (GLAPIENTRY * pfnDeleteFencesT) (GLsizei n, const GLuint* fences); + pfnDeleteFencesT fDeleteFences; + typedef void (GLAPIENTRY * pfnSetFenceT) (GLuint fence, GLenum condition); + pfnSetFenceT fSetFence; + typedef realGLboolean (GLAPIENTRY * pfnTestFenceT) (GLuint fence); + pfnTestFenceT fTestFence; + typedef void (GLAPIENTRY * pfnFinishFenceT) (GLuint fence); + pfnFinishFenceT fFinishFence; + typedef realGLboolean (GLAPIENTRY * pfnIsFenceT) (GLuint fence); + pfnIsFenceT fIsFence; + typedef void (GLAPIENTRY * pfnGetFenceivT) (GLuint fence, GLenum pname, GLint* params); + pfnGetFenceivT fGetFenceiv; }; } diff --git a/gfx/gl/SharedSurface.h b/gfx/gl/SharedSurface.h index 340d14cbbda..6557ea53dc1 100644 --- a/gfx/gl/SharedSurface.h +++ b/gfx/gl/SharedSurface.h @@ -80,6 +80,7 @@ protected: public: virtual void Fence() = 0; virtual bool WaitSync() = 0; + virtual bool PollSync() = 0; // This function waits until the buffer is no longer being used. // To optimize the performance, some implementaions recycle SharedSurfaces diff --git a/gfx/gl/SharedSurfaceANGLE.cpp b/gfx/gl/SharedSurfaceANGLE.cpp index 8b22a7f0f5e..16e4e2d3c2a 100644 --- a/gfx/gl/SharedSurfaceANGLE.cpp +++ b/gfx/gl/SharedSurfaceANGLE.cpp @@ -51,20 +51,12 @@ SharedSurface_ANGLEShareHandle::UnlockProdImpl() { } - void SharedSurface_ANGLEShareHandle::Fence() { mGL->fFinish(); } -bool -SharedSurface_ANGLEShareHandle::WaitSync() -{ - // Since we glFinish in Fence(), we're always going to be resolved here. - return true; -} - static void FillPBufferAttribs_ByBits(nsTArray& aAttrs, int redBits, int greenBits, diff --git a/gfx/gl/SharedSurfaceANGLE.h b/gfx/gl/SharedSurfaceANGLE.h index b2537f498d1..5658843bda1 100644 --- a/gfx/gl/SharedSurfaceANGLE.h +++ b/gfx/gl/SharedSurfaceANGLE.h @@ -64,7 +64,8 @@ public: virtual void UnlockProdImpl() MOZ_OVERRIDE; virtual void Fence() MOZ_OVERRIDE; - virtual bool WaitSync() MOZ_OVERRIDE; + virtual bool WaitSync() MOZ_OVERRIDE { return true; } // Fence is glFinish. + virtual bool PollSync() MOZ_OVERRIDE { return true; } // Implementation-specific functions below: HANDLE GetShareHandle() { diff --git a/gfx/gl/SharedSurfaceEGL.cpp b/gfx/gl/SharedSurfaceEGL.cpp index a14c0f292bd..4a9e6eddf51 100644 --- a/gfx/gl/SharedSurfaceEGL.cpp +++ b/gfx/gl/SharedSurfaceEGL.cpp @@ -162,6 +162,30 @@ SharedSurface_EGLImage::WaitSync() return true; } +bool +SharedSurface_EGLImage::PollSync() +{ + MutexAutoLock lock(mMutex); + if (!mSync) { + // We must not be needed. + return true; + } + MOZ_ASSERT(mEGL->IsExtensionSupported(GLLibraryEGL::KHR_fence_sync)); + + EGLint status = 0; + MOZ_ALWAYS_TRUE( mEGL->fGetSyncAttrib(mEGL->Display(), + mSync, + LOCAL_EGL_SYNC_STATUS_KHR, + &status) ); + if (status != LOCAL_EGL_SIGNALED_KHR) { + return false; + } + + MOZ_ALWAYS_TRUE( mEGL->fDestroySync(mEGL->Display(), mSync) ); + mSync = 0; + + return true; +} EGLDisplay SharedSurface_EGLImage::Display() const diff --git a/gfx/gl/SharedSurfaceEGL.h b/gfx/gl/SharedSurfaceEGL.h index ec91a87d8a0..f50578758ae 100644 --- a/gfx/gl/SharedSurfaceEGL.h +++ b/gfx/gl/SharedSurfaceEGL.h @@ -64,9 +64,9 @@ public: virtual void LockProdImpl() MOZ_OVERRIDE {} virtual void UnlockProdImpl() MOZ_OVERRIDE {} - virtual void Fence() MOZ_OVERRIDE; virtual bool WaitSync() MOZ_OVERRIDE; + virtual bool PollSync() MOZ_OVERRIDE; virtual GLuint ProdTexture() MOZ_OVERRIDE { return mProdTex; diff --git a/gfx/gl/SharedSurfaceGL.cpp b/gfx/gl/SharedSurfaceGL.cpp index ba3f55578f5..35c2e8b1335 100644 --- a/gfx/gl/SharedSurfaceGL.cpp +++ b/gfx/gl/SharedSurfaceGL.cpp @@ -169,7 +169,8 @@ SharedSurface_GLTexture::WaitSync() { MutexAutoLock lock(mMutex); if (!mSync) { - // We must have used glFinish instead of glFenceSync. + // We either used glFinish, or we passed this fence already. + // (PollSync/WaitSync returned true previously) return true; } @@ -185,6 +186,34 @@ SharedSurface_GLTexture::WaitSync() return true; } +bool +SharedSurface_GLTexture::PollSync() +{ + MutexAutoLock lock(mMutex); + if (!mSync) { + // We either used glFinish, or we passed this fence already. + // (PollSync/WaitSync returned true previously) + return true; + } + + mConsGL->MakeCurrent(); + MOZ_ASSERT(mConsGL->IsExtensionSupported(GLContext::ARB_sync)); + + GLint status = 0; + mConsGL->fGetSynciv(mSync, + LOCAL_GL_SYNC_STATUS, + 1, + nullptr, + &status); + if (status != LOCAL_GL_SIGNALED) + return false; + + mConsGL->fDeleteSync(mSync); + mSync = 0; + + return true; +} + GLuint SharedSurface_GLTexture::ConsTexture(GLContext* consGL) { diff --git a/gfx/gl/SharedSurfaceGL.h b/gfx/gl/SharedSurfaceGL.h index bc58748646c..5c0ecb0cf2a 100644 --- a/gfx/gl/SharedSurfaceGL.h +++ b/gfx/gl/SharedSurfaceGL.h @@ -70,6 +70,7 @@ public: // Since we already store the data in Fence, we're always done already. return true; } + virtual bool PollSync() MOZ_OVERRIDE { return true; } virtual GLuint ProdTexture() MOZ_OVERRIDE { return mTex; @@ -146,10 +147,9 @@ public: virtual void LockProdImpl() MOZ_OVERRIDE {} virtual void UnlockProdImpl() MOZ_OVERRIDE {} - virtual void Fence() MOZ_OVERRIDE; virtual bool WaitSync() MOZ_OVERRIDE; - + virtual bool PollSync() MOZ_OVERRIDE; virtual GLuint ProdTexture() MOZ_OVERRIDE { return mTex; diff --git a/gfx/gl/SharedSurfaceGralloc.cpp b/gfx/gl/SharedSurfaceGralloc.cpp index 8cbdf25e744..e8b4cde8e8f 100644 --- a/gfx/gl/SharedSurfaceGralloc.cpp +++ b/gfx/gl/SharedSurfaceGralloc.cpp @@ -251,6 +251,30 @@ SharedSurface_Gralloc::WaitSync() return true; } +bool +SharedSurface_Gralloc::PollSync() +{ + if (!mSync) { + // We must not be needed. + return true; + } + MOZ_ASSERT(mEGL->IsExtensionSupported(GLLibraryEGL::KHR_fence_sync)); + + EGLint status = 0; + MOZ_ALWAYS_TRUE( mEGL->fGetSyncAttrib(mEGL->Display(), + mSync, + LOCAL_EGL_SYNC_STATUS_KHR, + &status) ); + if (status != LOCAL_EGL_SIGNALED_KHR) { + return false; + } + + MOZ_ALWAYS_TRUE( mEGL->fDestroySync(mEGL->Display(), mSync) ); + mSync = 0; + + return true; +} + void SharedSurface_Gralloc::WaitForBufferOwnership() { diff --git a/gfx/gl/SharedSurfaceGralloc.h b/gfx/gl/SharedSurfaceGralloc.h index b8644fa56a1..dc10f259f5a 100644 --- a/gfx/gl/SharedSurfaceGralloc.h +++ b/gfx/gl/SharedSurfaceGralloc.h @@ -57,6 +57,7 @@ public: virtual void Fence() MOZ_OVERRIDE; virtual bool WaitSync() MOZ_OVERRIDE; + virtual bool PollSync() MOZ_OVERRIDE; virtual void WaitForBufferOwnership() MOZ_OVERRIDE; diff --git a/gfx/gl/SharedSurfaceIO.h b/gfx/gl/SharedSurfaceIO.h index 0c06a2d1903..59af20210b9 100644 --- a/gfx/gl/SharedSurfaceIO.h +++ b/gfx/gl/SharedSurfaceIO.h @@ -26,6 +26,7 @@ public: virtual void Fence() MOZ_OVERRIDE; virtual bool WaitSync() MOZ_OVERRIDE { return true; } + virtual bool PollSync() MOZ_OVERRIDE { return true; } virtual bool ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) MOZ_OVERRIDE; diff --git a/gfx/src/FilterSupport.cpp b/gfx/src/FilterSupport.cpp index 5bb5b3ad11f..26a4eaba3d8 100644 --- a/gfx/src/FilterSupport.cpp +++ b/gfx/src/FilterSupport.cpp @@ -1102,6 +1102,15 @@ FilterNodeGraphFromDescription(DrawTarget* aDT, IntPoint offset = surfaceRect.TopLeft(); sourceFilterNode = FilterWrappers::ForSurface(aDT, surf, offset); + // Clip the original SourceGraphic to the first filter region if the + // surface isn't already sized appropriately. + if ((inputIndex == FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic || + inputIndex == FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha) && + !descr.FilterSpaceBounds().Contains(aSourceGraphicRect)) { + sourceFilterNode = + FilterWrappers::Crop(aDT, sourceFilterNode, descr.FilterSpaceBounds()); + } + if (inputIndex == FilterPrimitiveDescription::kPrimitiveIndexSourceAlpha) { sourceFilterNode = FilterWrappers::ToAlpha(aDT, sourceFilterNode); } diff --git a/gfx/thebes/gfxFontconfigUtils.cpp b/gfx/thebes/gfxFontconfigUtils.cpp index a12f77b7a84..45b36449400 100644 --- a/gfx/thebes/gfxFontconfigUtils.cpp +++ b/gfx/thebes/gfxFontconfigUtils.cpp @@ -380,9 +380,7 @@ struct MozLangGroupData { const MozLangGroupData MozLangGroups[] = { { nsGkAtoms::x_western, "en" }, - { nsGkAtoms::x_central_euro, "pl" }, { nsGkAtoms::x_cyrillic, "ru" }, - { nsGkAtoms::x_baltic, "lv" }, { nsGkAtoms::x_devanagari, "hi" }, { nsGkAtoms::x_tamil, "ta" }, { nsGkAtoms::x_armn, "hy" }, diff --git a/gfx/thebes/gfxGDIFontList.h b/gfx/thebes/gfxGDIFontList.h index 05687688b9b..12b38193387 100644 --- a/gfx/thebes/gfxGDIFontList.h +++ b/gfx/thebes/gfxGDIFontList.h @@ -205,20 +205,14 @@ public: bit = CHINESEBIG5_CHARSET; } else if (aLangGroup == nsGkAtoms::el_) { bit = GREEK_CHARSET; - } else if (aLangGroup == nsGkAtoms::tr) { - bit = TURKISH_CHARSET; } else if (aLangGroup == nsGkAtoms::he) { bit = HEBREW_CHARSET; } else if (aLangGroup == nsGkAtoms::ar) { bit = ARABIC_CHARSET; - } else if (aLangGroup == nsGkAtoms::x_baltic) { - bit = BALTIC_CHARSET; } else if (aLangGroup == nsGkAtoms::x_cyrillic) { bit = RUSSIAN_CHARSET; } else if (aLangGroup == nsGkAtoms::th) { bit = THAI_CHARSET; - } else if (aLangGroup == nsGkAtoms::x_central_euro) { - bit = EASTEUROPE_CHARSET; } else if (aLangGroup == nsGkAtoms::x_symbol) { bit = SYMBOL_CHARSET; } diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 51571d67c5f..93a3831319b 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -228,16 +228,13 @@ MemoryPressureObserver::Observe(nsISupports *aSubject, // the order *must* match the order in eFontPrefLang static const char *gPrefLangNames[] = { "x-western", - "x-central-euro", "ja", "zh-TW", "zh-CN", "zh-HK", "ko", "x-cyrillic", - "x-baltic", "el", - "tr", "th", "he", "ar", @@ -1241,10 +1238,8 @@ gfxPlatform::GetFontPrefLangFor(uint8_t aUnicodeRange) case kRangeSetLatin: return eFontPrefLang_Western; case kRangeCyrillic: return eFontPrefLang_Cyrillic; case kRangeGreek: return eFontPrefLang_Greek; - case kRangeTurkish: return eFontPrefLang_Turkish; case kRangeHebrew: return eFontPrefLang_Hebrew; case kRangeArabic: return eFontPrefLang_Arabic; - case kRangeBaltic: return eFontPrefLang_Baltic; case kRangeThai: return eFontPrefLang_Thai; case kRangeKorean: return eFontPrefLang_Korean; case kRangeJapanese: return eFontPrefLang_Japanese; diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index a022f436d2b..968b76ce19e 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -74,39 +74,36 @@ extern cairo_user_data_key_t kDrawTarget; enum eFontPrefLang { eFontPrefLang_Western = 0, - eFontPrefLang_CentEuro = 1, - eFontPrefLang_Japanese = 2, - eFontPrefLang_ChineseTW = 3, - eFontPrefLang_ChineseCN = 4, - eFontPrefLang_ChineseHK = 5, - eFontPrefLang_Korean = 6, - eFontPrefLang_Cyrillic = 7, - eFontPrefLang_Baltic = 8, - eFontPrefLang_Greek = 9, - eFontPrefLang_Turkish = 10, - eFontPrefLang_Thai = 11, - eFontPrefLang_Hebrew = 12, - eFontPrefLang_Arabic = 13, - eFontPrefLang_Devanagari = 14, - eFontPrefLang_Tamil = 15, - eFontPrefLang_Armenian = 16, - eFontPrefLang_Bengali = 17, - eFontPrefLang_Canadian = 18, - eFontPrefLang_Ethiopic = 19, - eFontPrefLang_Georgian = 20, - eFontPrefLang_Gujarati = 21, - eFontPrefLang_Gurmukhi = 22, - eFontPrefLang_Khmer = 23, - eFontPrefLang_Malayalam = 24, - eFontPrefLang_Oriya = 25, - eFontPrefLang_Telugu = 26, - eFontPrefLang_Kannada = 27, - eFontPrefLang_Sinhala = 28, - eFontPrefLang_Tibetan = 29, + eFontPrefLang_Japanese = 1, + eFontPrefLang_ChineseTW = 2, + eFontPrefLang_ChineseCN = 3, + eFontPrefLang_ChineseHK = 4, + eFontPrefLang_Korean = 5, + eFontPrefLang_Cyrillic = 6, + eFontPrefLang_Greek = 7, + eFontPrefLang_Thai = 8, + eFontPrefLang_Hebrew = 9, + eFontPrefLang_Arabic = 10, + eFontPrefLang_Devanagari = 11, + eFontPrefLang_Tamil = 12, + eFontPrefLang_Armenian = 13, + eFontPrefLang_Bengali = 14, + eFontPrefLang_Canadian = 15, + eFontPrefLang_Ethiopic = 16, + eFontPrefLang_Georgian = 17, + eFontPrefLang_Gujarati = 18, + eFontPrefLang_Gurmukhi = 19, + eFontPrefLang_Khmer = 20, + eFontPrefLang_Malayalam = 21, + eFontPrefLang_Oriya = 22, + eFontPrefLang_Telugu = 23, + eFontPrefLang_Kannada = 24, + eFontPrefLang_Sinhala = 25, + eFontPrefLang_Tibetan = 26, - eFontPrefLang_Others = 30, // x-unicode + eFontPrefLang_Others = 27, // x-unicode - eFontPrefLang_CJKSet = 31 // special code for CJK set + eFontPrefLang_CJKSet = 28 // special code for CJK set }; enum eCMSMode { diff --git a/gfx/thebes/nsUnicodeRange.cpp b/gfx/thebes/nsUnicodeRange.cpp index 0e9628cc424..833f6b53992 100644 --- a/gfx/thebes/nsUnicodeRange.cpp +++ b/gfx/thebes/nsUnicodeRange.cpp @@ -14,10 +14,8 @@ static nsIAtom **gUnicodeRangeToLangGroupAtomTable[] = { &nsGkAtoms::x_cyrillic, &nsGkAtoms::el_, - &nsGkAtoms::tr, &nsGkAtoms::he, &nsGkAtoms::ar, - &nsGkAtoms::x_baltic, &nsGkAtoms::th, &nsGkAtoms::ko, &nsGkAtoms::Japanese, @@ -43,8 +41,8 @@ static nsIAtom **gUnicodeRangeToLangGroupAtomTable[] = /********************************************************************** * Unicode subranges as defined in unicode 3.0 - * x-western, x-central-euro, tr, x-baltic -> latin - * 0000 - 036f + * x-western -> latin + * 0000 - 036f * 1e00 - 1eff * 2000 - 206f (general punctuation) * 20a0 - 20cf (currency symbols) diff --git a/gfx/thebes/nsUnicodeRange.h b/gfx/thebes/nsUnicodeRange.h index 0a8eea4afcf..6390d68a626 100644 --- a/gfx/thebes/nsUnicodeRange.h +++ b/gfx/thebes/nsUnicodeRange.h @@ -16,35 +16,33 @@ class nsIAtom; // frequently used range definitions const uint8_t kRangeCyrillic = 0; const uint8_t kRangeGreek = 1; -const uint8_t kRangeTurkish = 2; -const uint8_t kRangeHebrew = 3; -const uint8_t kRangeArabic = 4; -const uint8_t kRangeBaltic = 5; -const uint8_t kRangeThai = 6; -const uint8_t kRangeKorean = 7; -const uint8_t kRangeJapanese = 8; -const uint8_t kRangeSChinese = 9; -const uint8_t kRangeTChinese = 10; -const uint8_t kRangeDevanagari = 11; -const uint8_t kRangeTamil = 12; -const uint8_t kRangeArmenian = 13; -const uint8_t kRangeBengali = 14; -const uint8_t kRangeCanadian = 15; -const uint8_t kRangeEthiopic = 16; -const uint8_t kRangeGeorgian = 17; -const uint8_t kRangeGujarati = 18; -const uint8_t kRangeGurmukhi = 19; -const uint8_t kRangeKhmer = 20; -const uint8_t kRangeMalayalam = 21; -const uint8_t kRangeOriya = 22; -const uint8_t kRangeTelugu = 23; -const uint8_t kRangeKannada = 24; -const uint8_t kRangeSinhala = 25; -const uint8_t kRangeTibetan = 26; +const uint8_t kRangeHebrew = 2; +const uint8_t kRangeArabic = 3; +const uint8_t kRangeThai = 4; +const uint8_t kRangeKorean = 5; +const uint8_t kRangeJapanese = 6; +const uint8_t kRangeSChinese = 7; +const uint8_t kRangeTChinese = 8; +const uint8_t kRangeDevanagari = 9; +const uint8_t kRangeTamil = 10; +const uint8_t kRangeArmenian = 11; +const uint8_t kRangeBengali = 12; +const uint8_t kRangeCanadian = 13; +const uint8_t kRangeEthiopic = 14; +const uint8_t kRangeGeorgian = 15; +const uint8_t kRangeGujarati = 16; +const uint8_t kRangeGurmukhi = 17; +const uint8_t kRangeKhmer = 18; +const uint8_t kRangeMalayalam = 19; +const uint8_t kRangeOriya = 20; +const uint8_t kRangeTelugu = 21; +const uint8_t kRangeKannada = 22; +const uint8_t kRangeSinhala = 23; +const uint8_t kRangeTibetan = 24; -const uint8_t kRangeSpecificItemNum = 27; +const uint8_t kRangeSpecificItemNum = 25; -//range/rangeSet grow to this place 27-29 +//range/rangeSet grow to this place 25-29 const uint8_t kRangeSetStart = 30; // range set definition starts from here const uint8_t kRangeSetLatin = 30; diff --git a/intl/locale/langGroups.properties b/intl/locale/langGroups.properties index ca39998fa59..4903d3c34d4 100644 --- a/intl/locale/langGroups.properties +++ b/intl/locale/langGroups.properties @@ -28,43 +28,38 @@ bg=x-cyrillic bn=x-beng bo=x-tibt br=x-western -bs=x-central-euro +bs=x-western ca=x-western -ce=x-central-euro +ce=x-western ch=x-western co=x-western cr=x-cans -cs=x-central-euro -csb=x-central-euro +cs=x-western +csb=x-western #cu=x-cyrillic cv=x-cyrillic -# XXX Latin Ext. A is also used for cy. cy=x-western da=x-western de=x-western -dsb=x-central-euro +dsb=x-western #dv=Thaanna dz=x-tibt -#ee=x-western(?) (Ewe uses characters outside Latin-1 as well) +ee=x-western el=el en=x-western -# Esperanto: Latin-3 eo=x-western es=x-western -et=x-baltic +et=x-western eu=x-western fa=ar -#ff=x-western(?) : Fulfulde +ff=x-western fi=x-western -# XXX Latin Ext. A is also used for fj. fj=x-western fo=x-western fr=x-western fy=x-western ga=x-western -#XXX Latin Ext. A and Ext. additional block are used for Gaelic (8859-14) gd=x-western -# gl : ISO-8859-13 gl=x-western gn=x-western #ha=x-western : Latin and Ajami scripts @@ -74,10 +69,10 @@ haw=x-western he=he hi=x-devanagari hil=x-western -hr=x-central-euro -hsb=x-central-euro +hr=x-western +hsb=x-western ht=x-western -hu=x-central-euro +hu=x-western hy=x-armn ia=x-western id=x-western @@ -96,18 +91,15 @@ kok=x-devanagari ks=ar # Arabic script is also used for Kurdish ku=x-western -# XXX Latin Ext. A is also used for kw(Cornish). kw=x-western #ky=x-cyrillic -# XXX Latin Ext. A is also used for Latin. la=x-western lb=x-western ln=x-western -lt=x-baltic -lv=x-baltic +lt=x-western +lv=x-western mg=x-western mh=x-western -#XXX Latin Ext. A is also used for Maori. mi=x-western mk=x-cyrillic ml=x-mlym @@ -115,7 +107,6 @@ ml=x-mlym mn=x-cyrillic mr=x-devanagari ms=x-western -# Maltese: Latin-3 mt=x-western na=x-western nb=x-western @@ -126,7 +117,6 @@ nn=x-western no=x-western nr=x-western nso=x-western -# XXX : x-central-euro may be better nv=x-western ny=x-western oc=x-western @@ -139,13 +129,13 @@ os=x-cyrillic pa-in=x-guru pa-pk=ar pa=x-guru -pl=x-central-euro +pl=x-western ps=ar pt=x-western qu=x-western rm=x-western rn=x-western -ro=x-central-euro +ro=x-western ru=x-cyrillic rw=x-western sa=x-devanagari @@ -153,10 +143,10 @@ sc=x-western sd=ar # African language (but related with French) sg=x-western -sh=x-central-euro +sh=x-western si=x-sinh -sk=x-central-euro -sl=x-central-euro +sk=x-western +sl=x-western sm=x-western so=x-western son=x-western @@ -172,26 +162,24 @@ th=th ti=x-ethi tig=x-ethi tk=x-cyrillic -#tk=tr # (The country declared in 1992 to gradually move to Latin script) +#tk=x-western # (The country declared in 1992 to gradually move to Latin script) tl=x-western tlh=x-western tn=x-western to=x-western -tr=tr +tr=x-western ts=x-western -tt=tr +tt=x-western uk=x-cyrillic ur=ar ve=x-western -# XXX : Vietnamese may need be put into a script group of its own vi=x-western vo=x-western wa=x-western wo=x-western xh=x-western yi=he -#Latin Ext. A and Latin Extended Additional block are used for Yoruba. -#yo=x-western +yo=x-western zh-cn=zh-CN # XXX : The following two entries are added as a quick fix (bug 251241). # When we have a general solution for ISO 15924 (script codes), the issue has @@ -208,13 +196,10 @@ zu=x-western # # mapping mozilla's internal x-* to themselves (see bug 256257) x-western=x-western -x-central-euro=x-central-euro x-cyrillic=x-cyrillic # el -# tr # he # ar -x-baltic=x-baltic # th # ja # zh-CN diff --git a/js/src/asmjs/AsmJSFrameIterator.cpp b/js/src/asmjs/AsmJSFrameIterator.cpp index e125e4c97b4..4f6ccdb6ce0 100644 --- a/js/src/asmjs/AsmJSFrameIterator.cpp +++ b/js/src/asmjs/AsmJSFrameIterator.cpp @@ -79,7 +79,8 @@ AsmJSFrameIterator::settle() fp_ = nullptr; JS_ASSERT(done()); break; - case AsmJSModule::CodeRange::FFI: + case AsmJSModule::CodeRange::IonFFI: + case AsmJSModule::CodeRange::SlowFFI: case AsmJSModule::CodeRange::Interrupt: case AsmJSModule::CodeRange::Inline: case AsmJSModule::CodeRange::Thunk: @@ -440,11 +441,6 @@ AsmJSProfilingFrameIterator::initFromFP(const AsmJSActivation &activation) // - for Math and other builtin calls, when profiling is activated, we // patch all call sites to instead call through a thunk; and // - for interrupts, we just accept that we'll lose the innermost frame. - // However, we do want FFI trampolines to show up in callstacks (so that - // they properly accumulate self-time) and for this we use the exitReason. - - exitReason_ = activation.exitReason(); - void *pc = ReturnAddressFromFP(fp); const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(pc); JS_ASSERT(codeRange); @@ -462,13 +458,25 @@ AsmJSProfilingFrameIterator::initFromFP(const AsmJSActivation &activation) callerFP_ = CallerFPFromFP(fp); AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, fp); break; - case AsmJSModule::CodeRange::FFI: + case AsmJSModule::CodeRange::IonFFI: + case AsmJSModule::CodeRange::SlowFFI: case AsmJSModule::CodeRange::Interrupt: case AsmJSModule::CodeRange::Inline: case AsmJSModule::CodeRange::Thunk: MOZ_CRASH("Unexpected CodeRange kind"); } + // Since, despite the above reasoning for skipping a frame, we do want FFI + // trampolines and interrupts to show up in the profile (so they can + // accumulate self time and explain performance faults), an "exit reason" is + // stored on all the paths leaving asm.js and the iterator logic treats this + // reason as its own frame. If we have exited asm.js code without setting an + // exit reason, the reason will be None and this means the code was + // asynchronously interrupted. + exitReason_ = activation.exitReason(); + if (exitReason_ == AsmJSExit::None) + exitReason_ = AsmJSExit::Interrupt; + JS_ASSERT(!done()); } @@ -505,7 +513,8 @@ AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation & const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(state.pc); switch (codeRange->kind()) { case AsmJSModule::CodeRange::Function: - case AsmJSModule::CodeRange::FFI: + case AsmJSModule::CodeRange::IonFFI: + case AsmJSModule::CodeRange::SlowFFI: case AsmJSModule::CodeRange::Interrupt: case AsmJSModule::CodeRange::Thunk: { // While codeRange describes the *current* frame, the fp/pc state stored in @@ -606,7 +615,8 @@ AsmJSProfilingFrameIterator::operator++() callerPC_ = nullptr; break; case AsmJSModule::CodeRange::Function: - case AsmJSModule::CodeRange::FFI: + case AsmJSModule::CodeRange::IonFFI: + case AsmJSModule::CodeRange::SlowFFI: case AsmJSModule::CodeRange::Interrupt: case AsmJSModule::CodeRange::Inline: case AsmJSModule::CodeRange::Thunk: @@ -657,19 +667,22 @@ AsmJSProfilingFrameIterator::label() const { JS_ASSERT(!done()); - // Note: this label is regexp-matched by - // browser/devtools/profiler/cleopatra/js/parserWorker.js. - // Use the same string for both time inside and under so that the two // entries will be coalesced by the profiler. - const char *ffiDescription = "FFI trampoline (in asm.js)"; - const char *interruptDescription = "slow script interrupt trampoline (in asm.js)"; + // + // NB: these labels are regexp-matched by + // browser/devtools/profiler/cleopatra/js/parserWorker.js. + const char *ionFFIDescription = "fast FFI trampoline (in asm.js)"; + const char *slowFFIDescription = "slow FFI trampoline (in asm.js)"; + const char *interruptDescription = "interrupt due to out-of-bounds or long execution (in asm.js)"; switch (AsmJSExit::ExtractReasonKind(exitReason_)) { case AsmJSExit::Reason_None: break; - case AsmJSExit::Reason_FFI: - return ffiDescription; + case AsmJSExit::Reason_IonFFI: + return ionFFIDescription; + case AsmJSExit::Reason_SlowFFI: + return slowFFIDescription; case AsmJSExit::Reason_Interrupt: return interruptDescription; case AsmJSExit::Reason_Builtin: @@ -680,7 +693,8 @@ AsmJSProfilingFrameIterator::label() const switch (codeRange->kind()) { case AsmJSModule::CodeRange::Function: return codeRange->functionProfilingLabel(*module_); case AsmJSModule::CodeRange::Entry: return "entry trampoline (in asm.js)"; - case AsmJSModule::CodeRange::FFI: return ffiDescription; + case AsmJSModule::CodeRange::IonFFI: return ionFFIDescription; + case AsmJSModule::CodeRange::SlowFFI: return slowFFIDescription; case AsmJSModule::CodeRange::Interrupt: return interruptDescription; case AsmJSModule::CodeRange::Inline: return "inline stub (in asm.js)"; case AsmJSModule::CodeRange::Thunk: return BuiltinToName(codeRange->thunkTarget()); diff --git a/js/src/asmjs/AsmJSFrameIterator.h b/js/src/asmjs/AsmJSFrameIterator.h index bd9f605958f..99413b5b10c 100644 --- a/js/src/asmjs/AsmJSFrameIterator.h +++ b/js/src/asmjs/AsmJSFrameIterator.h @@ -69,7 +69,8 @@ namespace AsmJSExit // handler). enum ReasonKind { Reason_None, - Reason_FFI, + Reason_IonFFI, + Reason_SlowFFI, Reason_Interrupt, Reason_Builtin }; @@ -105,7 +106,8 @@ namespace AsmJSExit typedef uint32_t Reason; static const uint32_t None = Reason_None; - static const uint32_t FFI = Reason_FFI; + static const uint32_t IonFFI = Reason_IonFFI; + static const uint32_t SlowFFI = Reason_SlowFFI; static const uint32_t Interrupt = Reason_Interrupt; static inline Reason Builtin(BuiltinKind builtin) { return uint16_t(Reason_Builtin) | (uint16_t(builtin) << 16); diff --git a/js/src/asmjs/AsmJSModule.cpp b/js/src/asmjs/AsmJSModule.cpp index 744bbaec213..7547b2c802f 100644 --- a/js/src/asmjs/AsmJSModule.cpp +++ b/js/src/asmjs/AsmJSModule.cpp @@ -790,18 +790,31 @@ AsmJSModule::initHeap(Handle heap, JSContext *cx) } void -AsmJSModule::restoreToInitialState(ArrayBufferObject *maybePrevBuffer, ExclusiveContext *cx) +AsmJSModule::restoreToInitialState(uint8_t *prevCode, ArrayBufferObject *maybePrevBuffer, + ExclusiveContext *cx) { #ifdef DEBUG // Put the absolute links back to -1 so PatchDataWithValueCheck assertions // in staticallyLink are valid. for (size_t imm = 0; imm < AsmJSImm_Limit; imm++) { + void *callee = AddressOf(AsmJSImmKind(imm), cx); + + // If we are in profiling mode, calls to builtins will have been patched + // by setProfilingEnabled to be calls to thunks. + AsmJSExit::BuiltinKind builtin; + void *profilingCallee = profilingEnabled_ && ImmKindIsBuiltin(AsmJSImmKind(imm), &builtin) + ? prevCode + builtinThunkOffsets_[builtin] + : nullptr; + const AsmJSModule::OffsetVector &offsets = staticLinkData_.absoluteLinks[imm]; - void *target = AddressOf(AsmJSImmKind(imm), cx); for (size_t i = 0; i < offsets.length(); i++) { - Assembler::PatchDataWithValueCheck(CodeLocationLabel(code_ + offsets[i]), + uint8_t *caller = code_ + offsets[i]; + void *originalValue = profilingCallee && !lookupCodeRange(caller)->isThunk() + ? profilingCallee + : callee; + Assembler::PatchDataWithValueCheck(CodeLocationLabel(caller), PatchedImmPtr((void*)-1), - PatchedImmPtr(target)); + PatchedImmPtr(originalValue)); } } #endif @@ -1235,6 +1248,7 @@ AsmJSModule::CodeRange::CodeRange(Kind kind, uint32_t begin, uint32_t profilingR JS_ASSERT(begin_ < profilingReturn_); JS_ASSERT(profilingReturn_ < end_); + JS_ASSERT(u.kind_ == IonFFI || u.kind_ == SlowFFI || u.kind_ == Interrupt); } AsmJSModule::CodeRange::CodeRange(AsmJSExit::BuiltinKind builtin, uint32_t begin, @@ -1537,7 +1551,7 @@ AsmJSModule::clone(JSContext *cx, ScopedJSDeletePtr *moduleOut) con // flush all of them at once. out.setAutoFlushICacheRange(); - out.restoreToInitialState(maybeHeap_, cx); + out.restoreToInitialState(code_, maybeHeap_, cx); return true; } diff --git a/js/src/asmjs/AsmJSModule.h b/js/src/asmjs/AsmJSModule.h index f0352d33081..1cae37245e2 100644 --- a/js/src/asmjs/AsmJSModule.h +++ b/js/src/asmjs/AsmJSModule.h @@ -366,7 +366,7 @@ class AsmJSModule void setDeltas(uint32_t entry, uint32_t profilingJump, uint32_t profilingEpilogue); public: - enum Kind { Function, Entry, FFI, Interrupt, Thunk, Inline }; + enum Kind { Function, Entry, IonFFI, SlowFFI, Interrupt, Thunk, Inline }; CodeRange() {} CodeRange(uint32_t nameIndex, uint32_t lineNumber, const AsmJSFunctionLabels &l); @@ -378,7 +378,7 @@ class AsmJSModule Kind kind() const { return Kind(u.kind_); } bool isFunction() const { return kind() == Function; } bool isEntry() const { return kind() == Entry; } - bool isFFI() const { return kind() == FFI; } + bool isFFI() const { return kind() == IonFFI || kind() == SlowFFI; } bool isInterrupt() const { return kind() == Interrupt; } bool isThunk() const { return kind() == Thunk; } @@ -827,6 +827,12 @@ class AsmJSModule if (len > pod.minHeapLength_) pod.minHeapLength_ = len; } + bool addCodeRange(CodeRange::Kind kind, uint32_t begin, uint32_t end) { + return codeRanges_.append(CodeRange(kind, begin, end)); + } + bool addCodeRange(CodeRange::Kind kind, uint32_t begin, uint32_t pret, uint32_t end) { + return codeRanges_.append(CodeRange(kind, begin, pret, end)); + } bool addFunctionCodeRange(PropertyName *name, uint32_t lineNumber, const AsmJSFunctionLabels &labels) { @@ -837,24 +843,12 @@ class AsmJSModule uint32_t nameIndex = names_.length(); return names_.append(name) && codeRanges_.append(CodeRange(nameIndex, lineNumber, labels)); } - bool addEntryCodeRange(uint32_t begin, uint32_t end) { - return codeRanges_.append(CodeRange(CodeRange::Entry, begin, end)); - } - bool addFFICodeRange(uint32_t begin, uint32_t pret, uint32_t end) { - return codeRanges_.append(CodeRange(CodeRange::FFI, begin, pret, end)); - } - bool addInterruptCodeRange(uint32_t begin, uint32_t pret, uint32_t end) { - return codeRanges_.append(CodeRange(CodeRange::Interrupt, begin, pret, end)); - } bool addBuiltinThunkCodeRange(AsmJSExit::BuiltinKind builtin, uint32_t begin, uint32_t profilingReturn, uint32_t end) { return builtinThunkOffsets_.append(begin) && codeRanges_.append(CodeRange(builtin, begin, profilingReturn, end)); } - bool addInlineCodeRange(uint32_t begin, uint32_t end) { - return codeRanges_.append(CodeRange(CodeRange::Inline, begin, end)); - } bool addExit(unsigned ffiIndex, unsigned *exitIndex) { JS_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies()); if (SIZE_MAX - pod.funcPtrTableAndExitBytes_ < sizeof(ExitDatum)) @@ -1154,7 +1148,8 @@ class AsmJSModule } void initHeap(Handle heap, JSContext *cx); bool clone(JSContext *cx, ScopedJSDeletePtr *moduleOut) const; - void restoreToInitialState(ArrayBufferObject *maybePrevBuffer, ExclusiveContext *cx); + void restoreToInitialState(uint8_t *prevCode, ArrayBufferObject *maybePrevBuffer, + ExclusiveContext *cx); /*************************************************************************/ // Functions that can be called after dynamic linking succeeds: diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJSValidate.cpp index 29120cc4275..732ddee5360 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -1491,32 +1491,35 @@ class MOZ_STACK_CLASS ModuleCompiler JS_ASSERT(finishedFunctionBodies_); module_->exportedFunction(exportIndex).initCodeOffset(begin->offset()); uint32_t end = masm_.currentOffset(); - return module_->addEntryCodeRange(begin->offset(), end); - } - bool finishGeneratingFFI(Label *begin, Label *profilingReturn) { - JS_ASSERT(finishedFunctionBodies_); - uint32_t end = masm_.currentOffset(); - return module_->addFFICodeRange(begin->offset(), profilingReturn->offset(), end); + return module_->addCodeRange(AsmJSModule::CodeRange::Entry, begin->offset(), end); } bool finishGeneratingInterpExit(unsigned exitIndex, Label *begin, Label *profilingReturn) { JS_ASSERT(finishedFunctionBodies_); - module_->exit(exitIndex).initInterpOffset(begin->offset()); - return finishGeneratingFFI(begin, profilingReturn); + uint32_t beg = begin->offset(); + module_->exit(exitIndex).initInterpOffset(beg); + uint32_t pret = profilingReturn->offset(); + uint32_t end = masm_.currentOffset(); + return module_->addCodeRange(AsmJSModule::CodeRange::SlowFFI, beg, pret, end); } bool finishGeneratingIonExit(unsigned exitIndex, Label *begin, Label *profilingReturn) { JS_ASSERT(finishedFunctionBodies_); - module_->exit(exitIndex).initIonOffset(begin->offset()); - return finishGeneratingFFI(begin, profilingReturn); + uint32_t beg = begin->offset(); + module_->exit(exitIndex).initIonOffset(beg); + uint32_t pret = profilingReturn->offset(); + uint32_t end = masm_.currentOffset(); + return module_->addCodeRange(AsmJSModule::CodeRange::IonFFI, beg, pret, end); } bool finishGeneratingInterrupt(Label *begin, Label *profilingReturn) { JS_ASSERT(finishedFunctionBodies_); + uint32_t beg = begin->offset(); + uint32_t pret = profilingReturn->offset(); uint32_t end = masm_.currentOffset(); - return module_->addInterruptCodeRange(begin->offset(), profilingReturn->offset(), end); + return module_->addCodeRange(AsmJSModule::CodeRange::Interrupt, beg, pret, end); } bool finishGeneratingInlineStub(Label *begin) { JS_ASSERT(finishedFunctionBodies_); uint32_t end = masm_.currentOffset(); - return module_->addInlineCodeRange(begin->offset(), end); + return module_->addCodeRange(AsmJSModule::CodeRange::Inline, begin->offset(), end); } bool finishGeneratingBuiltinThunk(AsmJSExit::BuiltinKind builtin, Label *begin, Label *pret) { JS_ASSERT(finishedFunctionBodies_); @@ -6141,7 +6144,7 @@ GenerateFFIInterpExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &e unsigned framePushed = StackDecrementForCall(masm, offsetToArgv + argvBytes); Label begin; - GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::FFI, &begin); + GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::SlowFFI, &begin); // Fill the argument array. unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed(); @@ -6200,7 +6203,7 @@ GenerateFFIInterpExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &e } Label profilingReturn; - GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::FFI, &profilingReturn); + GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::SlowFFI, &profilingReturn); return m.finishGeneratingInterpExit(exitIndex, &begin, &profilingReturn) && !masm.oom(); } @@ -6255,7 +6258,7 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit unsigned framePushed = Max(ionFrameSize, coerceFrameSize); Label begin; - GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::FFI, &begin); + GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::IonFFI, &begin); // 1. Descriptor size_t argOffset = offsetToIonArgs; @@ -6415,7 +6418,7 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit masm.bind(&done); Label profilingReturn; - GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::FFI, &profilingReturn); + GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::IonFFI, &profilingReturn); if (oolConvert.used()) { masm.bind(&oolConvert); diff --git a/js/src/jit-test/tests/asm.js/testBug1046688.js b/js/src/jit-test/tests/asm.js/testBug1046688.js new file mode 100644 index 00000000000..692747f0221 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testBug1046688.js @@ -0,0 +1,11 @@ +enableSPSProfiling(); +for (var j = 0; j < 1000; ++j) { + (function(stdlib) { + "use asm"; + var pow = stdlib.Math.pow; + function f() { + return +pow(.0, .0) + } + return f; +})(this)() +} diff --git a/js/src/jit-test/tests/asm.js/testProfiling.js b/js/src/jit-test/tests/asm.js/testProfiling.js index b027704f1f0..3f290458257 100644 --- a/js/src/jit-test/tests/asm.js/testProfiling.js +++ b/js/src/jit-test/tests/asm.js/testProfiling.js @@ -10,7 +10,7 @@ function assertEqualStacks(got, expect) got = String(got).replace(/ \([^\)]*\)/g, ""); // Shorten FFI/entry trampolines - got = got.replace(/FFI trampoline/g, "<").replace(/entry trampoline/g, ">"); + got = got.replace(/(fast|slow) FFI trampoline/g, "<").replace(/entry trampoline/g, ">"); assertEq(got, expect); } diff --git a/js/src/jit/ScalarReplacement.cpp b/js/src/jit/ScalarReplacement.cpp index 53e3fdbc1de..9e244f3167a 100644 --- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -526,7 +526,7 @@ IsArrayEscaped(MInstruction *ins) int32_t index; if (!IndexOf(access, &index)) return true; - if (index < 0 || count <= index) + if (index < 0 || count <= uint32_t(index)) return true; break; } @@ -547,7 +547,7 @@ IsArrayEscaped(MInstruction *ins) int32_t index; if (!IndexOf(access, &index)) return true; - if (index < 0 || count <= index) + if (index < 0 || count <= uint32_t(index)) return true; // We are not yet encoding magic hole constants in resume points. diff --git a/js/src/jit/shared/Assembler-shared.h b/js/src/jit/shared/Assembler-shared.h index ee879a98f41..d861cd982c7 100644 --- a/js/src/jit/shared/Assembler-shared.h +++ b/js/src/jit/shared/Assembler-shared.h @@ -774,6 +774,15 @@ BuiltinToImmKind(AsmJSExit::BuiltinKind builtin) return AsmJSImmKind(builtin); } +static inline bool +ImmKindIsBuiltin(AsmJSImmKind imm, AsmJSExit::BuiltinKind *builtin) +{ + if (unsigned(imm) >= unsigned(AsmJSExit::Builtin_Limit)) + return false; + *builtin = AsmJSExit::BuiltinKind(imm); + return true; +} + // Pointer to be embedded as an immediate in asm.js code. class AsmJSImmPtr { diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index f50838659eb..9c3764482a5 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -689,6 +689,15 @@ GlobalHelperThreadState::canStartGCHelperTask() return !gcHelperWorklist().empty(); } +static void +LeaveParseTaskZone(JSRuntime *rt, ParseTask *task) +{ + // Mark the zone as no longer in use by an ExclusiveContext, and available + // to be collected by the GC. + task->cx->leaveCompartment(task->cx->compartment()); + rt->clearUsedByExclusiveThread(task->cx->zone()); +} + JSScript * GlobalHelperThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token) { @@ -709,13 +718,11 @@ GlobalHelperThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void } JS_ASSERT(parseTask); - // Mark the zone as no longer in use by an ExclusiveContext, and available - // to be collected by the GC. - parseTask->cx->leaveCompartment(parseTask->cx->compartment()); - rt->clearUsedByExclusiveThread(parseTask->cx->zone()); if (!maybecx) { + LeaveParseTaskZone(rt, parseTask); return nullptr; } + JSContext *cx = maybecx; JS_ASSERT(cx->compartment()); @@ -728,9 +735,12 @@ GlobalHelperThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void !GlobalObject::ensureConstructor(cx, global, JSProto_RegExp) || !GlobalObject::ensureConstructor(cx, global, JSProto_Iterator)) { + LeaveParseTaskZone(rt, parseTask); return nullptr; } + LeaveParseTaskZone(rt, parseTask); + // Point the prototypes of any objects in the script's compartment to refer // to the corresponding prototype in the new compartment. This will briefly // create cross compartment pointers, which will be fixed by the diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 1d31c0236be..e8ee855ddb1 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -569,9 +569,9 @@ public: const_cast(aContainerItem ? aContainerItem->ReferenceFrameForChildren() : mBuilder->FindReferenceFrameFor(mContainerFrame)); mContainerAnimatedGeometryRoot = aContainerItem - ? nsLayoutUtils::GetAnimatedGeometryRootFor(aContainerItem, aBuilder) + ? nsLayoutUtils::GetAnimatedGeometryRootFor(aContainerItem, aBuilder, aManager) : mContainerReferenceFrame; - NS_ASSERTION(!aContainerItem || !aContainerItem->ShouldFixToViewport(aBuilder), + NS_ASSERTION(!aContainerItem || !aContainerItem->ShouldFixToViewport(aManager), "Container items never return true for ShouldFixToViewport"); mContainerFixedPosFrame = FindFixedPosFrameForLayerData(mContainerAnimatedGeometryRoot, false); @@ -2563,7 +2563,7 @@ ContainerState::ChooseAnimatedGeometryRoot(const nsDisplayList& aList, // Try using the actual active scrolled root of the backmost item, as that // should result in the least invalidation when scrolling. *aAnimatedGeometryRoot = - nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder); + nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder, mManager); return true; } return false; @@ -2760,7 +2760,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList, } else { forceInactive = false; if (mManager->IsWidgetLayerManager()) { - animatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder); + animatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder, mManager); } else { // For inactive layer subtrees, splitting content into ThebesLayers // based on animated geometry roots is pointless. It's more efficient @@ -2773,7 +2773,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList, } } bool shouldFixToViewport = !animatedGeometryRoot->GetParent() && - item->ShouldFixToViewport(mBuilder); + item->ShouldFixToViewport(mManager); if (maxLayers != -1 && layerCount >= maxLayers) { forceInactive = true; diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index f99d239f3eb..f18ed5c49c0 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -8480,9 +8480,25 @@ nsCSSFrameConstructor::GetInsertionPoint(nsIContent* aContainer, return ::GetContentInsertionFrameFor(aContainer); } + if (nsContentUtils::HasDistributedChildren(aContainer)) { + // The container distributes nodes, use the frame of the flattened tree parent. + // It may be the case that the node is distributed but not matched to any + // insertion points, so there is no flattened parent. + nsIContent* flattenedParent = aChildContent->GetFlattenedTreeParent(); + return flattenedParent ? ::GetContentInsertionFrameFor(flattenedParent) : nullptr; + } + insertionElement = bindingManager->FindNestedInsertionPoint(aContainer, aChildContent); } else { + if (nsContentUtils::HasDistributedChildren(aContainer)) { + // The container distributes nodes to shadow DOM insertion points. + // Return with aMultiple set to true to induce callers to insert children + // individually into the node's flattened tree parent. + *aMultiple = true; + return nullptr; + } + bool multiple; insertionElement = bindingManager->FindNestedSingleInsertionPoint(aContainer, &multiple); diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 86f5ed159c9..db4bc9dc226 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -3115,7 +3115,7 @@ RequiredLayerStateForChildren(nsDisplayListBuilder* aBuilder, LayerState result = LAYER_INACTIVE; for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) { if (result == LAYER_INACTIVE && - nsLayoutUtils::GetAnimatedGeometryRootFor(i, aBuilder) != + nsLayoutUtils::GetAnimatedGeometryRootFor(i, aBuilder, aManager) != aExpectedAnimatedGeometryRootForChildren) { result = LAYER_ACTIVE; } @@ -3334,7 +3334,7 @@ nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder, return LAYER_ACTIVE; return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList, - nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder)); + nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder, aManager)); } bool diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index c29918e5fad..8f275e39eb7 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -1050,7 +1050,7 @@ public: * @return true if the contents of this item are rendered fixed relative * to the nearest viewport. */ - virtual bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) + virtual bool ShouldFixToViewport(LayerManager* aManager) { return false; } /** @@ -2153,11 +2153,12 @@ public: static nsRegion GetInsideClipRegion(nsDisplayItem* aItem, nsPresContext* aPresContext, uint8_t aClip, const nsRect& aRect, bool* aSnap); - virtual bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE + virtual bool ShouldFixToViewport(LayerManager* aManager) MOZ_OVERRIDE { // APZ doesn't (yet) know how to scroll the visible region for these type of // items, so don't layerize them if it's enabled. - if (nsLayoutUtils::UsesAsyncScrolling()) { + if (nsLayoutUtils::UsesAsyncScrolling() || + (aManager && aManager->ShouldAvoidComponentAlphaLayers())) { return false; } diff --git a/layout/base/nsLayoutDebugger.cpp b/layout/base/nsLayoutDebugger.cpp index 37e2b94ab59..edffbe3fbd8 100644 --- a/layout/base/nsLayoutDebugger.cpp +++ b/layout/base/nsLayoutDebugger.cpp @@ -175,7 +175,7 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, aStream << nsPrintfCString(" (opaque %d,%d,%d,%d)", r->x, r->y, r->width, r->height); } - if (aItem->ShouldFixToViewport(aBuilder)) { + if (aItem->ShouldFixToViewport(nullptr)) { aStream << " fixed"; } diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 157ec70b3b1..005dafe6025 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1737,7 +1737,8 @@ nsLayoutUtils::GetAnimatedGeometryRootForFrame(nsIFrame* aFrame, nsIFrame* nsLayoutUtils::GetAnimatedGeometryRootFor(nsDisplayItem* aItem, - nsDisplayListBuilder* aBuilder) + nsDisplayListBuilder* aBuilder, + LayerManager* aManager) { nsIFrame* f = aItem->Frame(); if (aItem->GetType() == nsDisplayItem::TYPE_SCROLL_LAYER) { @@ -1747,7 +1748,7 @@ nsLayoutUtils::GetAnimatedGeometryRootFor(nsDisplayItem* aItem, return GetAnimatedGeometryRootForFrame(scrolledFrame, aBuilder->FindReferenceFrameFor(scrolledFrame)); } - if (aItem->ShouldFixToViewport(aBuilder)) { + if (aItem->ShouldFixToViewport(aManager)) { // Make its active scrolled root be the active scrolled root of // the enclosing viewport, since it shouldn't be scrolled by scrolled // frames in its document. InvalidateFixedBackgroundFramesFromList in diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 1e3dd26879b..e4034e905ad 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -498,7 +498,8 @@ public: * geometry root. */ static nsIFrame* GetAnimatedGeometryRootFor(nsDisplayItem* aItem, - nsDisplayListBuilder* aBuilder); + nsDisplayListBuilder* aBuilder, + mozilla::layers::LayerManager* aManager); /** * Finds the nearest ancestor frame to aFrame that is considered to have (or diff --git a/layout/generic/nsCanvasFrame.h b/layout/generic/nsCanvasFrame.h index d0d7c20e66c..e20344fb8de 100644 --- a/layout/generic/nsCanvasFrame.h +++ b/layout/generic/nsCanvasFrame.h @@ -230,7 +230,7 @@ public: mFrame->Properties().Delete(nsIFrame::CachedBackgroundImageDT()); } - virtual bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE + virtual bool ShouldFixToViewport(LayerManager* aManager) MOZ_OVERRIDE { // Put background-attachment:fixed canvas background images in their own // compositing layer. Since we know their background painting area can't diff --git a/layout/reftests/webcomponents/dynamic-insertion-point-distribution-1-ref.html b/layout/reftests/webcomponents/dynamic-insertion-point-distribution-1-ref.html new file mode 100644 index 00000000000..5cf030c4cea --- /dev/null +++ b/layout/reftests/webcomponents/dynamic-insertion-point-distribution-1-ref.html @@ -0,0 +1,6 @@ + + + +
a b c
+ + diff --git a/layout/reftests/webcomponents/dynamic-insertion-point-distribution-1.html b/layout/reftests/webcomponents/dynamic-insertion-point-distribution-1.html new file mode 100644 index 00000000000..c57f72b379f --- /dev/null +++ b/layout/reftests/webcomponents/dynamic-insertion-point-distribution-1.html @@ -0,0 +1,25 @@ + + + + + +
+ + + diff --git a/layout/reftests/webcomponents/dynamic-insertion-point-distribution-2-ref.html b/layout/reftests/webcomponents/dynamic-insertion-point-distribution-2-ref.html new file mode 100644 index 00000000000..e5121d97e41 --- /dev/null +++ b/layout/reftests/webcomponents/dynamic-insertion-point-distribution-2-ref.html @@ -0,0 +1,6 @@ + + + +
a
+ + diff --git a/layout/reftests/webcomponents/dynamic-insertion-point-distribution-2.html b/layout/reftests/webcomponents/dynamic-insertion-point-distribution-2.html new file mode 100644 index 00000000000..29a850e90f1 --- /dev/null +++ b/layout/reftests/webcomponents/dynamic-insertion-point-distribution-2.html @@ -0,0 +1,26 @@ + + + + + +
+ + + diff --git a/layout/reftests/webcomponents/reftest.list b/layout/reftests/webcomponents/reftest.list index facca9d1f30..74b2cd1d5f9 100644 --- a/layout/reftests/webcomponents/reftest.list +++ b/layout/reftests/webcomponents/reftest.list @@ -14,3 +14,5 @@ pref(dom.webcomponents.enabled,true) == basic-shadow-element-1.html basic-shadow pref(dom.webcomponents.enabled,true) == nested-shadow-element-1.html nested-shadow-element-1-ref.html pref(dom.webcomponents.enabled,true) == update-dist-node-descendants-1.html update-dist-node-descendants-1-ref.html pref(dom.webcomponents.enabled,true) random-if(B2G&&browserIsRemote) == input-transition-1.html input-transition-1-ref.html # Failure on B2G emulator due to Bug 1018381 +pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-1.html dynamic-insertion-point-distribution-1-ref.html +pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-2.html dynamic-insertion-point-distribution-2-ref.html diff --git a/media/libstagefright/binding/Box.cpp b/media/libstagefright/binding/Box.cpp new file mode 100644 index 00000000000..d33cfcc634a --- /dev/null +++ b/media/libstagefright/binding/Box.cpp @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 "mp4_demuxer/Box.h" +#include "mp4_demuxer/mp4_demuxer.h" +#include "mozilla/Endian.h" + +using namespace mozilla; + +namespace mp4_demuxer { + +Box::Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent) + : mContext(aContext), mType(0), mParent(aParent) +{ + uint8_t header[8]; + MediaByteRange headerRange(aOffset, aOffset + sizeof(header)); + if (mParent && !mParent->mRange.Contains(headerRange)) { + return; + } + + const MediaByteRange* byteRange; + for (int i = 0; ; i++) { + if (i == mContext->mByteRanges.Length()) { + return; + } + + byteRange = &mContext->mByteRanges[i]; + if (byteRange->Contains(headerRange)) { + break; + } + } + + size_t bytes; + if (!mContext->mSource->ReadAt(aOffset, header, sizeof(header), &bytes) || + bytes != sizeof(header)) { + return; + } + + uint64_t size = BigEndian::readUint32(header); + if (size == 1) { + uint8_t bigLength[8]; + MediaByteRange bigLengthRange(headerRange.mEnd, + headerRange.mEnd + sizeof(bigLength)); + if ((mParent && !mParent->mRange.Contains(bigLengthRange)) || + !byteRange->Contains(bigLengthRange) || + !mContext->mSource->ReadAt(aOffset, bigLength, + sizeof(bigLengthRange), &bytes) || + bytes != sizeof(bigLengthRange)) { + return; + } + size = BigEndian::readUint64(bigLength); + mChildOffset = bigLengthRange.mEnd; + } else { + mChildOffset = headerRange.mEnd; + } + + MediaByteRange boxRange(aOffset, aOffset + size); + if (boxRange.mEnd > mChildOffset || + (mParent && !mParent->mRange.Contains(boxRange)) || + !byteRange->Contains(boxRange)) { + return; + } + mRange = MediaByteRange(aOffset, aOffset + size); + mType = BigEndian::readUint32(&header[4]); +} + +Box +Box::Next() const +{ + MOZ_ASSERT(IsAvailable()); + return Box(mContext, mRange.mEnd, mParent); +} + +Box +Box::FirstChild() const +{ + MOZ_ASSERT(IsAvailable()); + return Box(mContext, mChildOffset, this); +} + +void +Box::Read(nsTArray* aDest) +{ + aDest->SetLength(mRange.mEnd - mChildOffset); + size_t bytes; + if (!mContext->mSource->ReadAt(mChildOffset, &(*aDest)[0], aDest->Length(), + &bytes) || + bytes != aDest->Length()) { + // Byte ranges are being reported incorrectly + MOZ_ASSERT(false); + aDest->Clear(); + } +} +} diff --git a/media/libstagefright/binding/DecoderData.cpp b/media/libstagefright/binding/DecoderData.cpp index 7267dbc399a..32c1d5e1cef 100644 --- a/media/libstagefright/binding/DecoderData.cpp +++ b/media/libstagefright/binding/DecoderData.cpp @@ -124,11 +124,19 @@ CryptoSample::Update(sp& aMetaData) } void -AudioDecoderConfig::Update(sp& aMetaData, const char* aMimeType) +TrackConfig::Update(sp& aMetaData, const char* aMimeType) { // aMimeType points to a string from MediaDefs.cpp so we don't need to copy it mime_type = aMimeType; duration = FindInt64(aMetaData, kKeyDuration); + mTrackId = FindInt32(aMetaData, kKeyTrackID); + crypto.Update(aMetaData); +} + +void +AudioDecoderConfig::Update(sp& aMetaData, const char* aMimeType) +{ + TrackConfig::Update(aMetaData, aMimeType); channel_count = FindInt32(aMetaData, kKeyChannelCount); bits_per_sample = FindInt32(aMetaData, kKeySampleSize); samples_per_second = FindInt32(aMetaData, kKeySampleRate); @@ -145,8 +153,6 @@ AudioDecoderConfig::Update(sp& aMetaData, const char* aMimeType) size); } } - - crypto.Update(aMetaData); } bool @@ -159,9 +165,7 @@ AudioDecoderConfig::IsValid() void VideoDecoderConfig::Update(sp& aMetaData, const char* aMimeType) { - // aMimeType points to a string from MediaDefs.cpp so we don't need to copy it - mime_type = aMimeType; - duration = FindInt64(aMetaData, kKeyDuration); + TrackConfig::Update(aMetaData, aMimeType); display_width = FindInt32(aMetaData, kKeyDisplayWidth); display_height = FindInt32(aMetaData, kKeyDisplayHeight); @@ -171,8 +175,6 @@ VideoDecoderConfig::Update(sp& aMetaData, const char* aMimeType) extra_data[4] |= 3; annex_b = AnnexB::ConvertExtraDataToAnnexB(extra_data); } - - crypto.Update(aMetaData); } bool diff --git a/media/libstagefright/binding/Index.cpp b/media/libstagefright/binding/Index.cpp index f0a01e4e247..75e2374dc09 100644 --- a/media/libstagefright/binding/Index.cpp +++ b/media/libstagefright/binding/Index.cpp @@ -4,6 +4,7 @@ #include "mp4_demuxer/Index.h" #include "mp4_demuxer/Interval.h" +#include "mp4_demuxer/MoofParser.h" #include "media/stagefright/MediaSource.h" #include "MediaResource.h" @@ -71,11 +72,13 @@ RangeFinder::Contains(MediaByteRange aByteRange) return false; } -void -Index::Init(const stagefright::Vector& aIndex) +Index::Index(const stagefright::Vector& aIndex, + Stream* aSource, uint32_t aTrackId) + : mMonitor("mp4_demuxer::Index") { - MOZ_ASSERT(mIndex.IsEmpty()); - if (!aIndex.isEmpty()) { + if (aIndex.isEmpty()) { + mMoofParser = new MoofParser(aSource, aTrackId); + } else { mIndex.AppendElements(&aIndex[0], aIndex.size()); } } @@ -85,12 +88,29 @@ Index::ConvertByteRangesToTimeRanges( const nsTArray& aByteRanges, nsTArray>* aTimeRanges) { + nsTArray moofIndex; + nsTArray* index; + if (mMoofParser) { + { + MonitorAutoLock mon(mMonitor); + mMoofParser->RebuildFragmentedIndex(aByteRanges); + + // We take the index out of the moof parser and move it into a local + // variable so we don't get concurrency issues. It gets freed when we + // exit this function. + moofIndex = mMoofParser->mIndex; + } + index = &moofIndex; + } else { + index = &mIndex; + } + nsTArray> timeRanges; RangeFinder rangeFinder(aByteRanges); bool hasSync = false; - for (size_t i = 0; i < mIndex.Length(); i++) { - const MediaSource::Indice& indice = mIndex[i]; + for (size_t i = 0; i < index->Length(); i++) { + const MediaSource::Indice& indice = (*index)[i]; if (!rangeFinder.Contains(MediaByteRange(indice.start_offset, indice.end_offset))) { // We process the index in decode order so we clear hasSync when we hit diff --git a/media/libstagefright/binding/MoofParser.cpp b/media/libstagefright/binding/MoofParser.cpp new file mode 100644 index 00000000000..45625bd4fb5 --- /dev/null +++ b/media/libstagefright/binding/MoofParser.cpp @@ -0,0 +1,270 @@ +/* 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 "mp4_demuxer/MoofParser.h" +#include "mp4_demuxer/Box.h" +#include "MediaResource.h" + +using namespace stagefright; +using namespace mozilla; + +namespace mp4_demuxer +{ + +class Moof +{ +public: + Moof(Box& aBox, MoofParser* aMoofParser); + void ParseTraf(Box& aBox); + void ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt); + +private: + MoofParser* mMoofParser; +}; + +void +MoofParser::RebuildFragmentedIndex(const nsTArray& aByteRanges) +{ + BoxContext context(mSource, aByteRanges); + + mIndex.Clear(); + size_t moofCount = 0; + for (size_t i = 0; i + 1 < mMoofOffsets.Length(); i++) { + Box box(&context, mMoofOffsets[i]); + if (box.IsAvailable()) { + MOZ_ASSERT(box.IsType("moof")); + Moof(box, this); + } + } + for (Box box = mMoofOffsets.IsEmpty() + ? Box(&context, 0) + : Box(&context, mMoofOffsets.LastElement()); + box.IsAvailable(); box = box.Next()) { + if (box.IsType("moov")) { + ParseMoov(box); + } else if (box.IsType("moof")) { + if (mMoofOffsets.IsEmpty() || + mMoofOffsets.LastElement() != box.Offset()) { + mMoofOffsets.AppendElement(box.Offset()); + } + Moof(box, this); + } + } +} + +void +MoofParser::ParseMoov(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("trak")) { + ParseTrak(box); + } else if (box.IsType("mvex")) { + ParseMvex(box); + } + } +} + +void +MoofParser::ParseTrak(Box& aBox) +{ + Tkhd tkhd; + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("tkhd")) { + tkhd = Tkhd(box); + } else if (box.IsType("mdia")) { + ParseMdia(box, tkhd); + } + } +} + +void +MoofParser::ParseMdia(Box& aBox, Tkhd& aTkhd) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("mdhd")) { + if (mTrackId == aTkhd.mTrackId) { + mMdhd = Mdhd(box); + } + } + } +} + +void +MoofParser::ParseMvex(Box& aBox) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("trex")) { + mTrex = Trex(box); + } + } +} + +Moof::Moof(Box& aBox, MoofParser* aMoofParser) : mMoofParser(aMoofParser) +{ + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("traf")) { + ParseTraf(box); + } + } +} + +void +Moof::ParseTraf(Box& aBox) +{ + Tfhd tfhd(mMoofParser->mTrex); + Tfdt tfdt; + for (Box box = aBox.FirstChild(); box.IsAvailable(); box = box.Next()) { + if (box.IsType("tfhd")) { + tfhd = Tfhd(box, mMoofParser->mTrex); + } else if (box.IsType("tfdt")) { + tfdt = Tfdt(box); + } else if (box.IsType("trun")) { + if (mMoofParser->mTrackId == tfhd.mTrackId) { + ParseTrun(box, tfhd, tfdt); + } + } + } +} + +void +Moof::ParseTrun(Box& aBox, Tfhd& aTfhd, Tfdt& aTfdt) +{ + if (!mMoofParser->mMdhd.mTimescale) { + return; + } + + BoxReader reader(aBox); + uint32_t flags = reader->ReadU32(); + if ((flags & 0x404) == 0x404) { + // Can't use these flags together + reader->DiscardRemaining(); + return; + } + + uint32_t sampleCount = reader->ReadU32(); + uint64_t offset = aTfhd.mBaseDataOffset + (flags & 1 ? reader->ReadU32() : 0); + bool hasFirstSampleFlags = flags & 4; + uint32_t firstSampleFlags = hasFirstSampleFlags ? reader->ReadU32() : 0; + uint64_t decodeTime = aTfdt.mBaseMediaDecodeTime; + for (size_t i = 0; i < sampleCount; i++) { + uint32_t sampleDuration = + flags & 0x100 ? reader->ReadU32() : aTfhd.mDefaultSampleDuration; + uint32_t sampleSize = + flags & 0x200 ? reader->ReadU32() : aTfhd.mDefaultSampleSize; + uint32_t sampleFlags = + flags & 0x400 ? reader->ReadU32() : hasFirstSampleFlags && i == 0 + ? firstSampleFlags + : aTfhd.mDefaultSampleFlags; + uint32_t ctsOffset = flags & 0x800 ? reader->ReadU32() : 0; + + MediaSource::Indice indice; + indice.start_offset = offset; + offset += sampleSize; + indice.end_offset = offset; + + indice.start_composition = + ((decodeTime + ctsOffset) * 1000000ll) / mMoofParser->mMdhd.mTimescale; + decodeTime += sampleDuration; + indice.end_composition = + ((decodeTime + ctsOffset) * 1000000ll) / mMoofParser->mMdhd.mTimescale; + + indice.sync = !(sampleFlags & 0x1010000); + + mMoofParser->mIndex.AppendElement(indice); + } +} + +Tkhd::Tkhd(Box& aBox) +{ + BoxReader reader(aBox); + uint32_t flags = reader->ReadU32(); + uint8_t version = flags >> 24; + if (version == 0) { + mCreationTime = reader->ReadU32(); + mModificationTime = reader->ReadU32(); + mTrackId = reader->ReadU32(); + uint32_t reserved = reader->ReadU32(); + NS_ASSERTION(!reserved, "reserved should be 0"); + mDuration = reader->ReadU32(); + } else if (version == 1) { + mCreationTime = reader->ReadU64(); + mModificationTime = reader->ReadU64(); + mTrackId = reader->ReadU32(); + uint32_t reserved = reader->ReadU32(); + NS_ASSERTION(!reserved, "reserved should be 0"); + mDuration = reader->ReadU64(); + } + // More stuff that we don't care about + reader->DiscardRemaining(); +} + +Mdhd::Mdhd(Box& aBox) +{ + BoxReader reader(aBox); + uint32_t flags = reader->ReadU32(); + uint8_t version = flags >> 24; + if (version == 0) { + mCreationTime = reader->ReadU32(); + mModificationTime = reader->ReadU32(); + mTimescale = reader->ReadU32(); + mDuration = reader->ReadU32(); + } else if (version == 1) { + mCreationTime = reader->ReadU64(); + mModificationTime = reader->ReadU64(); + mTimescale = reader->ReadU32(); + mDuration = reader->ReadU64(); + } + // language and pre_defined=0 + reader->ReadU32(); +} + +Trex::Trex(Box& aBox) +{ + BoxReader reader(aBox); + mFlags = reader->ReadU32(); + mTrackId = reader->ReadU32(); + mDefaultSampleDescriptionIndex = reader->ReadU32(); + mDefaultSampleDuration = reader->ReadU32(); + mDefaultSampleSize = reader->ReadU32(); + mDefaultSampleFlags = reader->ReadU32(); +} + +Tfhd::Tfhd(Box& aBox, Trex& aTrex) : Trex(aTrex) +{ + MOZ_ASSERT(aBox.IsType("tfhd")); + MOZ_ASSERT(aBox.Parent()->IsType("traf")); + MOZ_ASSERT(aBox.Parent()->Parent()->IsType("moof")); + + BoxReader reader(aBox); + mFlags = reader->ReadU32(); + mBaseDataOffset = + mFlags & 1 ? reader->ReadU32() : aBox.Parent()->Parent()->Offset(); + mTrackId = reader->ReadU32(); + if (mFlags & 2) { + mDefaultSampleDescriptionIndex = reader->ReadU32(); + } + if (mFlags & 8) { + mDefaultSampleDuration = reader->ReadU32(); + } + if (mFlags & 0x10) { + mDefaultSampleSize = reader->ReadU32(); + } + if (mFlags & 0x20) { + mDefaultSampleFlags = reader->ReadU32(); + } +} + +Tfdt::Tfdt(Box& aBox) +{ + BoxReader reader(aBox); + uint32_t flags = reader->ReadU32(); + uint8_t version = flags >> 24; + if (version == 0) { + mBaseMediaDecodeTime = reader->ReadU32(); + } else if (version == 1) { + mBaseMediaDecodeTime = reader->ReadU64(); + } + reader->DiscardRemaining(); +} +} diff --git a/media/libstagefright/binding/include/mp4_demuxer/Box.h b/media/libstagefright/binding/include/mp4_demuxer/Box.h new file mode 100644 index 00000000000..c5dbf600bd0 --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/Box.h @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 BOX_H_ +#define BOX_H_ + +#include +#include "nsTArray.h" +#include "MediaResource.h" +#include "mozilla/Endian.h" +#include "mp4_demuxer/ByteReader.h" + +using namespace mozilla; + +namespace mp4_demuxer { + +class Stream; + +class BoxContext +{ +public: + BoxContext(Stream* aSource, const nsTArray& aByteRanges) + : mSource(aSource), mByteRanges(aByteRanges) + { + } + + Stream* mSource; + const nsTArray& mByteRanges; +}; + +class Box +{ +public: + Box(BoxContext* aContext, uint64_t aOffset, const Box* aParent = nullptr); + + bool IsAvailable() const { return !mRange.IsNull(); } + uint64_t Offset() const { return mRange.mStart; } + uint64_t Length() const { return mRange.mEnd - mRange.mStart; } + const Box* Parent() const { return mParent; } + + bool IsType(const char* aType) const + { + return mType == BigEndian::readUint32(aType); + } + + Box Next() const; + Box FirstChild() const; + void Read(nsTArray* aDest); + +private: + bool Contains(MediaByteRange aRange) const; + BoxContext* mContext; + mozilla::MediaByteRange mRange; + uint64_t mChildOffset; + uint32_t mType; + const Box* mParent; +}; + +class BoxReader +{ +public: + BoxReader(Box& aBox) + { + aBox.Read(&mBuffer); + mReader.SetData(mBuffer); + } + ByteReader* operator->() { return &mReader; } + ByteReader mReader; + +private: + nsTArray mBuffer; +}; +} + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h b/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h index 6f5dd6b6ad2..8897d976fbf 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h +++ b/media/libstagefright/binding/include/mp4_demuxer/ByteReader.h @@ -5,15 +5,16 @@ #ifndef BYTE_READER_H_ #define BYTE_READER_H_ +#include "mozilla/Endian.h" #include "mozilla/Vector.h" #include "nsTArray.h" -namespace mp4_demuxer -{ +namespace mp4_demuxer { class ByteReader { public: + ByteReader() : mPtr(nullptr), mRemaining(0) {} ByteReader(const mozilla::Vector& aData) : mPtr(&aData[0]), mRemaining(aData.length()) { @@ -22,6 +23,12 @@ public: : mPtr(aData), mRemaining(aSize) { } + void SetData(const nsTArray& aData) + { + MOZ_ASSERT(!mPtr && !mRemaining); + mPtr = &aData[0]; + mRemaining = aData.Length(); + } ~ByteReader() { @@ -56,7 +63,27 @@ public: MOZ_ASSERT(false); return 0; } - return ptr[0] << 8 | ptr[1]; + return mozilla::BigEndian::readUint16(ptr); + } + + uint32_t ReadU32() + { + auto ptr = Read(4); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::BigEndian::readUint32(ptr); + } + + uint64_t ReadU64() + { + auto ptr = Read(8); + if (!ptr) { + MOZ_ASSERT(false); + return 0; + } + return mozilla::BigEndian::readUint64(ptr); } const uint8_t* Read(size_t aCount) diff --git a/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h index 288cb9376b4..c59fded4f00 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h +++ b/media/libstagefright/binding/include/mp4_demuxer/DecoderData.h @@ -67,20 +67,31 @@ public: nsTArray iv; }; -class AudioDecoderConfig +class TrackConfig +{ +public: + TrackConfig() : mime_type(nullptr), mTrackId(0), duration(0) {} + const char* mime_type; + uint32_t mTrackId; + int64_t duration; + CryptoTrack crypto; + + void Update(stagefright::sp& aMetaData, + const char* aMimeType); +}; + +class AudioDecoderConfig : public TrackConfig { public: AudioDecoderConfig() - : mime_type(nullptr) - , duration(0) - , channel_count(0) + : channel_count(0) , bits_per_sample(0) , samples_per_second(0) + , frequency_index(0) , aac_profile(0) { } - const char* mime_type; int64_t duration; uint32_t channel_count; uint32_t bits_per_sample; @@ -90,7 +101,8 @@ public: mozilla::Vector audio_specific_config; CryptoTrack crypto; - void Update(stagefright::sp& aMetaData, const char* aMimeType); + void Update(stagefright::sp& aMetaData, + const char* aMimeType); bool IsValid(); private: @@ -98,27 +110,19 @@ private: int8_t aac_profile; }; -class VideoDecoderConfig +class VideoDecoderConfig : public TrackConfig { public: - VideoDecoderConfig() - : mime_type(nullptr) - , duration(0) - , display_width(0) - , display_height(0) - { - } + VideoDecoderConfig() : display_width(0), display_height(0) {} - const char* mime_type; - int64_t duration; int32_t display_width; int32_t display_height; mozilla::Vector extra_data; // Unparsed AVCDecoderConfig payload. mozilla::Vector annex_b; // Parsed version for sample prepend. - CryptoTrack crypto; - void Update(stagefright::sp& aMetaData, const char* aMimeType); + void Update(stagefright::sp& aMetaData, + const char* aMimeType); bool IsValid(); }; diff --git a/media/libstagefright/binding/include/mp4_demuxer/Index.h b/media/libstagefright/binding/include/mp4_demuxer/Index.h index 01fe1ce8271..81bb23824ad 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/Index.h +++ b/media/libstagefright/binding/include/mp4_demuxer/Index.h @@ -6,26 +6,28 @@ #define INDEX_H_ #include "media/stagefright/MediaSource.h" +#include "mozilla/Monitor.h" #include "mp4_demuxer/mp4_demuxer.h" namespace mp4_demuxer { template class Interval; +class MoofParser; class Index { public: - Index() {} - - void Init( - const stagefright::Vector& aIndex); + Index(const stagefright::Vector& aIndex, + Stream* aSource, uint32_t aTrackId); void ConvertByteRangesToTimeRanges( const nsTArray& aByteRanges, nsTArray>* aTimeRanges); private: + mozilla::Monitor mMonitor; nsTArray mIndex; + nsAutoPtr mMoofParser; }; } diff --git a/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h new file mode 100644 index 00000000000..25d5074cf7c --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/MoofParser.h @@ -0,0 +1,119 @@ +/* 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 MOOF_PARSER_H_ +#define MOOF_PARSER_H_ + +#include "media/stagefright/MediaSource.h" +#include "mp4_demuxer/mp4_demuxer.h" + +namespace mozilla { class MediaByteRange; } + +namespace mp4_demuxer { + +class Stream; +class Box; + +class Tkhd +{ +public: + Tkhd() + : mCreationTime(0) + , mModificationTime(0) + , mTrackId(0) + , mDuration(0) + { + } + Tkhd(Box& aBox); + + uint64_t mCreationTime; + uint64_t mModificationTime; + uint32_t mTrackId; + uint64_t mDuration; +}; + +class Mdhd +{ +public: + Mdhd() + : mCreationTime(0) + , mModificationTime(0) + , mTimescale(0) + , mDuration(0) + { + } + Mdhd(Box& aBox); + + uint64_t mCreationTime; + uint64_t mModificationTime; + uint32_t mTimescale; + uint64_t mDuration; +}; + +class Trex +{ +public: + Trex() + : mFlags(0) + , mTrackId(0) + , mDefaultSampleDescriptionIndex(0) + , mDefaultSampleDuration(0) + , mDefaultSampleSize(0) + , mDefaultSampleFlags(0) + { + } + + Trex(Box& aBox); + + uint32_t mFlags; + uint32_t mTrackId; + uint32_t mDefaultSampleDescriptionIndex; + uint32_t mDefaultSampleDuration; + uint32_t mDefaultSampleSize; + uint32_t mDefaultSampleFlags; +}; + +class Tfhd : public Trex +{ +public: + Tfhd(Trex& aTrex) : Trex(aTrex), mBaseDataOffset(0) {} + Tfhd(Box& aBox, Trex& aTrex); + + uint64_t mBaseDataOffset; +}; + +class Tfdt +{ +public: + Tfdt() : mBaseMediaDecodeTime(0) {} + Tfdt(Box& aBox); + + uint64_t mBaseMediaDecodeTime; +}; + +class MoofParser +{ +public: + MoofParser(Stream* aSource, uint32_t aTrackId) + : mSource(aSource), mTrackId(aTrackId) + { + } + void RebuildFragmentedIndex( + const nsTArray& aByteRanges); + void ParseMoov(Box& aBox); + void ParseTrak(Box& aBox); + void ParseMdia(Box& aBox, Tkhd& aTkhd); + void ParseMvex(Box& aBox); + + nsRefPtr mSource; + uint32_t mTrackId; + nsTArray mMoofOffsets; + Mdhd mMdhd; + Trex mTrex; + Tfdt mTfdt; + nsTArray mIndex; +}; +} + +#endif diff --git a/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h b/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h index f322691aa23..72fa4c6690c 100644 --- a/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h +++ b/media/libstagefright/binding/include/mp4_demuxer/mp4_demuxer.h @@ -9,6 +9,7 @@ #include "nsTArray.h" #include "mp4_demuxer/DecoderData.h" #include "mp4_demuxer/Interval.h" +#include "nsISupportsImpl.h" namespace mozilla { class MediaByteRange; } @@ -20,12 +21,14 @@ typedef int64_t Microseconds; class Stream { - public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Stream); + virtual bool ReadAt(int64_t offset, void* data, size_t size, size_t* bytes_read) = 0; virtual bool Length(int64_t* size) = 0; +protected: virtual ~Stream() {} }; @@ -66,6 +69,7 @@ private: CryptoFile mCrypto; nsAutoPtr mPrivate; + nsRefPtr mSource; }; } // namespace mozilla diff --git a/media/libstagefright/binding/mp4_demuxer.cpp b/media/libstagefright/binding/mp4_demuxer.cpp index a3078ef27bf..f4d9242bb12 100644 --- a/media/libstagefright/binding/mp4_demuxer.cpp +++ b/media/libstagefright/binding/mp4_demuxer.cpp @@ -24,11 +24,11 @@ struct StageFrightPrivate sp mAudio; MediaSource::ReadOptions mAudioOptions; - Index mAudioIndex; + nsAutoPtr mAudioIndex; sp mVideo; MediaSource::ReadOptions mVideoOptions; - Index mVideoIndex; + nsAutoPtr mVideoIndex; }; class DataSourceAdapter : public DataSource @@ -66,11 +66,11 @@ public: virtual status_t reconnectAtOffset(off64_t offset) { return NO_ERROR; } private: - nsAutoPtr mSource; + nsRefPtr mSource; }; MP4Demuxer::MP4Demuxer(Stream* source) - : mPrivate(new StageFrightPrivate()) + : mPrivate(new StageFrightPrivate()), mSource(source) { mPrivate->mExtractor = new MPEG4Extractor(new DataSourceAdapter(source)); } @@ -102,13 +102,13 @@ MP4Demuxer::Init() mPrivate->mAudio->start(); mAudioConfig.Update(metaData, mimeType); auto index = mPrivate->mAudio->exportIndex(); - mPrivate->mAudioIndex.Init(index); + mPrivate->mAudioIndex = new Index(index, mSource, mAudioConfig.mTrackId); } else if (!mPrivate->mVideo.get() && !strncmp(mimeType, "video/", 6)) { mPrivate->mVideo = e->getTrack(i); mPrivate->mVideo->start(); mVideoConfig.Update(metaData, mimeType); auto index = mPrivate->mVideo->exportIndex(); - mPrivate->mVideoIndex.Init(index); + mPrivate->mVideoIndex = new Index(index, mSource, mVideoConfig.mTrackId); } } sp metaData = e->getMetaData(); @@ -207,22 +207,22 @@ MP4Demuxer::ConvertByteRangesToTime( if (HasValidVideo()) { nsTArray> ranges; if (!HasValidAudio()) { - mPrivate->mVideoIndex.ConvertByteRangesToTimeRanges(aByteRanges, - aIntervals); + mPrivate->mVideoIndex->ConvertByteRangesToTimeRanges(aByteRanges, + aIntervals); return; } - mPrivate->mVideoIndex.ConvertByteRangesToTimeRanges(aByteRanges, &video); + mPrivate->mVideoIndex->ConvertByteRangesToTimeRanges(aByteRanges, &video); } nsTArray> audio; if (HasValidAudio()) { nsTArray> ranges; if (!HasValidVideo()) { - mPrivate->mAudioIndex.ConvertByteRangesToTimeRanges(aByteRanges, - aIntervals); + mPrivate->mAudioIndex->ConvertByteRangesToTimeRanges(aByteRanges, + aIntervals); return; } - mPrivate->mAudioIndex.ConvertByteRangesToTimeRanges(aByteRanges, &audio); + mPrivate->mAudioIndex->ConvertByteRangesToTimeRanges(aByteRanges, &audio); } Interval::Intersection(audio, video, aIntervals); diff --git a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp index b70a4f10712..4549779ad23 100644 --- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp @@ -3653,6 +3653,10 @@ status_t MPEG4Source::fragmentedRead( Vector MPEG4Source::exportIndex() { Vector index; + if (!mTimescale) { + return index; + } + for (uint32_t sampleIndex = 0; sampleIndex < mSampleTable->countSamples(); sampleIndex++) { off64_t offset; diff --git a/media/libstagefright/moz.build b/media/libstagefright/moz.build index 977404e1699..2205b2566ba 100644 --- a/media/libstagefright/moz.build +++ b/media/libstagefright/moz.build @@ -23,14 +23,16 @@ elif CONFIG['OS_TARGET'] == 'Darwin': DEFINES['HAVE_SYS_UIO_H'] = True DEFINES['off64_t'] = 'off_t' LOCAL_INCLUDES += [ 'ports/darwin/include' ] -elif CONFIG['OS_TARGET'] in ('DragonFly', 'FreeBSD', 'OpenBSD', 'NetBSD'): - if not CONFIG['OS_TARGET'] == 'NetBSD': +elif CONFIG['OS_TARGET'] in ('DragonFly', 'FreeBSD', 'OpenBSD', 'NetBSD', + 'GNU/kFreeBSD'): + if CONFIG['OS_TARGET'] != 'NetBSD': DEFINES['ENODATA'] = '-0x80000003' if CONFIG['OS_TARGET'] == 'OpenBSD': DEFINES['EBADMSG'] = '-0x80000006' DEFINES['HAVE_SYS_UIO_H'] = True - DEFINES['off64_t'] = 'off_t' - LOCAL_INCLUDES += [ 'ports/bsd/include' ] + if CONFIG['OS_TARGET'] != 'GNU/kFreeBSD': + DEFINES['off64_t'] = 'off_t' + LOCAL_INCLUDES += [ 'ports/bsd/include' ] else: DEFINES['HAVE_SYS_UIO_H'] = True @@ -64,8 +66,10 @@ SOURCES += [ UNIFIED_SOURCES += [ 'binding/Adts.cpp', 'binding/AnnexB.cpp', + 'binding/Box.cpp', 'binding/DecoderData.cpp', 'binding/Index.cpp', + 'binding/MoofParser.cpp', 'binding/mp4_demuxer.cpp', 'frameworks/av/media/libstagefright/DataSource.cpp', 'frameworks/av/media/libstagefright/ESDS.cpp', diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp index 605821b4cc7..50076b34de0 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp @@ -543,7 +543,8 @@ PeerConnectionMedia::DtlsConnected(TransportLayer *dtlsLayer, bool privacyRequested = false; // TODO (Bug 952678) set privacy mode, ask the DTLS layer about that GetMainThread()->Dispatch( - WrapRunnable(mParent, &PeerConnectionImpl::SetDtlsConnected, privacyRequested), + WrapRunnable(nsRefPtr(mParent), + &PeerConnectionImpl::SetDtlsConnected, privacyRequested), NS_DISPATCH_NORMAL); } @@ -749,12 +750,13 @@ RefPtr SourceStreamInfo::GetPipelineByLevel_m(int level) { bool RemoteSourceStreamInfo::SetUsingBundle_m(int aLevel, bool decision) { ASSERT_ON_THREAD(mParent->GetMainThread()); - RefPtr pipeline(GetPipelineByLevel_m(aLevel)); + // Avoid adding and dropping an extra ref + MediaPipeline *pipeline = GetPipelineByLevel_m(aLevel); if (pipeline) { RUN_ON_THREAD(mParent->GetSTSThread(), WrapRunnable( - pipeline, + RefPtr(pipeline), &MediaPipeline::SetUsingBundle_s, decision ), diff --git a/memory/mozalloc/VolatileBufferAshmem.cpp b/memory/mozalloc/VolatileBufferAshmem.cpp index 66895d2ad28..b0d5f58e212 100644 --- a/memory/mozalloc/VolatileBufferAshmem.cpp +++ b/memory/mozalloc/VolatileBufferAshmem.cpp @@ -52,8 +52,6 @@ VolatileBuffer::Init(size_t aSize, size_t aAlignment) } if (ioctl(mFd, ASHMEM_SET_SIZE, mSize) < 0) { - close(mFd); - mFd = -1; goto heap_alloc; } @@ -63,6 +61,12 @@ VolatileBuffer::Init(size_t aSize, size_t aAlignment) } heap_alloc: + mBuf = nullptr; + if (mFd >= 0) { + close(mFd); + mFd = -1; + } + #ifdef MOZ_MEMORY #ifdef MOZ_WIDGET_ANDROID __wrap_posix_memalign(&mBuf, aAlignment, aSize); diff --git a/mfbt/HashFunctions.h b/mfbt/HashFunctions.h index 8b2c172bd14..86052a7afe4 100644 --- a/mfbt/HashFunctions.h +++ b/mfbt/HashFunctions.h @@ -292,13 +292,13 @@ HashKnownLength(const T* aStr, size_t aLength) MOZ_WARN_UNUSED_RESULT inline uint32_t HashString(const char* aStr) { - return detail::HashUntilZero(aStr); + return detail::HashUntilZero(reinterpret_cast(aStr)); } MOZ_WARN_UNUSED_RESULT inline uint32_t HashString(const char* aStr, size_t aLength) { - return detail::HashKnownLength(aStr, aLength); + return detail::HashKnownLength(reinterpret_cast(aStr), aLength); } MOZ_WARN_UNUSED_RESULT diff --git a/mfbt/UniquePtr.h b/mfbt/UniquePtr.h index bddb6169264..45a8e80565a 100644 --- a/mfbt/UniquePtr.h +++ b/mfbt/UniquePtr.h @@ -154,174 +154,180 @@ namespace mozilla { template class UniquePtr { - public: - typedef T* Pointer; - typedef T ElementType; - typedef D DeleterType; +public: + typedef T* Pointer; + typedef T ElementType; + typedef D DeleterType; - private: - Pair tuple; +private: + Pair mTuple; - Pointer& ptr() { return tuple.first(); } - const Pointer& ptr() const { return tuple.first(); } + Pointer& ptr() { return mTuple.first(); } + const Pointer& ptr() const { return mTuple.first(); } - DeleterType& del() { return tuple.second(); } - const DeleterType& del() const { return tuple.second(); } + DeleterType& del() { return mTuple.second(); } + const DeleterType& del() const { return mTuple.second(); } - public: - /** - * Construct a UniquePtr containing |nullptr|. - */ - MOZ_CONSTEXPR UniquePtr() - : tuple(static_cast(nullptr), DeleterType()) - { - static_assert(!IsPointer::value, "must provide a deleter instance"); - static_assert(!IsReference::value, "must provide a deleter instance"); +public: + /** + * Construct a UniquePtr containing |nullptr|. + */ + MOZ_CONSTEXPR UniquePtr() + : mTuple(static_cast(nullptr), DeleterType()) + { + static_assert(!IsPointer::value, "must provide a deleter instance"); + static_assert(!IsReference::value, "must provide a deleter instance"); + } + + /** + * Construct a UniquePtr containing |aPtr|. + */ + explicit UniquePtr(Pointer aPtr) + : mTuple(aPtr, DeleterType()) + { + static_assert(!IsPointer::value, "must provide a deleter instance"); + static_assert(!IsReference::value, "must provide a deleter instance"); + } + + UniquePtr(Pointer aPtr, + typename Conditional::value, + D, + const D&>::Type aD1) + : mTuple(aPtr, aD1) + {} + + // If you encounter an error with MSVC10 about RemoveReference below, along + // the lines that "more than one partial specialization matches the template + // argument list": don't use UniquePtr! Ideally + // you should make deletion use the same function every time, using a + // deleter policy: + // + // // BAD, won't compile with MSVC10, deleter doesn't need to be a + // // variable at all + // typedef void (&FreeSignature)(void*); + // UniquePtr ptr((int*) malloc(sizeof(int)), free); + // + // // GOOD, compiles with MSVC10, deletion behavior statically known and + // // optimizable + // struct DeleteByFreeing + // { + // void operator()(void* aPtr) { free(aPtr); } + // }; + // + // If deletion really, truly, must be a variable: you might be able to work + // around this with a deleter class that contains the function reference. + // But this workaround is untried and untested, because variable deletion + // behavior really isn't something you should use. + UniquePtr(Pointer aPtr, + typename RemoveReference::Type&& aD2) + : mTuple(aPtr, Move(aD2)) + { + static_assert(!IsReference::value, + "rvalue deleter can't be stored by reference"); + } + + UniquePtr(UniquePtr&& aOther) + : mTuple(aOther.release(), Forward(aOther.getDeleter())) + {} + + template + UniquePtr(N, + typename EnableIf::value, int>::Type aDummy = 0) + : mTuple(static_cast(nullptr), DeleterType()) + { + static_assert(!IsPointer::value, "must provide a deleter instance"); + static_assert(!IsReference::value, "must provide a deleter instance"); + } + + template + UniquePtr(UniquePtr&& aOther, + typename EnableIf::Pointer, + Pointer>::value && + !IsArray::value && + (IsReference::value + ? IsSame::value + : IsConvertible::value), + int>::Type aDummy = 0) + : mTuple(aOther.release(), Forward(aOther.getDeleter())) + { + } + + ~UniquePtr() { reset(nullptr); } + + UniquePtr& operator=(UniquePtr&& aOther) + { + reset(aOther.release()); + getDeleter() = Forward(aOther.getDeleter()); + return *this; + } + + template + UniquePtr& operator=(UniquePtr&& aOther) + { + static_assert(IsConvertible::Pointer, + Pointer>::value, + "incompatible UniquePtr pointees"); + static_assert(!IsArray::value, + "can't assign from UniquePtr holding an array"); + + reset(aOther.release()); + getDeleter() = Forward(aOther.getDeleter()); + return *this; + } + + UniquePtr& operator=(NullptrT aNull) + { + MOZ_ASSERT(aNull == nullptr); + reset(nullptr); + return *this; + } + + T& operator*() const { return *get(); } + Pointer operator->() const + { + MOZ_ASSERT(get(), "dereferencing a UniquePtr containing nullptr"); + return get(); + } + + Pointer get() const { return ptr(); } + + DeleterType& getDeleter() { return del(); } + const DeleterType& getDeleter() const { return del(); } + +private: + typedef void (UniquePtr::* ConvertibleToBool)(double, char); + void nonNull(double, char) {} + +public: + operator ConvertibleToBool() const + { + return get() != nullptr ? &UniquePtr::nonNull : nullptr; + } + + Pointer release() + { + Pointer p = ptr(); + ptr() = nullptr; + return p; + } + + void reset(Pointer aPtr = Pointer()) + { + Pointer old = ptr(); + ptr() = aPtr; + if (old != nullptr) { + getDeleter()(old); } + } - /** - * Construct a UniquePtr containing |p|. - */ - explicit UniquePtr(Pointer p) - : tuple(p, DeleterType()) - { - static_assert(!IsPointer::value, "must provide a deleter instance"); - static_assert(!IsReference::value, "must provide a deleter instance"); - } + void swap(UniquePtr& aOther) + { + mTuple.swap(aOther.mTuple); + } - UniquePtr(Pointer p, - typename Conditional::value, - D, - const D&>::Type d1) - : tuple(p, d1) - {} - - // If you encounter an error with MSVC10 about RemoveReference below, along - // the lines that "more than one partial specialization matches the template - // argument list": don't use UniquePtr! Ideally - // you should make deletion use the same function every time, using a - // deleter policy: - // - // // BAD, won't compile with MSVC10, deleter doesn't need to be a - // // variable at all - // typedef void (&FreeSignature)(void*); - // UniquePtr ptr((int*) malloc(sizeof(int)), free); - // - // // GOOD, compiles with MSVC10, deletion behavior statically known and - // // optimizable - // struct DeleteByFreeing - // { - // void operator()(void* ptr) { free(ptr); } - // }; - // - // If deletion really, truly, must be a variable: you might be able to work - // around this with a deleter class that contains the function reference. - // But this workaround is untried and untested, because variable deletion - // behavior really isn't something you should use. - UniquePtr(Pointer p, - typename RemoveReference::Type&& d2) - : tuple(p, Move(d2)) - { - static_assert(!IsReference::value, - "rvalue deleter can't be stored by reference"); - } - - UniquePtr(UniquePtr&& other) - : tuple(other.release(), Forward(other.getDeleter())) - {} - - template - UniquePtr(N, - typename EnableIf::value, int>::Type dummy = 0) - : tuple(static_cast(nullptr), DeleterType()) - { - static_assert(!IsPointer::value, "must provide a deleter instance"); - static_assert(!IsReference::value, "must provide a deleter instance"); - } - - template - UniquePtr(UniquePtr&& other, - typename EnableIf::Pointer, - Pointer>::value && - !IsArray::value && - (IsReference::value - ? IsSame::value - : IsConvertible::value), - int>::Type dummy = 0) - : tuple(other.release(), Forward(other.getDeleter())) - { - } - - ~UniquePtr() { - reset(nullptr); - } - - UniquePtr& operator=(UniquePtr&& other) { - reset(other.release()); - getDeleter() = Forward(other.getDeleter()); - return *this; - } - - template - UniquePtr& operator=(UniquePtr&& other) - { - static_assert(IsConvertible::Pointer, Pointer>::value, - "incompatible UniquePtr pointees"); - static_assert(!IsArray::value, - "can't assign from UniquePtr holding an array"); - - reset(other.release()); - getDeleter() = Forward(other.getDeleter()); - return *this; - } - - UniquePtr& operator=(NullptrT n) { - MOZ_ASSERT(n == nullptr); - reset(nullptr); - return *this; - } - - T& operator*() const { return *get(); } - Pointer operator->() const { - MOZ_ASSERT(get(), "dereferencing a UniquePtr containing nullptr"); - return get(); - } - - Pointer get() const { return ptr(); } - - DeleterType& getDeleter() { return del(); } - const DeleterType& getDeleter() const { return del(); } - - private: - typedef void (UniquePtr::* ConvertibleToBool)(double, char); - void nonNull(double, char) {} - - public: - operator ConvertibleToBool() const { - return get() != nullptr ? &UniquePtr::nonNull : nullptr; - } - - Pointer release() { - Pointer p = ptr(); - ptr() = nullptr; - return p; - } - - void reset(Pointer p = Pointer()) { - Pointer old = ptr(); - ptr() = p; - if (old != nullptr) { - getDeleter()(old); - } - } - - void swap(UniquePtr& other) { - tuple.swap(other.tuple); - } - - private: - UniquePtr(const UniquePtr& other) MOZ_DELETE; // construct using Move()! - void operator=(const UniquePtr& other) MOZ_DELETE; // assign using Move()! +private: + UniquePtr(const UniquePtr& aOther) MOZ_DELETE; // construct using Move()! + void operator=(const UniquePtr& aOther) MOZ_DELETE; // assign using Move()! }; // In case you didn't read the comment by the main definition (you should!): the @@ -331,246 +337,249 @@ class UniquePtr template class UniquePtr { - public: - typedef T* Pointer; - typedef T ElementType; - typedef D DeleterType; +public: + typedef T* Pointer; + typedef T ElementType; + typedef D DeleterType; - private: - Pair tuple; +private: + Pair mTuple; - public: - /** - * Construct a UniquePtr containing nullptr. - */ - MOZ_CONSTEXPR UniquePtr() - : tuple(static_cast(nullptr), DeleterType()) - { - static_assert(!IsPointer::value, "must provide a deleter instance"); - static_assert(!IsReference::value, "must provide a deleter instance"); +public: + /** + * Construct a UniquePtr containing nullptr. + */ + MOZ_CONSTEXPR UniquePtr() + : mTuple(static_cast(nullptr), DeleterType()) + { + static_assert(!IsPointer::value, "must provide a deleter instance"); + static_assert(!IsReference::value, "must provide a deleter instance"); + } + + /** + * Construct a UniquePtr containing |aPtr|. + */ + explicit UniquePtr(Pointer aPtr) + : mTuple(aPtr, DeleterType()) + { + static_assert(!IsPointer::value, "must provide a deleter instance"); + static_assert(!IsReference::value, "must provide a deleter instance"); + } + +private: + // delete[] knows how to handle *only* an array of a single class type. For + // delete[] to work correctly, it must know the size of each element, the + // fields and base classes of each element requiring destruction, and so on. + // So forbid all overloads which would end up invoking delete[] on a pointer + // of the wrong type. + template + UniquePtr(U&& aU, + typename EnableIf::value && + IsConvertible::value, + int>::Type aDummy = 0) + MOZ_DELETE; + +public: + UniquePtr(Pointer aPtr, + typename Conditional::value, + D, + const D&>::Type aD1) + : mTuple(aPtr, aD1) + {} + + // If you encounter an error with MSVC10 about RemoveReference below, along + // the lines that "more than one partial specialization matches the template + // argument list": don't use UniquePtr! See the + // comment by this constructor in the non-T[] specialization above. + UniquePtr(Pointer aPtr, + typename RemoveReference::Type&& aD2) + : mTuple(aPtr, Move(aD2)) + { + static_assert(!IsReference::value, + "rvalue deleter can't be stored by reference"); + } + +private: + // Forbidden for the same reasons as stated above. + template + UniquePtr(U&& aU, V&& aV, + typename EnableIf::value && + IsConvertible::value, + int>::Type aDummy = 0) + MOZ_DELETE; + +public: + UniquePtr(UniquePtr&& aOther) + : mTuple(aOther.release(), Forward(aOther.getDeleter())) + {} + + template + UniquePtr(N, + typename EnableIf::value, int>::Type aDummy = 0) + : mTuple(static_cast(nullptr), DeleterType()) + { + static_assert(!IsPointer::value, "must provide a deleter instance"); + static_assert(!IsReference::value, "must provide a deleter instance"); + } + + ~UniquePtr() { reset(nullptr); } + + UniquePtr& operator=(UniquePtr&& aOther) + { + reset(aOther.release()); + getDeleter() = Forward(aOther.getDeleter()); + return *this; + } + + UniquePtr& operator=(NullptrT) + { + reset(); + return *this; + } + + T& operator[](decltype(sizeof(int)) aIndex) const { return get()[aIndex]; } + Pointer get() const { return mTuple.first(); } + + DeleterType& getDeleter() { return mTuple.second(); } + const DeleterType& getDeleter() const { return mTuple.second(); } + +private: + typedef void (UniquePtr::* ConvertibleToBool)(double, char); + void nonNull(double, char) {} + +public: + operator ConvertibleToBool() const + { + return get() != nullptr ? &UniquePtr::nonNull : nullptr; + } + + Pointer release() + { + Pointer p = mTuple.first(); + mTuple.first() = nullptr; + return p; + } + + void reset(Pointer aPtr = Pointer()) + { + Pointer old = mTuple.first(); + mTuple.first() = aPtr; + if (old != nullptr) { + mTuple.second()(old); } + } - /** - * Construct a UniquePtr containing |p|. - */ - explicit UniquePtr(Pointer p) - : tuple(p, DeleterType()) - { - static_assert(!IsPointer::value, "must provide a deleter instance"); - static_assert(!IsReference::value, "must provide a deleter instance"); - } +private: + // Kill off all remaining overloads that aren't true nullptr (the overload + // above should handle that) or emulated nullptr (which acts like int/long + // on gcc 4.4/4.5). + template + void reset(U, + typename EnableIf::value && + !IsSame::Type>::value, + int>::Type aDummy = 0) + MOZ_DELETE; - private: - // delete[] knows how to handle *only* an array of a single class type. For - // delete[] to work correctly, it must know the size of each element, the - // fields and base classes of each element requiring destruction, and so on. - // So forbid all overloads which would end up invoking delete[] on a pointer - // of the wrong type. - template - UniquePtr(U&& u, - typename EnableIf::value && - IsConvertible::value, - int>::Type dummy = 0) - MOZ_DELETE; +public: + void swap(UniquePtr& aOther) { mTuple.swap(aOther.mTuple); } - public: - UniquePtr(Pointer p, - typename Conditional::value, - D, - const D&>::Type d1) - : tuple(p, d1) - {} - - // If you encounter an error with MSVC10 about RemoveReference below, along - // the lines that "more than one partial specialization matches the template - // argument list": don't use UniquePtr! See the - // comment by this constructor in the non-T[] specialization above. - UniquePtr(Pointer p, - typename RemoveReference::Type&& d2) - : tuple(p, Move(d2)) - { - static_assert(!IsReference::value, - "rvalue deleter can't be stored by reference"); - } - - private: - // Forbidden for the same reasons as stated above. - template - UniquePtr(U&& u, V&& v, - typename EnableIf::value && - IsConvertible::value, - int>::Type dummy = 0) - MOZ_DELETE; - - public: - UniquePtr(UniquePtr&& other) - : tuple(other.release(), Forward(other.getDeleter())) - {} - - template - UniquePtr(N, - typename EnableIf::value, int>::Type dummy = 0) - : tuple(static_cast(nullptr), DeleterType()) - { - static_assert(!IsPointer::value, "must provide a deleter instance"); - static_assert(!IsReference::value, "must provide a deleter instance"); - } - - ~UniquePtr() { - reset(nullptr); - } - - UniquePtr& operator=(UniquePtr&& other) { - reset(other.release()); - getDeleter() = Forward(other.getDeleter()); - return *this; - } - - UniquePtr& operator=(NullptrT) { - reset(); - return *this; - } - - T& operator[](decltype(sizeof(int)) i) const { return get()[i]; } - Pointer get() const { return tuple.first(); } - - DeleterType& getDeleter() { return tuple.second(); } - const DeleterType& getDeleter() const { return tuple.second(); } - - private: - typedef void (UniquePtr::* ConvertibleToBool)(double, char); - void nonNull(double, char) {} - - public: - operator ConvertibleToBool() const { - return get() != nullptr ? &UniquePtr::nonNull : nullptr; - } - - Pointer release() { - Pointer p = tuple.first(); - tuple.first() = nullptr; - return p; - } - - void reset(Pointer p = Pointer()) { - Pointer old = tuple.first(); - tuple.first() = p; - if (old != nullptr) { - tuple.second()(old); - } - } - - private: - // Kill off all remaining overloads that aren't true nullptr (the overload - // above should handle that) or emulated nullptr (which acts like int/long - // on gcc 4.4/4.5). - template - void reset(U, - typename EnableIf::value && - !IsSame::Type>::value, - int>::Type dummy = 0) - MOZ_DELETE; - - public: - void swap(UniquePtr& other) { - tuple.swap(other.tuple); - } - - private: - UniquePtr(const UniquePtr& other) MOZ_DELETE; // construct using Move()! - void operator=(const UniquePtr& other) MOZ_DELETE; // assign using Move()! +private: + UniquePtr(const UniquePtr& aOther) MOZ_DELETE; // construct using Move()! + void operator=(const UniquePtr& aOther) MOZ_DELETE; // assign using Move()! }; /** A default deletion policy using plain old operator delete. */ template class DefaultDelete { - public: - MOZ_CONSTEXPR DefaultDelete() {} +public: + MOZ_CONSTEXPR DefaultDelete() {} - template - DefaultDelete(const DefaultDelete& other, - typename EnableIf::value, - int>::Type dummy = 0) - {} + template + DefaultDelete(const DefaultDelete& aOther, + typename EnableIf::value, + int>::Type aDummy = 0) + {} - void operator()(T* ptr) const { - static_assert(sizeof(T) > 0, "T must be complete"); - delete ptr; - } + void operator()(T* aPtr) const + { + static_assert(sizeof(T) > 0, "T must be complete"); + delete aPtr; + } }; /** A default deletion policy using operator delete[]. */ template class DefaultDelete { - public: - MOZ_CONSTEXPR DefaultDelete() {} +public: + MOZ_CONSTEXPR DefaultDelete() {} - void operator()(T* ptr) const { - static_assert(sizeof(T) > 0, "T must be complete"); - delete[] ptr; - } + void operator()(T* aPtr) const + { + static_assert(sizeof(T) > 0, "T must be complete"); + delete[] aPtr; + } - private: - template - void operator()(U* ptr) const MOZ_DELETE; +private: + template + void operator()(U* aPtr) const MOZ_DELETE; }; template void -Swap(UniquePtr& x, UniquePtr& y) +Swap(UniquePtr& aX, UniquePtr& aY) { - x.swap(y); + aX.swap(aY); } template bool -operator==(const UniquePtr& x, const UniquePtr& y) +operator==(const UniquePtr& aX, const UniquePtr& aY) { - return x.get() == y.get(); + return aX.get() == aY.get(); } template bool -operator!=(const UniquePtr& x, const UniquePtr& y) +operator!=(const UniquePtr& aX, const UniquePtr& aY) { - return x.get() != y.get(); + return aX.get() != aY.get(); } template bool -operator==(const UniquePtr& x, NullptrT n) +operator==(const UniquePtr& aX, NullptrT aNull) { - MOZ_ASSERT(n == nullptr); - return !x; + MOZ_ASSERT(aNull == nullptr); + return !aX; } template bool -operator==(NullptrT n, const UniquePtr& x) +operator==(NullptrT aNull, const UniquePtr& aX) { - MOZ_ASSERT(n == nullptr); - return !x; + MOZ_ASSERT(aNull == nullptr); + return !aX; } template bool -operator!=(const UniquePtr& x, NullptrT n) +operator!=(const UniquePtr& aX, NullptrT aNull) { - MOZ_ASSERT(n == nullptr); - return bool(x); + MOZ_ASSERT(aNull == nullptr); + return bool(aX); } template bool -operator!=(NullptrT n, const UniquePtr& x) +operator!=(NullptrT aNull, const UniquePtr& aX) { - MOZ_ASSERT(n == nullptr); - return bool(x); + MOZ_ASSERT(aNull == nullptr); + return bool(aX); } // No operator<, operator>, operator<=, operator>= for now because simplicity. @@ -580,19 +589,19 @@ namespace detail { template struct UniqueSelector { - typedef UniquePtr SingleObject; + typedef UniquePtr SingleObject; }; template struct UniqueSelector { - typedef UniquePtr UnknownBound; + typedef UniquePtr UnknownBound; }; template struct UniqueSelector { - typedef UniquePtr KnownBound; + typedef UniquePtr KnownBound; }; } // namespace detail @@ -669,46 +678,50 @@ MakeUnique() template typename detail::UniqueSelector::SingleObject -MakeUnique(A1&& a1) +MakeUnique(A1&& aA1) { - return UniquePtr(new T(Forward(a1))); + return UniquePtr(new T(Forward(aA1))); } template typename detail::UniqueSelector::SingleObject -MakeUnique(A1&& a1, A2&& a2) +MakeUnique(A1&& aA1, A2&& aA2) { - return UniquePtr(new T(Forward(a1), Forward(a2))); + return UniquePtr(new T(Forward(aA1), Forward(aA2))); } template typename detail::UniqueSelector::SingleObject -MakeUnique(A1&& a1, A2&& a2, A3&& a3) +MakeUnique(A1&& aA1, A2&& aA2, A3&& aA3) { - return UniquePtr(new T(Forward(a1), Forward(a2), Forward(a3))); + return UniquePtr(new T(Forward(aA1), Forward(aA2), + Forward(aA3))); } template typename detail::UniqueSelector::SingleObject -MakeUnique(A1&& a1, A2&& a2, A3&& a3, A4&& a4) +MakeUnique(A1&& aA1, A2&& aA2, A3&& aA3, A4&& aA4) { - return UniquePtr(new T(Forward(a1), Forward(a2), Forward(a3), - Forward(a4))); + return UniquePtr(new T(Forward(aA1), Forward(aA2), + Forward(aA3), Forward(aA4))); } -template +template typename detail::UniqueSelector::SingleObject -MakeUnique(A1&& a1, A2&& a2, A3&& a3, A4&& a4, A5&& a5) +MakeUnique(A1&& aA1, A2&& aA2, A3&& aA3, A4&& aA4, A5&& aA5) { - return UniquePtr(new T(Forward(a1), Forward(a2), Forward(a3), Forward(a4), Forward(a5))); + return UniquePtr(new T(Forward(aA1), Forward(aA2), + Forward(aA3), Forward(aA4), + Forward(aA5))); } template typename detail::UniqueSelector::UnknownBound -MakeUnique(decltype(sizeof(int)) n) +MakeUnique(decltype(sizeof(int)) aN) { typedef typename RemoveExtent::Type ArrayType; - return UniquePtr(new ArrayType[n]()); + return UniquePtr(new ArrayType[aN]()); } template @@ -717,23 +730,24 @@ MakeUnique() MOZ_DELETE; template typename detail::UniqueSelector::KnownBound -MakeUnique(A1&& a1) MOZ_DELETE; +MakeUnique(A1&& aA1) MOZ_DELETE; template typename detail::UniqueSelector::KnownBound -MakeUnique(A1&& a1, A2&& a2) MOZ_DELETE; +MakeUnique(A1&& aA1, A2&& aA2) MOZ_DELETE; template typename detail::UniqueSelector::KnownBound -MakeUnique(A1&& a1, A2&& a2, A3&& a3) MOZ_DELETE; +MakeUnique(A1&& aA1, A2&& aA2, A3&& aA3) MOZ_DELETE; template typename detail::UniqueSelector::KnownBound -MakeUnique(A1&& a1, A2&& a2, A3&& a3, A4&& a4) MOZ_DELETE; +MakeUnique(A1&& aA1, A2&& aA2, A3&& aA3, A4&& aA4) MOZ_DELETE; -template +template typename detail::UniqueSelector::KnownBound -MakeUnique(A1&& a1, A2&& a2, A3&& a3, A4&& a4, A5&& a5) MOZ_DELETE; +MakeUnique(A1&& aA1, A2&& aA2, A3&& aA3, A4&& aA4, A5&& aA5) MOZ_DELETE; } // namespace mozilla diff --git a/mfbt/tests/TestUniquePtr.cpp b/mfbt/tests/TestUniquePtr.cpp index ea070f3d6f6..d94de4fb369 100644 --- a/mfbt/tests/TestUniquePtr.cpp +++ b/mfbt/tests/TestUniquePtr.cpp @@ -34,24 +34,24 @@ using mozilla::Vector; typedef UniquePtr NewInt; static_assert(sizeof(NewInt) == sizeof(int*), "stored most efficiently"); -static size_t ADestructorCalls = 0; +static size_t gADestructorCalls = 0; struct A { public: A() : mX(0) {} - virtual ~A() { ADestructorCalls++; } + virtual ~A() { gADestructorCalls++; } int mX; }; -static size_t BDestructorCalls = 0; +static size_t gBDestructorCalls = 0; struct B : public A { public: B() : mY(1) {} - ~B() { BDestructorCalls++; } + ~B() { gBDestructorCalls++; } int mY; }; @@ -131,30 +131,30 @@ TestDefaultFreeGuts() UniqueA a1; CHECK(a1 == nullptr); a1.reset(new A); - CHECK(ADestructorCalls == 0); + CHECK(gADestructorCalls == 0); CHECK(a1->mX == 0); B* bp1 = new B; bp1->mX = 5; - CHECK(BDestructorCalls == 0); + CHECK(gBDestructorCalls == 0); a1.reset(bp1); - CHECK(ADestructorCalls == 1); + CHECK(gADestructorCalls == 1); CHECK(a1->mX == 5); a1.reset(nullptr); - CHECK(ADestructorCalls == 2); - CHECK(BDestructorCalls == 1); + CHECK(gADestructorCalls == 2); + CHECK(gBDestructorCalls == 1); B* bp2 = new B; UniqueB b1(bp2); UniqueA a2(nullptr); a2 = Move(b1); - CHECK(ADestructorCalls == 2); - CHECK(BDestructorCalls == 1); + CHECK(gADestructorCalls == 2); + CHECK(gBDestructorCalls == 1); UniqueA a3(Move(a2)); a3 = nullptr; - CHECK(ADestructorCalls == 3); - CHECK(BDestructorCalls == 2); + CHECK(gADestructorCalls == 3); + CHECK(gBDestructorCalls == 2); B* bp3 = new B; bp3->mX = 42; @@ -162,39 +162,39 @@ TestDefaultFreeGuts() UniqueA a4(Move(b2)); CHECK(b2.get() == nullptr); CHECK((*a4).mX == 42); - CHECK(ADestructorCalls == 3); - CHECK(BDestructorCalls == 2); + CHECK(gADestructorCalls == 3); + CHECK(gBDestructorCalls == 2); UniqueA a5(new A); UniqueB b3(new B); a5 = Move(b3); - CHECK(ADestructorCalls == 4); - CHECK(BDestructorCalls == 2); + CHECK(gADestructorCalls == 4); + CHECK(gBDestructorCalls == 2); ReturnUniqueA(); - CHECK(ADestructorCalls == 5); - CHECK(BDestructorCalls == 3); + CHECK(gADestructorCalls == 5); + CHECK(gBDestructorCalls == 3); ReturnLocalA(); - CHECK(ADestructorCalls == 6); - CHECK(BDestructorCalls == 3); + CHECK(gADestructorCalls == 6); + CHECK(gBDestructorCalls == 3); UniqueA a6(ReturnLocalA()); a6 = nullptr; - CHECK(ADestructorCalls == 7); - CHECK(BDestructorCalls == 3); + CHECK(gADestructorCalls == 7); + CHECK(gBDestructorCalls == 3); UniqueC c1(new B); UniqueA a7(new B); a7 = Move(c1); - CHECK(ADestructorCalls == 8); - CHECK(BDestructorCalls == 4); + CHECK(gADestructorCalls == 8); + CHECK(gBDestructorCalls == 4); c1.reset(new B); UniqueA a8(Move(c1)); - CHECK(ADestructorCalls == 8); - CHECK(BDestructorCalls == 4); + CHECK(gADestructorCalls == 8); + CHECK(gBDestructorCalls == 4); // These smart pointers still own B resources. CHECK(a4); @@ -208,8 +208,8 @@ static bool TestDefaultFree() { CHECK(TestDefaultFreeGuts()); - CHECK(ADestructorCalls == 12); - CHECK(BDestructorCalls == 8); + CHECK(gADestructorCalls == 12); + CHECK(gBDestructorCalls == 8); return true; } @@ -317,16 +317,16 @@ TestReferenceDeleterGuts() static bool TestReferenceDeleter() { - ADestructorCalls = 0; - BDestructorCalls = 0; + gADestructorCalls = 0; + gBDestructorCalls = 0; CHECK(TestReferenceDeleterGuts()); - CHECK(ADestructorCalls == 4); - CHECK(BDestructorCalls == 3); + CHECK(gADestructorCalls == 4); + CHECK(gBDestructorCalls == 3); - ADestructorCalls = 0; - BDestructorCalls = 0; + gADestructorCalls = 0; + gBDestructorCalls = 0; return true; } @@ -409,7 +409,9 @@ template struct AppendNullptrTwice { AppendNullptrTwice() {} - bool operator()(Vector& vec) { + + bool operator()(Vector& vec) + { CHECK(vec.append(static_cast(nullptr))); CHECK(vec.append(static_cast(nullptr))); return true; @@ -420,7 +422,9 @@ template struct AppendNullptrTwice { AppendNullptrTwice() {} - bool operator()(Vector& vec) { + + bool operator()(Vector& vec) + { CHECK(vec.append(nullptr)); CHECK(vec.append(nullptr)); return true; @@ -453,21 +457,21 @@ TestVectorGuts() BAfter = numAppended / 2; AAfter = numAppended - numAppended / 2; - CHECK(ADestructorCalls == 0); - CHECK(BDestructorCalls == 0); + CHECK(gADestructorCalls == 0); + CHECK(gBDestructorCalls == 0); return true; } static bool TestVector() { - ADestructorCalls = 0; - BDestructorCalls = 0; + gADestructorCalls = 0; + gBDestructorCalls = 0; CHECK(TestVectorGuts()); - CHECK(ADestructorCalls == 3 + AAfter + BAfter); - CHECK(BDestructorCalls == 2 + BAfter); + CHECK(gADestructorCalls == 3 + AAfter + BAfter); + CHECK(gBDestructorCalls == 2 + BAfter); return true; } diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index b8ea711bd1a..bf873b87cfa 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -2235,21 +2235,6 @@ pref("font.minimum-size.th", 0); pref("font.size.variable.th", 16); pref("font.size.fixed.th", 13); -pref("font.default.tr", "serif"); -pref("font.minimum-size.tr", 0); -pref("font.size.variable.tr", 16); -pref("font.size.fixed.tr", 13); - -pref("font.default.x-baltic", "serif"); -pref("font.minimum-size.x-baltic", 0); -pref("font.size.variable.x-baltic", 16); -pref("font.size.fixed.x-baltic", 13); - -pref("font.default.x-central-euro", "serif"); -pref("font.minimum-size.x-central-euro", 0); -pref("font.size.variable.x-central-euro", 16); -pref("font.size.fixed.x-central-euro", 13); - pref("font.default.x-cyrillic", "serif"); pref("font.minimum-size.x-cyrillic", 0); pref("font.size.variable.x-cyrillic", 16); @@ -2531,21 +2516,6 @@ pref("font.name.sans-serif.th", "Tahoma"); pref("font.name.monospace.th", "Tahoma"); pref("font.name.cursive.th", "Tahoma"); -pref("font.name.serif.tr", "Times New Roman"); -pref("font.name.sans-serif.tr", "Arial"); -pref("font.name.monospace.tr", "Courier New"); -pref("font.name.cursive.tr", "Comic Sans MS"); - -pref("font.name.serif.x-baltic", "Times New Roman"); -pref("font.name.sans-serif.x-baltic", "Arial"); -pref("font.name.monospace.x-baltic", "Courier New"); -pref("font.name.cursive.x-baltic", "Comic Sans MS"); - -pref("font.name.serif.x-central-euro", "Times New Roman"); -pref("font.name.sans-serif.x-central-euro", "Arial"); -pref("font.name.monospace.x-central-euro", "Courier New"); -pref("font.name.cursive.x-central-euro", "Comic Sans MS"); - pref("font.name.serif.x-cyrillic", "Times New Roman"); pref("font.name.sans-serif.x-cyrillic", "Arial"); pref("font.name.monospace.x-cyrillic", "Courier New"); @@ -2902,17 +2872,6 @@ pref("font.name-list.serif.th", "Thonburi"); pref("font.name-list.sans-serif.th", "Thonburi"); pref("font.name-list.monospace.th", "Ayuthaya"); -pref("font.name.serif.tr", "Times"); -pref("font.name.sans-serif.tr", "Helvetica"); -pref("font.name.monospace.tr", "Courier"); -pref("font.name.cursive.tr", "Apple Chancery"); -pref("font.name.fantasy.tr", "Papyrus"); -pref("font.name-list.serif.tr", "Times"); -pref("font.name-list.sans-serif.tr", "Helvetica"); -pref("font.name-list.monospace.tr", "Courier"); -pref("font.name-list.cursive.tr", "Apple Chancery"); -pref("font.name-list.fantasy.tr", "Papyrus"); - pref("font.name.serif.x-armn", "Mshtakan"); pref("font.name.sans-serif.x-armn", "Mshtakan"); pref("font.name.monospace.x-armn", "Mshtakan"); @@ -2920,17 +2879,6 @@ pref("font.name-list.serif.x-armn", "Mshtakan"); pref("font.name-list.sans-serif.x-armn", "Mshtakan"); pref("font.name-list.monospace.x-armn", "Mshtakan"); -pref("font.name.serif.x-baltic", "Times"); -pref("font.name.sans-serif.x-baltic", "Helvetica"); -pref("font.name.monospace.x-baltic", "Courier"); -pref("font.name.cursive.x-baltic", "Apple Chancery"); -pref("font.name.fantasy.x-baltic", "Papyrus"); -pref("font.name-list.serif.x-baltic", "Times"); -pref("font.name-list.sans-serif.x-baltic", "Helvetica"); -pref("font.name-list.monospace.x-baltic", "Courier"); -pref("font.name-list.cursive.x-baltic", "Apple Chancery"); -pref("font.name-list.fantasy.x-baltic", "Papyrus"); - // SolaimanLipi, Rupali http://ekushey.org/?page/mac_download pref("font.name.serif.x-beng", "Bangla MN"); pref("font.name.sans-serif.x-beng", "Bangla Sangam MN"); @@ -2946,17 +2894,6 @@ pref("font.name-list.serif.x-cans", "Euphemia UCAS"); pref("font.name-list.sans-serif.x-cans", "Euphemia UCAS"); pref("font.name-list.monospace.x-cans", "Euphemia UCAS"); -pref("font.name.serif.x-central-euro", "Times"); -pref("font.name.sans-serif.x-central-euro", "Helvetica"); -pref("font.name.monospace.x-central-euro", "Courier"); -pref("font.name.cursive.x-central-euro", "Apple Chancery"); -pref("font.name.fantasy.x-central-euro", "Papyrus"); -pref("font.name-list.serif.x-central-euro", "Times"); -pref("font.name-list.sans-serif.x-central-euro", "Helvetica"); -pref("font.name-list.monospace.x-central-euro", "Courier"); -pref("font.name-list.cursive.x-central-euro", "Apple Chancery"); -pref("font.name-list.fantasy.x-central-euro", "Papyrus"); - pref("font.name.serif.x-cyrillic", "Times"); pref("font.name.sans-serif.x-cyrillic", "Helvetica"); pref("font.name.monospace.x-cyrillic", "Monaco"); @@ -3265,18 +3202,6 @@ pref("font.name.sans-serif.th", "Fira Sans"); pref("font.name.monospace.th", "Fira Mono"); pref("font.name-list.sans-serif.th", "Fira Sans, Noto Sans Thai, Droid Sans Thai"); -pref("font.name.serif.tr", "Charis SIL Compact"); -pref("font.name.sans-serif.tr", "Fira Sans"); -pref("font.name.monospace.tr", "Fira Mono"); - -pref("font.name.serif.x-baltic", "Charis SIL Compact"); -pref("font.name.sans-serif.x-baltic", "Fira Sans"); -pref("font.name.monospace.x-baltic", "Fira Mono"); - -pref("font.name.serif.x-central-euro", "Charis SIL Compact"); -pref("font.name.sans-serif.x-central-euro", "Fira Sans"); -pref("font.name.monospace.x-central-euro", "Fira Mono"); - pref("font.name.serif.x-cyrillic", "Charis SIL Compact"); pref("font.name.sans-serif.x-cyrillic", "Fira Sans"); pref("font.name.monospace.x-cyrillic", "Fira Mono"); @@ -3337,24 +3262,6 @@ pref("font.name.monospace.th", "Droid Sans Mono"); pref("font.name-list.serif.th", "Droid Serif"); pref("font.name-list.sans-serif.th", "Droid Sans Thai, Clear Sans, Droid Sans"); -pref("font.name.serif.tr", "Charis SIL Compact"); -pref("font.name.sans-serif.tr", "Clear Sans"); -pref("font.name.monospace.tr", "Droid Sans Mono"); -pref("font.name-list.serif.tr", "Droid Serif"); -pref("font.name-list.sans-serif.tr", "Clear Sans, Roboto, Droid Sans"); - -pref("font.name.serif.x-baltic", "Charis SIL Compact"); -pref("font.name.sans-serif.x-baltic", "Clear Sans"); -pref("font.name.monospace.x-baltic", "Droid Sans Mono"); -pref("font.name-list.serif.x-baltic", "Droid Serif"); -pref("font.name-list.sans-serif.x-baltic", "Clear Sans, Roboto, Droid Sans"); - -pref("font.name.serif.x-central-euro", "Charis SIL Compact"); -pref("font.name.sans-serif.x-central-euro", "Clear Sans"); -pref("font.name.monospace.x-central-euro", "Droid Sans Mono"); -pref("font.name-list.serif.x-central-euro", "Droid Serif"); -pref("font.name-list.sans-serif.x-central-euro", "Clear Sans, Roboto, Droid Sans"); - pref("font.name.serif.x-cyrillic", "Charis SIL Compact"); pref("font.name.sans-serif.x-cyrillic", "Clear Sans"); pref("font.name.monospace.x-cyrillic", "Droid Sans Mono"); @@ -3407,15 +3314,6 @@ pref("font.size.fixed.he", 12); pref("font.minimum-size.th", 13); -pref("font.default.tr", "sans-serif"); -pref("font.size.fixed.tr", 12); - -pref("font.default.x-baltic", "sans-serif"); -pref("font.size.fixed.x-baltic", 12); - -pref("font.default.x-central-euro", "sans-serif"); -pref("font.size.fixed.x-central-euro", 12); - pref("font.default.x-cyrillic", "sans-serif"); pref("font.size.fixed.x-cyrillic", 12); @@ -3524,21 +3422,6 @@ pref("font.name.sans-serif.th", "sans-serif"); pref("font.minimum-size.th", 13); pref("font.name.monospace.th", "monospace"); -pref("font.name.serif.tr", "serif"); -pref("font.name.sans-serif.tr", "sans-serif"); -pref("font.name.monospace.tr", "monospace"); -pref("font.size.fixed.tr", 12); - -pref("font.name.serif.x-baltic", "serif"); -pref("font.name.sans-serif.x-baltic", "sans-serif"); -pref("font.name.monospace.x-baltic", "monospace"); -pref("font.size.fixed.x-baltic", 12); - -pref("font.name.serif.x-central-euro", "serif"); -pref("font.name.sans-serif.x-central-euro", "sans-serif"); -pref("font.name.monospace.x-central-euro", "monospace"); -pref("font.size.fixed.x-central-euro", 12); - pref("font.name.serif.x-cyrillic", "serif"); pref("font.name.sans-serif.x-cyrillic", "sans-serif"); pref("font.name.monospace.x-cyrillic", "monospace"); diff --git a/mozglue/linker/CustomElf.cpp b/mozglue/linker/CustomElf.cpp index 29208d47e82..1f73303055b 100644 --- a/mozglue/linker/CustomElf.cpp +++ b/mozglue/linker/CustomElf.cpp @@ -353,11 +353,6 @@ CustomElf::GetSymbolPtrInDeps(const char *symbol) const if (strcmp(symbol + 2, "gnu_Unwind_Find_exidx") == 0) return FunctionPtr(__wrap___gnu_Unwind_Find_exidx); #endif - } else if (symbol[0] == 's' && symbol[1] == 'i') { - if (strcmp(symbol + 2, "gnal") == 0) - return FunctionPtr(SEGVHandler::__wrap_signal); - if (strcmp(symbol + 2, "gaction") == 0) - return FunctionPtr(SEGVHandler::__wrap_sigaction); } #define MISSING_FLASH_SYMNAME_START "_ZN7android10VectorImpl19reservedVectorImpl" diff --git a/mozglue/linker/ElfLoader.cpp b/mozglue/linker/ElfLoader.cpp index 617b07ad538..28b4eb9bf7c 100644 --- a/mozglue/linker/ElfLoader.cpp +++ b/mozglue/linker/ElfLoader.cpp @@ -1123,30 +1123,4 @@ SEGVHandler::__wrap_sigaction(int signum, const struct sigaction *act, return 0; } -sighandler_t -SEGVHandler::__wrap_signal(int signum, sighandler_t handler) -{ - /* Use system signal() function for all but SIGSEGV signals. */ - if (signum != SIGSEGV) - return signal(signum, handler); - - SEGVHandler &that = ElfLoader::Singleton; - union { - sighandler_t signal; - void (*sigaction)(int, siginfo_t *, void *); - } oldHandler; - - /* Keep the previous handler to return its value */ - if (that.action.sa_flags & SA_SIGINFO) { - oldHandler.sigaction = that.action.sa_sigaction; - } else { - oldHandler.signal = that.action.sa_handler; - } - /* Set the new handler */ - that.action.sa_handler = handler; - that.action.sa_flags = 0; - - return oldHandler.signal; -} - Logging Logging::Singleton; diff --git a/mozglue/linker/ElfLoader.h b/mozglue/linker/ElfLoader.h index be644c112d7..0c59974e527 100644 --- a/mozglue/linker/ElfLoader.h +++ b/mozglue/linker/ElfLoader.h @@ -319,16 +319,14 @@ public: return signalHandlingBroken; } - static int __wrap_sigaction(int signum, const struct sigaction *act, - struct sigaction *oldact); - - static sighandler_t __wrap_signal(int signum, sighandler_t handler); - protected: SEGVHandler(); ~SEGVHandler(); private: + static int __wrap_sigaction(int signum, const struct sigaction *act, + struct sigaction *oldact); + /** * SIGSEGV handler registered with __wrap_signal or __wrap_sigaction. */ diff --git a/netwerk/protocol/http/ASpdySession.cpp b/netwerk/protocol/http/ASpdySession.cpp index f317d67cb7d..ba3b373f715 100644 --- a/netwerk/protocol/http/ASpdySession.cpp +++ b/netwerk/protocol/http/ASpdySession.cpp @@ -67,6 +67,8 @@ ASpdySession::NewSpdySession(uint32_t version, SpdyInformation::SpdyInformation() { + // highest index of enabled protocols is the + // most preferred for ALPN negotiaton Version[0] = SPDY_VERSION_3; VersionString[0] = NS_LITERAL_CSTRING("spdy/3"); diff --git a/netwerk/protocol/http/Http2Session.cpp b/netwerk/protocol/http/Http2Session.cpp index 644c1781c36..ef0887cbbc0 100644 --- a/netwerk/protocol/http/Http2Session.cpp +++ b/netwerk/protocol/http/Http2Session.cpp @@ -2947,6 +2947,14 @@ Http2Session::ConfirmTLSProfile() RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY); } + int16_t macAlgorithm = ssl->GetMACAlgorithmUsed(); + LOG3(("Http2Session::ConfirmTLSProfile %p MAC Algortihm (aead==6) %d\n", + this, macAlgorithm)); + if (macAlgorithm != nsISSLSocketControl::SSL_MAC_AEAD) { + LOG3(("Http2Session::ConfirmTLSProfile %p FAILED due to lack of AEAD\n", this)); + RETURN_SESSION_ERROR(this, INADEQUATE_SECURITY); + } + /* We are required to send SNI. We do that already, so no check is done * here to make sure we did. */ diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp index 89b82fbbe69..b4b6ba701ab 100644 --- a/netwerk/protocol/http/nsHttpConnection.cpp +++ b/netwerk/protocol/http/nsHttpConnection.cpp @@ -482,17 +482,20 @@ nsHttpConnection::SetupNPNList(nsISSLSocketControl *ssl, uint32_t caps) // The first protocol is used as the fallback if none of the // protocols supported overlap with the server's list. - // In the case of overlap, matching priority is driven by - // the order of the server's advertisement. + // When using ALPN the advertised preferences are protocolArray indicies + // {1, .., N, 0} in decreasing order. + // For NPN, In the case of overlap, matching priority is driven by + // the order of the server's advertisement - with index 0 used when + // there is no match. protocolArray.AppendElement(NS_LITERAL_CSTRING("http/1.1")); if (gHttpHandler->IsSpdyEnabled() && !(caps & NS_HTTP_DISALLOW_SPDY)) { LOG(("nsHttpConnection::SetupSSL Allow SPDY NPN selection")); - for (uint32_t index = 0; index < SpdyInformation::kCount; ++index) { - if (gHttpHandler->SpdyInfo()->ProtocolEnabled(index)) + for (uint32_t index = SpdyInformation::kCount; index > 0; --index) { + if (gHttpHandler->SpdyInfo()->ProtocolEnabled(index - 1)) protocolArray.AppendElement( - gHttpHandler->SpdyInfo()->VersionString[index]); + gHttpHandler->SpdyInfo()->VersionString[index - 1]); } } diff --git a/netwerk/socket/nsISSLSocketControl.idl b/netwerk/socket/nsISSLSocketControl.idl index c6dd9551476..0703bbe4765 100644 --- a/netwerk/socket/nsISSLSocketControl.idl +++ b/netwerk/socket/nsISSLSocketControl.idl @@ -14,7 +14,7 @@ class nsCString; %} [ref] native nsCStringTArrayRef(nsTArray); -[scriptable, builtinclass, uuid(ec72446c-8241-457f-ba75-83d214392289)] +[scriptable, builtinclass, uuid(2032ad83-229f-4ddb-818a-59b9ae4ecd4b)] interface nsISSLSocketControl : nsISupports { attribute nsIInterfaceRequestor notificationCallbacks; @@ -82,5 +82,17 @@ interface nsISSLSocketControl : nsISupports { const short SSL_VERSION_UNKNOWN = -1; [infallible] readonly attribute short SSLVersionUsed; + + /* These values match the NSS defined values in sslt.h */ + const short SSL_MAC_UNKNOWN = -1; + const short SSL_MAC_NULL = 0; + const short SSL_MAC_MD5 = 1; + const short SSL_MAC_SHA = 2; + const short SSL_HMAC_MD5 = 3; + const short SSL_HMAC_SHA = 4; + const short SSL_HMAC_SHA256 = 5; + const short SSL_MAC_AEAD = 6; + + [infallible] readonly attribute short MACAlgorithmUsed; }; diff --git a/parser/html/javasrc/TreeBuilder.java b/parser/html/javasrc/TreeBuilder.java index b3cc39c9d30..7acced0d1c8 100644 --- a/parser/html/javasrc/TreeBuilder.java +++ b/parser/html/javasrc/TreeBuilder.java @@ -2416,7 +2416,7 @@ public abstract class TreeBuilder implements TokenHandler, generateImpliedEndTags(); } if (eltPos != currentPtr) { - if (eltPos != NOT_FOUND_ON_STACK) { + if (eltPos == NOT_FOUND_ON_STACK) { errStartTagSeenWithoutRuby(name); } else { errUnclosedChildrenInRuby(); @@ -2434,7 +2434,7 @@ public abstract class TreeBuilder implements TokenHandler, } if (eltPos != currentPtr) { if (!isCurrent("rtc")) { - if (eltPos != NOT_FOUND_ON_STACK) { + if (eltPos == NOT_FOUND_ON_STACK) { errStartTagSeenWithoutRuby(name); } else { errUnclosedChildrenInRuby(); diff --git a/parser/html/nsHtml5TreeBuilder.cpp b/parser/html/nsHtml5TreeBuilder.cpp index 3d71aab39db..22c82b5031b 100644 --- a/parser/html/nsHtml5TreeBuilder.cpp +++ b/parser/html/nsHtml5TreeBuilder.cpp @@ -1328,7 +1328,7 @@ nsHtml5TreeBuilder::startTag(nsHtml5ElementName* elementName, nsHtml5HtmlAttribu generateImpliedEndTags(); } if (eltPos != currentPtr) { - if (eltPos != NS_HTML5TREE_BUILDER_NOT_FOUND_ON_STACK) { + if (eltPos == NS_HTML5TREE_BUILDER_NOT_FOUND_ON_STACK) { errStartTagSeenWithoutRuby(name); } else { errUnclosedChildrenInRuby(); @@ -1345,7 +1345,7 @@ nsHtml5TreeBuilder::startTag(nsHtml5ElementName* elementName, nsHtml5HtmlAttribu } if (eltPos != currentPtr) { if (!isCurrent(nsHtml5Atoms::rtc)) { - if (eltPos != NS_HTML5TREE_BUILDER_NOT_FOUND_ON_STACK) { + if (eltPos == NS_HTML5TREE_BUILDER_NOT_FOUND_ON_STACK) { errStartTagSeenWithoutRuby(name); } else { errUnclosedChildrenInRuby(); diff --git a/rdf/build/nsRDFModule.cpp b/rdf/build/nsRDFModule.cpp index ec0fcff27db..d1af94bc04f 100644 --- a/rdf/build/nsRDFModule.cpp +++ b/rdf/build/nsRDFModule.cpp @@ -146,34 +146,14 @@ static const mozilla::Module::ContractIDEntry kRDFContracts[] = { { nullptr } }; -static nsresult -StartupRDFModule() -{ - if (RDFServiceImpl::gRDFService) { - NS_ERROR("Leaked the RDF service from a previous startup."); - RDFServiceImpl::gRDFService = nullptr; - } - - return NS_OK; -} - -static void -ShutdownRDFModule() -{ - if (RDFServiceImpl::gRDFService) { - // XXX make this an assertion! - NS_WARNING("Leaking the RDF Service."); - } -} - static const mozilla::Module kRDFModule = { mozilla::Module::kVersion, kRDFCIDs, kRDFContracts, nullptr, nullptr, - StartupRDFModule, - ShutdownRDFModule + nullptr, + nullptr }; NSMODULE_DEFN(nsRDFModule) = &kRDFModule; diff --git a/security/certverifier/moz.build b/security/certverifier/moz.build index 9629f559cfb..0e38c928d3f 100644 --- a/security/certverifier/moz.build +++ b/security/certverifier/moz.build @@ -43,6 +43,7 @@ if CONFIG['_MSC_VER']: # class copy constructor is inaccessible or deleted '-wd4626', # assignment operator could not be generated because a base # class assignment operator is inaccessible or deleted + '-wd4640', # construction of local static object is not thread-safe '-wd4710', # 'function': function not inlined '-wd4711', # function 'function' selected for inline expansion '-wd4820', # 'bytes' bytes padding added after construct 'member_name' diff --git a/security/manager/ssl/src/nsNSSCallbacks.cpp b/security/manager/ssl/src/nsNSSCallbacks.cpp index 2e4a9898242..0fe3ea745e6 100644 --- a/security/manager/ssl/src/nsNSSCallbacks.cpp +++ b/security/manager/ssl/src/nsNSSCallbacks.cpp @@ -898,6 +898,7 @@ PreliminaryHandshakeDone(PRFileDesc* fd) status->mCipherName.Assign(cipherInfo.cipherSuiteName); infoObject->SetKEAUsed(cipherInfo.keaType); infoObject->SetKEAKeyBits(channelInfo.keaKeyBits); + infoObject->SetMACAlgorithmUsed(cipherInfo.macAlgorithm); } } diff --git a/security/manager/ssl/src/nsNSSIOLayer.cpp b/security/manager/ssl/src/nsNSSIOLayer.cpp index e193e217ee1..3be405f47f4 100644 --- a/security/manager/ssl/src/nsNSSIOLayer.cpp +++ b/security/manager/ssl/src/nsNSSIOLayer.cpp @@ -137,6 +137,7 @@ nsNSSSocketInfo::nsNSSSocketInfo(SharedSSLState& aState, uint32_t providerFlags) mKEAExpected(nsISSLSocketControl::KEY_EXCHANGE_UNKNOWN), mKEAKeyBits(0), mSSLVersionUsed(nsISSLSocketControl::SSL_VERSION_UNKNOWN), + mMACAlgorithmUsed(nsISSLSocketControl::SSL_MAC_UNKNOWN), mProviderFlags(providerFlags), mSocketCreationTimestamp(TimeStamp::Now()), mPlaintextBytesRead(0) @@ -195,6 +196,13 @@ nsNSSSocketInfo::GetSSLVersionUsed(int16_t* aSSLVersionUsed) return NS_OK; } +NS_IMETHODIMP +nsNSSSocketInfo::GetMACAlgorithmUsed(int16_t* aMac) +{ + *aMac = mMACAlgorithmUsed; + return NS_OK; +} + NS_IMETHODIMP nsNSSSocketInfo::GetRememberClientAuthCertificate(bool* aRemember) { diff --git a/security/manager/ssl/src/nsNSSIOLayer.h b/security/manager/ssl/src/nsNSSIOLayer.h index 050a5ac81ef..6c5661e1649 100644 --- a/security/manager/ssl/src/nsNSSIOLayer.h +++ b/security/manager/ssl/src/nsNSSIOLayer.h @@ -111,6 +111,8 @@ public: mSSLVersionUsed = version; } + void SetMACAlgorithmUsed(int16_t mac) { mMACAlgorithmUsed = mac; } + protected: virtual ~nsNSSSocketInfo(); @@ -144,6 +146,7 @@ private: int16_t mKEAExpected; uint32_t mKEAKeyBits; int16_t mSSLVersionUsed; + int16_t mMACAlgorithmUsed; uint32_t mProviderFlags; mozilla::TimeStamp mSocketCreationTimestamp; diff --git a/security/sandbox/linux/Sandbox.cpp b/security/sandbox/linux/Sandbox.cpp index 654d81c00bc..095d6a150ef 100644 --- a/security/sandbox/linux/Sandbox.cpp +++ b/security/sandbox/linux/Sandbox.cpp @@ -19,13 +19,14 @@ #include #include #include +#include #include "mozilla/Atomics.h" #include "mozilla/NullPtr.h" #include "mozilla/unused.h" #include "mozilla/dom/Exceptions.h" -#include "nsString.h" #include "nsThreadUtils.h" +#include "prenv.h" #ifdef MOZ_CRASHREPORTER #include "nsExceptionHandler.h" @@ -36,16 +37,8 @@ #include #endif -#if defined(MOZ_CONTENT_SANDBOX) #include "linux_seccomp.h" #include "SandboxFilter.h" -#endif - -#ifdef MOZ_LOGGING -#define FORCE_PR_LOG 1 -#endif -#include "prlog.h" -#include "prenv.h" // See definition of SandboxDie, below. #include "sandbox/linux/seccomp-bpf/die.h" @@ -53,11 +46,16 @@ namespace mozilla { #if defined(ANDROID) #define LOG_ERROR(args...) __android_log_print(ANDROID_LOG_ERROR, "Sandbox", ## args) -#elif defined(PR_LOGGING) -static PRLogModuleInfo* gSeccompSandboxLog; -#define LOG_ERROR(args...) PR_LOG(mozilla::gSeccompSandboxLog, PR_LOG_ERROR, (args)) #else -#define LOG_ERROR(args...) +#define LOG_ERROR(fmt, args...) fprintf(stderr, "Sandbox: " fmt, ## args) +#endif + +#ifdef MOZ_GMP_SANDBOX +// For media plugins, we can start the sandbox before we dlopen the +// module, so we have to pre-open the file and simulate the sandboxed +// open(). +static int gMediaPluginFileDesc = -1; +static const char *gMediaPluginFilePath; #endif /** @@ -137,6 +135,27 @@ Reporter(int nr, siginfo_t *info, void *void_context) args[4] = SECCOMP_PARM5(ctx); args[5] = SECCOMP_PARM6(ctx); +#ifdef MOZ_GMP_SANDBOX + if (syscall_nr == __NR_open && gMediaPluginFilePath) { + const char *path = reinterpret_cast(args[0]); + int flags = int(args[1]); + + if ((flags & O_ACCMODE) != O_RDONLY) { + LOG_ERROR("non-read-only open of file %s attempted (flags=0%o)", + path, flags); + } else if (strcmp(path, gMediaPluginFilePath) != 0) { + LOG_ERROR("attempt to open file %s which is not the media plugin %s", + path, gMediaPluginFilePath); + } else if (gMediaPluginFileDesc == -1) { + LOG_ERROR("multiple opens of media plugin file unimplemented"); + } else { + SECCOMP_RESULT(ctx) = gMediaPluginFileDesc; + gMediaPluginFileDesc = -1; + return; + } + } +#endif + LOG_ERROR("seccomp sandbox violation: pid %d, syscall %lu, args %lu %lu %lu" " %lu %lu %lu. Killing process.", pid, syscall_nr, args[0], args[1], args[2], args[3], args[4], args[5]); @@ -254,8 +273,7 @@ SetThreadSandbox() { bool didAnything = false; - if (PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX") == nullptr && - prctl(PR_GET_SECCOMP, 0, 0, 0, 0) == 0) { + if (prctl(PR_GET_SECCOMP, 0, 0, 0, 0) == 0) { if (InstallSyscallFilter(sSetSandboxFilter) == 0) { didAnything = true; } @@ -285,19 +303,19 @@ SetThreadSandboxHandler(int signum) } static void -BroadcastSetThreadSandbox() +BroadcastSetThreadSandbox(SandboxType aType) { int signum; - pid_t pid, tid; + pid_t pid, tid, myTid; DIR *taskdp; struct dirent *de; - SandboxFilter filter(&sSetSandboxFilter, - PR_GetEnv("MOZ_CONTENT_SANDBOX_VERBOSE")); + SandboxFilter filter(&sSetSandboxFilter, aType, + PR_GetEnv("MOZ_SANDBOX_VERBOSE")); static_assert(sizeof(mozilla::Atomic) == sizeof(int), "mozilla::Atomic isn't represented by an int"); - MOZ_ASSERT(NS_IsMainThread()); pid = getpid(); + myTid = syscall(__NR_gettid); taskdp = opendir("/proc/self/task"); if (taskdp == nullptr) { LOG_ERROR("opendir /proc/self/task: %s\n", strerror(errno)); @@ -330,9 +348,9 @@ BroadcastSetThreadSandbox() // Not a task ID. continue; } - if (tid == pid) { - // Drop the main thread's privileges last, below, so - // we can continue to signal other threads. + if (tid == myTid) { + // Drop this thread's privileges last, below, so we can + // continue to signal other threads. continue; } // Reset the futex cell and signal. @@ -425,31 +443,69 @@ IsSandboxingSupported(void) return prctl(PR_GET_SECCOMP) != -1; } -/** - * Starts the seccomp sandbox for this process and sets user/group-based privileges. - * Should be called only once, and before any potentially harmful content is loaded. - * - * Should normally make the process exit on failure. -*/ -void -SetCurrentProcessSandbox() +// Common code for sandbox startup. +static void +SetCurrentProcessSandbox(SandboxType aType) { -#if !defined(ANDROID) && defined(PR_LOGGING) - if (!gSeccompSandboxLog) { - gSeccompSandboxLog = PR_NewLogModule("SeccompSandbox"); - } - PR_ASSERT(gSeccompSandboxLog); -#endif - if (InstallSyscallReporter()) { LOG_ERROR("install_syscall_reporter() failed\n"); } if (IsSandboxingSupported()) { - BroadcastSetThreadSandbox(); + BroadcastSetThreadSandbox(aType); } } +#ifdef MOZ_CONTENT_SANDBOX +/** + * Starts the seccomp sandbox for a content process. Should be called + * only once, and before any potentially harmful content is loaded. + * + * Will normally make the process exit on failure. +*/ +void +SetContentProcessSandbox() +{ + if (PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) { + return; + } + + SetCurrentProcessSandbox(kSandboxContentProcess); +} +#endif // MOZ_CONTENT_SANDBOX + +#ifdef MOZ_GMP_SANDBOX +/** + * Starts the seccomp sandbox for a media plugin process. Should be + * called only once, and before any potentially harmful content is + * loaded -- including the plugin itself, if it's considered untrusted. + * + * The file indicated by aFilePath, if non-null, can be open()ed once + * read-only after the sandbox starts; it should be the .so file + * implementing the not-yet-loaded plugin. + * + * Will normally make the process exit on failure. +*/ +void +SetMediaPluginSandbox(const char *aFilePath) +{ + if (PR_GetEnv("MOZ_DISABLE_GMP_SANDBOX")) { + return; + } + + if (aFilePath) { + gMediaPluginFilePath = strdup(aFilePath); + gMediaPluginFileDesc = open(aFilePath, O_RDONLY | O_CLOEXEC); + if (gMediaPluginFileDesc == -1) { + LOG_ERROR("failed to open plugin file %s: %s", aFilePath, strerror(errno)); + MOZ_CRASH(); + } + } + // Finally, start the sandbox. + SetCurrentProcessSandbox(kSandboxMediaPlugin); +} +#endif // MOZ_GMP_SANDBOX + } // namespace mozilla diff --git a/security/sandbox/linux/Sandbox.h b/security/sandbox/linux/Sandbox.h index c9e2be7133f..d8a3891ceb1 100644 --- a/security/sandbox/linux/Sandbox.h +++ b/security/sandbox/linux/Sandbox.h @@ -9,7 +9,12 @@ namespace mozilla { -void SetCurrentProcessSandbox(); +#ifdef MOZ_CONTENT_SANDBOX +void SetContentProcessSandbox(); +#endif +#ifdef MOZ_GMP_SANDBOX +void SetMediaPluginSandbox(const char *aFilePath); +#endif } // namespace mozilla diff --git a/security/sandbox/linux/SandboxFilter.cpp b/security/sandbox/linux/SandboxFilter.cpp index 694485f55fb..51b640fa154 100644 --- a/security/sandbox/linux/SandboxFilter.cpp +++ b/security/sandbox/linux/SandboxFilter.cpp @@ -14,49 +14,26 @@ #include "mozilla/NullPtr.h" #include -#include -#include #include +#include +#include +#include +#include +#include +#include namespace mozilla { class SandboxFilterImpl : public SandboxAssembler { - void Build(); public: - SandboxFilterImpl() { - Build(); - Finish(); - } + virtual void Build() = 0; + virtual ~SandboxFilterImpl() { } }; -SandboxFilter::SandboxFilter(const sock_fprog** aStored, bool aVerbose) - : mStored(aStored) -{ - MOZ_ASSERT(*mStored == nullptr); - std::vector filterVec; - { - SandboxFilterImpl impl; - impl.Compile(&filterVec, aVerbose); - } - mProg = new sock_fprog; - mProg->len = filterVec.size(); - mProg->filter = mFilter = new sock_filter[mProg->len]; - for (size_t i = 0; i < mProg->len; ++i) { - mFilter[i] = filterVec[i]; - } - *mStored = mProg; -} +// Some helper macros to make the code that builds the filter more +// readable, and to help deal with differences among architectures. -SandboxFilter::~SandboxFilter() -{ - *mStored = nullptr; - delete[] mFilter; - delete mProg; -} - -void -SandboxFilterImpl::Build() { #define SYSCALL_EXISTS(name) (defined(__NR_##name)) #define SYSCALL(name) (Condition(__NR_##name)) @@ -69,32 +46,40 @@ SandboxFilterImpl::Build() { Condition(__NR_##name, arg, argValues); \ }) - // Some architectures went through a transition from 32-bit to - // 64-bit off_t and had to version all the syscalls that referenced - // it; others (newer and/or 64-bit ones) didn't. Adjust the - // conditional as needed. +// Some architectures went through a transition from 32-bit to +// 64-bit off_t and had to version all the syscalls that referenced +// it; others (newer and/or 64-bit ones) didn't. Adjust the +// conditional as needed. #if SYSCALL_EXISTS(stat64) #define SYSCALL_LARGEFILE(plain, versioned) SYSCALL(versioned) #else #define SYSCALL_LARGEFILE(plain, versioned) SYSCALL(plain) #endif - // i386 multiplexes all the socket-related interfaces into a single - // syscall. +// i386 multiplexes all the socket-related interfaces into a single +// syscall. #if SYSCALL_EXISTS(socketcall) #define SOCKETCALL(name, NAME) SYSCALL_WITH_ARG(socketcall, 0, SYS_##NAME) #else #define SOCKETCALL(name, NAME) SYSCALL(name) #endif - // i386 multiplexes all the SysV-IPC-related interfaces into a single - // syscall. +// i386 multiplexes all the SysV-IPC-related interfaces into a single +// syscall. #if SYSCALL_EXISTS(ipc) #define SYSVIPCCALL(name, NAME) SYSCALL_WITH_ARG(ipc, 0, NAME) #else #define SYSVIPCCALL(name, NAME) SYSCALL(name) #endif +#ifdef MOZ_CONTENT_SANDBOX +class SandboxFilterImplContent : public SandboxFilterImpl { +protected: + virtual void Build() MOZ_OVERRIDE; +}; + +void +SandboxFilterImplContent::Build() { /* Most used system calls should be at the top of the whitelist * for performance reasons. The whitelist BPF filter exits after * processing any ALLOW_SYSCALL macro. @@ -102,7 +87,7 @@ SandboxFilterImpl::Build() { * How are those syscalls found? * 1) via strace -p or/and * 2) the child will report which system call has been denied by seccomp-bpf, - * just before exiting, via NSPR or Android logging. + * just before exiting * System call number to name mapping is found in: * bionic/libc/kernel/arch-arm/asm/unistd.h * or your libc's unistd.h/kernel headers. @@ -206,6 +191,11 @@ SandboxFilterImpl::Build() { Allow(SYSCALL(fsync)); Allow(SYSCALL(msync)); +#if defined(ANDROID) && !defined(MOZ_MEMORY) + // Android's libc's realloc uses mremap. + Allow(SYSCALL(mremap)); +#endif + /* Should remove all of the following in the future, if possible */ Allow(SYSCALL(getpriority)); Allow(SYSCALL(sched_get_priority_min)); @@ -329,5 +319,159 @@ SandboxFilterImpl::Build() { Allow(SYSCALL(exit_group)); Allow(SYSCALL(exit)); } +#endif // MOZ_CONTENT_SANDBOX + +#ifdef MOZ_GMP_SANDBOX +class SandboxFilterImplGMP : public SandboxFilterImpl { +protected: + virtual void Build() MOZ_OVERRIDE; +}; + +void SandboxFilterImplGMP::Build() { + // As for content processes, check the most common syscalls first. + + Allow(SYSCALL_WITH_ARG(clock_gettime, 0, CLOCK_MONOTONIC, CLOCK_REALTIME)); + Allow(SYSCALL(futex)); + Allow(SYSCALL(gettimeofday)); + Allow(SYSCALL(poll)); + Allow(SYSCALL(write)); + Allow(SYSCALL(read)); + Allow(SYSCALL(epoll_wait)); + Allow(SOCKETCALL(recvmsg, RECVMSG)); + Allow(SOCKETCALL(sendmsg, SENDMSG)); + Allow(SYSCALL(time)); + + // Nothing after this line is performance-critical. + +#if SYSCALL_EXISTS(mmap2) + Allow(SYSCALL(mmap2)); +#else + Allow(SYSCALL(mmap)); +#endif + Allow(SYSCALL_LARGEFILE(fstat, fstat64)); + Allow(SYSCALL(munmap)); + + Allow(SYSCALL(getpid)); + Allow(SYSCALL(gettid)); + + // The glibc source hasn't changed the thread creation clone flags + // since 2004, so this *should* be safe to hard-code. Bionic is + // different, but MOZ_GMP_SANDBOX isn't supported there yet. + // + // At minimum we should require CLONE_THREAD, so that a single + // SIGKILL from the parent will destroy all descendant tasks. In + // general, pinning down as much of the flags word as possible is a + // good idea, because it exposes a lot of subtle (and probably not + // well tested in all cases) kernel functionality. + // + // WARNING: s390 and cris pass the flags in a different arg -- see + // CLONE_BACKWARDS2 in arch/Kconfig in the kernel source -- but we + // don't support seccomp-bpf on those archs yet. + static const int new_thread_flags = CLONE_VM | CLONE_FS | CLONE_FILES | + CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS | + CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID; + Allow(SYSCALL_WITH_ARG(clone, 0, new_thread_flags)); + + Allow(SYSCALL_WITH_ARG(prctl, 0, PR_GET_SECCOMP, PR_SET_NAME)); + +#if SYSCALL_EXISTS(set_robust_list) + Allow(SYSCALL(set_robust_list)); +#endif + + // NSPR can call this when creating a thread, but it will accept a + // polite "no". + Deny(EACCES, SYSCALL(getpriority)); + + // Stack bounds are obtained via pthread_getattr_np, which calls + // this but doesn't actually need it: + Deny(ENOSYS, SYSCALL(sched_getaffinity)); + +#ifdef MOZ_ASAN + Allow(SYSCALL(sigaltstack)); +#endif + + Allow(SYSCALL(mprotect)); + Allow(SYSCALL_WITH_ARG(madvise, 2, MADV_DONTNEED)); + +#if SYSCALL_EXISTS(sigreturn) + Allow(SYSCALL(sigreturn)); +#endif + Allow(SYSCALL(rt_sigreturn)); + + Allow(SYSCALL(restart_syscall)); + Allow(SYSCALL(close)); + + // "Sleeping for 300 seconds" in debug crashes; possibly other uses. + Allow(SYSCALL(nanosleep)); + + // For the crash reporter: +#if SYSCALL_EXISTS(sigprocmask) + Allow(SYSCALL(sigprocmask)); +#endif + Allow(SYSCALL(rt_sigprocmask)); +#if SYSCALL_EXISTS(sigaction) + Allow(SYSCALL(sigaction)); +#endif + Allow(SYSCALL(rt_sigaction)); + Allow(SOCKETCALL(socketpair, SOCKETPAIR)); + Allow(SYSCALL_WITH_ARG(tgkill, 0, uint32_t(getpid()))); + Allow(SYSCALL_WITH_ARG(prctl, 0, PR_SET_DUMPABLE)); + + // Note for when GMP is supported on an ARM platform: Add whichever + // of the ARM-specific syscalls are needed for this type of process. + + Allow(SYSCALL(epoll_ctl)); + Allow(SYSCALL(exit)); + Allow(SYSCALL(exit_group)); +} +#endif // MOZ_GMP_SANDBOX + +SandboxFilter::SandboxFilter(const sock_fprog** aStored, SandboxType aType, + bool aVerbose) + : mStored(aStored) +{ + MOZ_ASSERT(*mStored == nullptr); + std::vector filterVec; + SandboxFilterImpl *impl; + + switch (aType) { + case kSandboxContentProcess: +#ifdef MOZ_CONTENT_SANDBOX + impl = new SandboxFilterImplContent(); +#else + MOZ_CRASH("Content process sandboxing not supported in this build!"); +#endif + break; + case kSandboxMediaPlugin: +#ifdef MOZ_GMP_SANDBOX + impl = new SandboxFilterImplGMP(); +#else + MOZ_CRASH("Gecko Media Plugin process sandboxing not supported in this" + " build!"); +#endif + break; + default: + MOZ_CRASH("Nonexistent sandbox type!"); + } + impl->Build(); + impl->Finish(); + impl->Compile(&filterVec, aVerbose); + delete impl; + + mProg = new sock_fprog; + mProg->len = filterVec.size(); + mProg->filter = mFilter = new sock_filter[mProg->len]; + for (size_t i = 0; i < mProg->len; ++i) { + mFilter[i] = filterVec[i]; + } + *mStored = mProg; +} + +SandboxFilter::~SandboxFilter() +{ + *mStored = nullptr; + delete[] mFilter; + delete mProg; +} } diff --git a/security/sandbox/linux/SandboxFilter.h b/security/sandbox/linux/SandboxFilter.h index a00dea2cf4b..861fd12bb1d 100644 --- a/security/sandbox/linux/SandboxFilter.h +++ b/security/sandbox/linux/SandboxFilter.h @@ -11,17 +11,25 @@ struct sock_fprog; struct sock_filter; namespace mozilla { - class SandboxFilter { - sock_filter *mFilter; - sock_fprog *mProg; - const sock_fprog **mStored; - public: + +enum SandboxType { + kSandboxContentProcess, + kSandboxMediaPlugin +}; + +class SandboxFilter { + sock_filter *mFilter; + sock_fprog *mProg; + const sock_fprog **mStored; +public: // RAII: on construction, builds the filter and stores it in the // provided variable (with optional logging); on destruction, frees // the filter and nulls out the pointer. - SandboxFilter(const sock_fprog** aStored, bool aVerbose = false); - ~SandboxFilter(); - }; -} + SandboxFilter(const sock_fprog** aStored, SandboxType aBox, + bool aVerbose = false); + ~SandboxFilter(); +}; + +} // namespace mozilla #endif diff --git a/testing/crashtest/crashtests.list b/testing/crashtest/crashtests.list index f834ef2fbbb..90c58028558 100644 --- a/testing/crashtest/crashtests.list +++ b/testing/crashtest/crashtests.list @@ -6,8 +6,8 @@ include ../../testing/crashtest/sanity/crashtests.list include ../../accessible/tests/crashtests/crashtests.list include ../../content/base/crashtests/crashtests.list -include ../../content/html/document/crashtests/crashtests.list include ../../content/html/content/crashtests/crashtests.list +include ../../content/html/document/crashtests/crashtests.list include ../../content/svg/content/src/crashtests/crashtests.list include ../../content/xul/content/crashtests/crashtests.list include ../../content/xul/document/crashtests/crashtests.list @@ -24,7 +24,10 @@ include ../../dom/canvas/crashtests/crashtests.list include ../../dom/events/crashtests/crashtests.list include ../../dom/indexedDB/crashtests/crashtests.list include ../../dom/mathml/crashtests/crashtests.list +include ../../dom/plugins/test/crashtests/crashtests.list include ../../dom/smil/crashtests/crashtests.list +include ../../dom/src/jsurl/crashtests/crashtests.list +include ../../dom/src/offline/crashtests/crashtests.list include ../../dom/xbl/crashtests/crashtests.list include ../../dom/xml/crashtests/crashtests.list include ../../dom/xslt/crashtests/crashtests.list @@ -32,11 +35,12 @@ include ../../dom/xslt/crashtests/crashtests.list # Bug 811873 - mozRTCPeerConnection doesn't support remote browser yet skip-if(B2G||browserIsRemote||!webrtc) include ../../dom/media/tests/crashtests/crashtests.list -include ../../dom/src/offline/crashtests/crashtests.list -include ../../dom/src/jsurl/crashtests/crashtests.list - include ../../editor/crashtests.list +include ../../gfx/tests/crashtests/crashtests.list + +include ../../image/test/crashtests/crashtests.list + include ../../intl/lwbrk/crashtests/crashtests.list include ../../intl/uconv/crashtests/crashtests.list @@ -45,8 +49,8 @@ include ../../js/xpconnect/crashtests/crashtests.list include ../../layout/base/crashtests/crashtests.list include ../../layout/forms/crashtests/crashtests.list include ../../layout/generic/crashtests/crashtests.list -include ../../layout/printing/crashtests/crashtests.list include ../../layout/mathml/crashtests/crashtests.list +include ../../layout/printing/crashtests/crashtests.list include ../../layout/style/crashtests/crashtests.list include ../../layout/svg/crashtests/crashtests.list include ../../layout/tables/crashtests/crashtests.list @@ -54,12 +58,10 @@ include ../../layout/xul/crashtests/crashtests.list include ../../layout/xul/grid/crashtests/crashtests.list include ../../layout/xul/tree/crashtests/crashtests.list -include ../../gfx/tests/crashtests/crashtests.list - -include ../../image/test/crashtests/crashtests.list -include ../../dom/plugins/test/crashtests/crashtests.list +include ../../media/libpng/crashtests/crashtests.list include ../../netwerk/test/crashtests/crashtests.list + include ../../parser/htmlparser/tests/crashtests/crashtests.list include ../../security/manager/ssl/crashtests/crashtests.list @@ -67,9 +69,7 @@ include ../../security/manager/ssl/crashtests/crashtests.list include ../../view/crashtests/crashtests.list include ../../widget/cocoa/crashtests/crashtests.list -include ../../widget/gtk/crashtests/crashtests.list include ../../widget/crashtests/crashtests.list - -include ../../media/libpng/crashtests/crashtests.list +include ../../widget/gtk/crashtests/crashtests.list include ../../xpcom/string/crashtests/crashtests.list diff --git a/testing/mozbase/mozlog/mozlog/structured/logtypes.py b/testing/mozbase/mozlog/mozlog/structured/logtypes.py index f28c3ef088f..56dc8a109a7 100644 --- a/testing/mozbase/mozlog/mozlog/structured/logtypes.py +++ b/testing/mozbase/mozlog/mozlog/structured/logtypes.py @@ -53,20 +53,27 @@ class log_action(object): values = {} values.update(kwargs) - j = -1 - for i, arg in enumerate(args): - j += 1 - # Skip over non-defaulted args supplied as keyword arguments - while self.args_no_default[j] in values: - j += 1 - if j == len(self.args_no_default): - raise TypeError("Too many arguments") - values[self.args_no_default[j]] = arg + positional_no_default = [item for item in self.args_no_default if item not in values] - for k in range(j+1, len(self.args_no_default)): - if self.args_no_default[k] not in values: - raise TypeError("Missing required argument %s\n%r\n%r\n%r" % - (self.args_no_default[k], args, kwargs, values)) + num_no_default = len(positional_no_default) + + if len(args) < num_no_default: + raise TypeError("Too few arguments") + + if len(args) > num_no_default + len(self.args_with_default): + raise TypeError("Too many arguments") + + for i, name in enumerate(positional_no_default): + values[name] = args[i] + + positional_with_default = [self.args_with_default[i] + for i in range(len(args) - num_no_default)] + + + for i, name in enumerate(positional_with_default): + if name in values: + raise TypeError("Argument %s specified twice" % name) + values[name] = args[i + num_no_default] # Fill in missing arguments for name in self.args_with_default: @@ -74,9 +81,12 @@ class log_action(object): values[name] = self.args[name].default for key, value in values.iteritems(): - out_value = self.args[key](value) - if out_value is not missing: - data[key] = out_value + if key in self.args: + out_value = self.args[key](value) + if out_value is not missing: + data[key] = out_value + else: + raise TypeError("Unrecognised argument %s" % key) return data diff --git a/testing/mozbase/mozlog/mozlog/structured/structuredlog.py b/testing/mozbase/mozlog/mozlog/structured/structuredlog.py index 364fc318fdd..0fe556c802e 100644 --- a/testing/mozbase/mozlog/mozlog/structured/structuredlog.py +++ b/testing/mozbase/mozlog/mozlog/structured/structuredlog.py @@ -144,7 +144,8 @@ class StructuredLogger(object): if action in ("test_status", "test_end"): if (data["expected"] == data["status"] or - data["status"] == "SKIP"): + data["status"] == "SKIP" or + "expected" not in raw_data): del data["expected"] self._handle_log(data) diff --git a/testing/mozbase/mozlog/tests/test_structured.py b/testing/mozbase/mozlog/tests/test_structured.py index fb589436846..b3db38d006f 100644 --- a/testing/mozbase/mozlog/tests/test_structured.py +++ b/testing/mozbase/mozlog/tests/test_structured.py @@ -363,6 +363,35 @@ class TestTypeconversions(BaseStructuredTest): self.assertEquals(data[-1], "☺\n") self.logger.suite_end() + def test_arguments(self): + self.logger.info(message="test") + self.assert_log_equals({"action": "log", + "message": "test", + "level": "INFO"}) + + self.logger.suite_start([], {}) + self.assert_log_equals({"action": "suite_start", + "tests": [], + "run_info": {}}) + self.logger.test_start(test="test1") + self.logger.test_status("subtest1", "FAIL", test="test1", status="PASS") + self.assert_log_equals({"action": "test_status", + "test": "test1", + "subtest": "subtest1", + "status": "PASS", + "expected": "FAIL"}) + self.logger.process_output(123, "data", "test") + self.assert_log_equals({"action": "process_output", + "process": "123", + "command": "test", + "data": "data"}) + self.assertRaises(TypeError, self.logger.test_status, subtest="subtest2", + status="FAIL", expected="PASS") + self.assertRaises(TypeError, self.logger.test_status, "test1", "subtest1", + "PASS", "FAIL", "message", "stack", {}, "unexpected") + self.assertRaises(TypeError, self.logger.test_status, "test1", test="test2") + self.logger.suite_end() + class TestCommandline(unittest.TestCase): def test_setup_logging(self): parser = argparse.ArgumentParser() diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild index 20b9d686514..28e52f77acb 100644 --- a/toolkit/toolkit.mozbuild +++ b/toolkit/toolkit.mozbuild @@ -6,7 +6,7 @@ if CONFIG['LIBXUL_SDK']: error('toolkit.mozbuild is not compatible with --enable-libxul-sdk=') -if CONFIG['MOZ_CONTENT_SANDBOX'] or (CONFIG['OS_ARCH'] == 'WINNT'): +if CONFIG['MOZ_CONTENT_SANDBOX'] or CONFIG['MOZ_GMP_SANDBOX']: add_tier_dir('sandbox', 'security/sandbox') # Depends on NSS and NSPR, and must be built after sandbox or else B2G emulator diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index 1c5308b390d..00ebea78f15 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -883,6 +883,34 @@ public: }; NS_IMPL_ISUPPORTS(AtomTablesReporter, nsIMemoryReporter) +#ifdef DEBUG + +// Ideally, this would be implemented in BlockingResourceBase.cpp. +// However, this ends up breaking the linking step of various unit tests due +// to adding a new dependency to libdmd for a commonly used feature (mutexes) +// in DMD builds. So instead we do it here. +class DeadlockDetectorReporter MOZ_FINAL : public nsIMemoryReporter +{ + MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf) + + ~DeadlockDetectorReporter() {} + +public: + NS_DECL_ISUPPORTS + + NS_METHOD CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) + { + return MOZ_COLLECT_REPORT( + "explicit/deadlock-detector", KIND_HEAP, UNITS_BYTES, + BlockingResourceBase::SizeOfDeadlockDetector(MallocSizeOf), + "Memory used by the deadlock detector."); + } +}; +NS_IMPL_ISUPPORTS(DeadlockDetectorReporter, nsIMemoryReporter) + +#endif + #ifdef MOZ_DMD namespace mozilla { @@ -986,6 +1014,10 @@ nsMemoryReporterManager::Init() RegisterStrongReporter(new AtomTablesReporter()); +#ifdef DEBUG + RegisterStrongReporter(new DeadlockDetectorReporter()); +#endif + #ifdef MOZ_DMD RegisterStrongReporter(new mozilla::dmd::DMDReporter()); #endif diff --git a/xpcom/glue/BlockingResourceBase.cpp b/xpcom/glue/BlockingResourceBase.cpp index 401f2d06115..97645347854 100644 --- a/xpcom/glue/BlockingResourceBase.cpp +++ b/xpcom/glue/BlockingResourceBase.cpp @@ -90,6 +90,7 @@ BlockingResourceBase::~BlockingResourceBase() // base class, or its underlying primitive, will check for such // stupid mistakes. mChainPrev = 0; // racy only for stupidly buggy client code + sDeadlockDetector->Remove(mDDEntry); mDDEntry = 0; // owned by deadlock detector } diff --git a/xpcom/glue/BlockingResourceBase.h b/xpcom/glue/BlockingResourceBase.h index 79d12c6797f..a275ecfbd4d 100644 --- a/xpcom/glue/BlockingResourceBase.h +++ b/xpcom/glue/BlockingResourceBase.h @@ -52,6 +52,13 @@ public: #ifdef DEBUG + static size_t + SizeOfDeadlockDetector(MallocSizeOf aMallocSizeOf) + { + return sDeadlockDetector ? + sDeadlockDetector->SizeOfIncludingThis(aMallocSizeOf) : 0; + } + private: // forward declaration for the following typedef struct DeadlockDetectorEntry; @@ -79,6 +86,16 @@ private: NS_ABORT_IF_FALSE(mName, "Name must be nonnull"); } + size_t + SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const + { + // NB: |mName| is not reported as it's expected to be a static string. + // If we switch to a nsString it should be added to the tally. + // |mAcquisitionContext| has no measurable heap allocations in it. + size_t n = aMallocSizeOf(this); + return n; + } + /** * Print * Write a description of this blocking resource to |aOut|. If diff --git a/xpcom/glue/DeadlockDetector.h b/xpcom/glue/DeadlockDetector.h index d03cd0c6df8..ecb346c469d 100644 --- a/xpcom/glue/DeadlockDetector.h +++ b/xpcom/glue/DeadlockDetector.h @@ -10,9 +10,9 @@ #include -#include "plhash.h" #include "prlock.h" +#include "nsClassHashtable.h" #include "nsTArray.h" #ifdef NS_TRACE_MALLOC @@ -177,10 +177,11 @@ public: typedef nsTArray ResourceAcquisitionArray; private: - typedef nsTArray HashEntryArray; + struct OrderingEntry; + typedef nsTArray HashEntryArray; typedef typename HashEntryArray::index_type index_type; typedef typename HashEntryArray::size_type size_type; - static const HashEntryArray::index_type NoIndex = HashEntryArray::NoIndex; + static const index_type NoIndex = HashEntryArray::NoIndex; /** * Value type for the ordering table. Contains the other @@ -191,122 +192,33 @@ private: */ struct OrderingEntry { - OrderingEntry() + OrderingEntry(const T* aResource) : mFirstSeen(CallStack::kNone) , mOrderedLT() // FIXME bug 456272: set to empirical dep size? + , mExternalRefs() + , mResource(aResource) { } ~OrderingEntry() { } + size_t + SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const + { + size_t n = aMallocSizeOf(this); + n += mOrderedLT.SizeOfExcludingThis(aMallocSizeOf); + n += mExternalRefs.SizeOfExcludingThis(aMallocSizeOf); + n += mResource->SizeOfIncludingThis(aMallocSizeOf); + return n; + } + CallStack mFirstSeen; // first site from which the resource appeared HashEntryArray mOrderedLT; // this <_o Other + HashEntryArray mExternalRefs; // hash entries that reference this + const T* mResource; }; - static void* TableAlloc(void* /*aPool*/, size_t aSize) - { - return operator new(aSize); - } - static void TableFree(void* /*aPool*/, void* aItem) - { - operator delete(aItem); - } - static PLHashEntry* EntryAlloc(void* /*aPool*/, const void* aKey) - { - return new PLHashEntry; - } - static void EntryFree(void* /*aPool*/, PLHashEntry* aEntry, unsigned aFlag) - { - delete static_cast(const_cast(aEntry->key)); - delete static_cast(aEntry->value); - aEntry->value = 0; - if (aFlag == HT_FREE_ENTRY) { - delete aEntry; - } - } - static PLHashNumber HashKey(const void* aKey) - { - return static_cast(NS_PTR_TO_INT32(aKey) >> 2); - } - static const PLHashAllocOps kAllocOps; - - // Hash table "interface" the rest of the code should use - - PLHashEntry** GetEntry(const T* aKey) - { - return PL_HashTableRawLookup(mOrdering, HashKey(aKey), aKey); - } - - void PutEntry(T* aKey) - { - PL_HashTableAdd(mOrdering, aKey, new OrderingEntry()); - } - - // XXX need these helper methods because OrderingEntry doesn't have - // XXX access to underlying PLHashEntry - - /** - * Add the order |aFirst <_o aSecond|. - * - * WARNING: this does not check whether it's sane to add this - * order. In the "best" bad case, when this order already exists, - * adding it anyway may unnecessarily result in O(n^2) space. In - * the "worst" bad case, adding it anyway will cause - * |InTransitiveClosure()| to diverge. - */ - void AddOrder(PLHashEntry* aLT, PLHashEntry* aGT) - { - static_cast(aLT->value)->mOrderedLT - .InsertElementSorted(aGT); - } - - /** - * Return true iff the order |aFirst < aSecond| has been - * *explicitly* added. - * - * Does not consider transitivity. - */ - bool IsOrdered(const PLHashEntry* aFirst, const PLHashEntry* aSecond) - const - { - const OrderingEntry* entry = - static_cast(aFirst->value); - return entry->mOrderedLT.BinaryIndexOf(aSecond) != NoIndex; - } - - /** - * Return a pointer to the array of all elements "that" for - * which the order |this < that| has been explicitly added. - * - * NOTE: this does *not* consider transitive orderings. - */ - PLHashEntry* const* GetOrders(const PLHashEntry* aEntry) const - { - return - static_cast(aEntry->value)->mOrderedLT.Elements(); - } - - /** - * Return the number of elements "that" for which the order - * |this < that| has been explicitly added. - * - * NOTE: this does *not* consider transitive orderings. - */ - size_type NumOrders(const PLHashEntry* aEntry) const - { - return - static_cast(aEntry->value)->mOrderedLT.Length(); - } - - /** Make a ResourceAcquisition out of |aEntry|. */ - ResourceAcquisition MakeResourceAcquisition(const PLHashEntry* aEntry) const - { - return ResourceAcquisition( - static_cast(aEntry->key), - static_cast(aEntry->value)->mFirstSeen); - } - // Throwaway RAII lock to make the following code safer. struct PRAutoLock { @@ -326,15 +238,8 @@ public: * that will be checked. */ explicit DeadlockDetector(uint32_t aNumResourcesGuess = kDefaultNumBuckets) + : mOrdering(aNumResourcesGuess) { - mOrdering = PL_NewHashTable(aNumResourcesGuess, - HashKey, - PL_CompareValues, PL_CompareValues, - &kAllocOps, 0); - if (!mOrdering) { - NS_RUNTIMEABORT("couldn't initialize resource ordering table"); - } - mLock = PR_NewLock(); if (!mLock) { NS_RUNTIMEABORT("couldn't allocate deadlock detector lock"); @@ -348,10 +253,31 @@ public: */ ~DeadlockDetector() { - PL_HashTableDestroy(mOrdering); PR_DestroyLock(mLock); } + static size_t + SizeOfEntryExcludingThis(const T* aKey, const nsAutoPtr& aEntry, + MallocSizeOf aMallocSizeOf, void* aUserArg) + { + // NB: Key is accounted for in the entry. + size_t n = aEntry->SizeOfIncludingThis(aMallocSizeOf); + return n; + } + + size_t + SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const + { + size_t n = aMallocSizeOf(this); + + { + PRAutoLock _(mLock); + n += mOrdering.SizeOfExcludingThis(SizeOfEntryExcludingThis, aMallocSizeOf); + } + + return n; + } + /** * Add * Make the deadlock detector aware of |aResource|. @@ -365,15 +291,31 @@ public: void Add(T* aResource) { PRAutoLock _(mLock); - PutEntry(aResource); + mOrdering.Put(aResource, new OrderingEntry(aResource)); } - // Nb: implementing a Remove() method makes the detector "more - // unsound." By removing a resource from the orderings, deadlocks - // may be missed that would otherwise have been found. However, - // removing resources possibly reduces the # of false positives, - // and additionally saves space. So it's a trade off; we have - // chosen to err on the side of caution and not implement Remove(). + void Remove(T* aResource) + { + PRAutoLock _(mLock); + + OrderingEntry* entry = mOrdering.Get(aResource); + + // Iterate the external refs and remove the entry from them. + HashEntryArray& refs = entry->mExternalRefs; + for (index_type i = 0; i < refs.Length(); i++) { + refs[i]->mOrderedLT.RemoveElementSorted(entry); + } + + // Iterate orders and remove this entry from their refs. + HashEntryArray& orders = entry->mOrderedLT; + for (index_type i = 0; i < orders.Length(); i++) { + orders[i]->mExternalRefs.RemoveElementSorted(entry); + } + + // Now the entry can be safely removed. + mOrdering.Remove(aResource); + delete aResource; + } /** * CheckAcquisition This method is called after acquiring |aLast|, @@ -404,10 +346,11 @@ public: NS_ASSERTION(aProposed, "null resource"); PRAutoLock _(mLock); - PLHashEntry* second = *GetEntry(aProposed); - OrderingEntry* e = static_cast(second->value); - if (CallStack::kNone == e->mFirstSeen) { - e->mFirstSeen = aCallContext; + OrderingEntry* proposed = mOrdering.Get(aProposed); + NS_ASSERTION(proposed, "missing ordering entry"); + + if (CallStack::kNone == proposed->mFirstSeen) { + proposed->mFirstSeen = aCallContext; } if (!aLast) { @@ -415,33 +358,35 @@ public: return 0; } - PLHashEntry* first = *GetEntry(aLast); + OrderingEntry* current = mOrdering.Get(aLast); + NS_ASSERTION(current, "missing ordering entry"); // this is the crux of the deadlock detector algorithm - if (first == second) { + if (current == proposed) { // reflexive deadlock. fastpath b/c InTransitiveClosure is // not applicable here. ResourceAcquisitionArray* cycle = new ResourceAcquisitionArray(); if (!cycle) { NS_RUNTIMEABORT("can't allocate dep. cycle array"); } - cycle->AppendElement(MakeResourceAcquisition(first)); + cycle->AppendElement(ResourceAcquisition(current->mResource, + current->mFirstSeen)); cycle->AppendElement(ResourceAcquisition(aProposed, aCallContext)); return cycle; } - if (InTransitiveClosure(first, second)) { + if (InTransitiveClosure(current, proposed)) { // we've already established |aLast < aProposed|. all is well. return 0; } - if (InTransitiveClosure(second, first)) { + if (InTransitiveClosure(proposed, current)) { // the order |aProposed < aLast| has been deduced, perhaps // transitively. we're attempting to violate that // constraint by acquiring resources in the order // |aLast < aProposed|, and thus we may deadlock under the // right conditions. - ResourceAcquisitionArray* cycle = GetDeductionChain(second, first); + ResourceAcquisitionArray* cycle = GetDeductionChain(proposed, current); // show how acquiring |aProposed| would complete the cycle cycle->AppendElement(ResourceAcquisition(aProposed, aCallContext)); @@ -450,7 +395,8 @@ public: // |aLast|, |aProposed| are unordered according to our // poset. this is fine, but we now need to add this // ordering constraint. - AddOrder(first, second); + current->mOrderedLT.InsertElementSorted(proposed); + proposed->mExternalRefs.InsertElementSorted(current); return 0; } @@ -460,16 +406,19 @@ public: * * @precondition |aStart != aTarget| */ - bool InTransitiveClosure(const PLHashEntry* aStart, - const PLHashEntry* aTarget) const + bool InTransitiveClosure(const OrderingEntry* aStart, + const OrderingEntry* aTarget) const { - if (IsOrdered(aStart, aTarget)) { + // NB: Using a static comparator rather than default constructing one shows + // a 9% improvement in scalability tests on some systems. + static nsDefaultComparator comp; + if (aStart->mOrderedLT.BinaryIndexOf(aTarget, comp) != NoIndex) { return true; } index_type i = 0; - size_type len = NumOrders(aStart); - for (const PLHashEntry* const* it = GetOrders(aStart); i < len; ++i, ++it) { + size_type len = aStart->mOrderedLT.Length(); + for (const OrderingEntry* const* it = aStart->mOrderedLT.Elements(); i < len; ++i, ++it) { if (InTransitiveClosure(*it, aTarget)) { return true; } @@ -493,14 +442,15 @@ public: * * @precondition |aStart != aTarget| */ - ResourceAcquisitionArray* GetDeductionChain(const PLHashEntry* aStart, - const PLHashEntry* aTarget) + ResourceAcquisitionArray* GetDeductionChain(const OrderingEntry* aStart, + const OrderingEntry* aTarget) { ResourceAcquisitionArray* chain = new ResourceAcquisitionArray(); if (!chain) { NS_RUNTIMEABORT("can't allocate dep. cycle array"); } - chain->AppendElement(MakeResourceAcquisition(aStart)); + chain->AppendElement(ResourceAcquisition(aStart->mResource, + aStart->mFirstSeen)); NS_ASSERTION(GetDeductionChain_Helper(aStart, aTarget, chain), "GetDeductionChain called when there's no deadlock"); @@ -509,19 +459,21 @@ public: // precondition: |aStart != aTarget| // invariant: |aStart| is the last element in |aChain| - bool GetDeductionChain_Helper(const PLHashEntry* aStart, - const PLHashEntry* aTarget, + bool GetDeductionChain_Helper(const OrderingEntry* aStart, + const OrderingEntry* aTarget, ResourceAcquisitionArray* aChain) { - if (IsOrdered(aStart, aTarget)) { - aChain->AppendElement(MakeResourceAcquisition(aTarget)); + if (aStart->mOrderedLT.BinaryIndexOf(aTarget) != NoIndex) { + aChain->AppendElement(ResourceAcquisition(aTarget->mResource, + aTarget->mFirstSeen)); return true; } index_type i = 0; - size_type len = NumOrders(aStart); - for (const PLHashEntry* const* it = GetOrders(aStart); i < len; ++i, ++it) { - aChain->AppendElement(MakeResourceAcquisition(*it)); + size_type len = aStart->mOrderedLT.Length(); + for (const OrderingEntry* const* it = aStart->mOrderedLT.Elements(); i < len; ++i, ++it) { + aChain->AppendElement(ResourceAcquisition((*it)->mResource, + (*it)->mFirstSeen)); if (GetDeductionChain_Helper(*it, aTarget, aChain)) { return true; } @@ -534,7 +486,8 @@ public: * The partial order on resource acquisitions used by the deadlock * detector. */ - PLHashTable* mOrdering; // T* -> PLHashEntry + nsClassHashtable, OrderingEntry> mOrdering; + /** * Protects contentious methods. @@ -549,13 +502,6 @@ private: }; -template -const PLHashAllocOps DeadlockDetector::kAllocOps = { - DeadlockDetector::TableAlloc, DeadlockDetector::TableFree, - DeadlockDetector::EntryAlloc, DeadlockDetector::EntryFree -}; - - template // FIXME bug 456272: tune based on average workload const uint32_t DeadlockDetector::kDefaultNumBuckets = 64; diff --git a/xpcom/glue/nsHashKeys.h b/xpcom/glue/nsHashKeys.h index 8a4caad73e5..a7ce2c29be1 100644 --- a/xpcom/glue/nsHashKeys.h +++ b/xpcom/glue/nsHashKeys.h @@ -286,7 +286,7 @@ public: static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; } static PLDHashNumber HashKey(KeyTypePointer aKey) { - return NS_PTR_TO_INT32(aKey) >> 2; + return NS_PTR_TO_UINT32(aKey) >> 2; } enum { ALLOW_MEMMOVE = true }; @@ -316,7 +316,7 @@ public: static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; } static PLDHashNumber HashKey(KeyTypePointer aKey) { - return NS_PTR_TO_INT32(aKey) >> 2; + return NS_PTR_TO_UINT32(aKey) >> 2; } enum { ALLOW_MEMMOVE = true }; @@ -356,7 +356,7 @@ public: static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; } static PLDHashNumber HashKey(KeyTypePointer aKey) { - return NS_PTR_TO_INT32(aKey) >> 2; + return NS_PTR_TO_UINT32(aKey) >> 2; } enum { ALLOW_MEMMOVE = true }; @@ -409,7 +409,7 @@ public: static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } static PLDHashNumber HashKey(KeyTypePointer aKey) { - return NS_PTR_TO_INT32(*aKey) >> 2; + return NS_PTR_TO_UINT32(*aKey) >> 2; } enum { ALLOW_MEMMOVE = true }; diff --git a/xpcom/tests/TestDeadlockDetectorScalability.cpp b/xpcom/tests/TestDeadlockDetectorScalability.cpp index 93e64a2e61b..e4eff5c08f7 100644 --- a/xpcom/tests/TestDeadlockDetectorScalability.cpp +++ b/xpcom/tests/TestDeadlockDetectorScalability.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "TestHarness.h" +#include "nsIMemoryReporter.h" //#define OLD_API @@ -38,6 +39,7 @@ #undef DD_TEST1 #undef DD_TEST2 #undef DD_TEST3 +#undef DD_TEST4 //----------------------------------------------------------------------------- @@ -154,6 +156,45 @@ MaxDepsNsq(const int N, const int K) //----------------------------------------------------------------------------- +#ifdef DD_TEST4 + +// This test creates a single lock that is ordered < N resources. The +// resources are allocated, exercised K times, and deallocated one at +// a time. + +static nsresult +OneLockNDepsUsedSeveralTimes(const size_t N, const size_t K) +{ + // Create master lock. + moz_lock_t lock_1 = NEWLOCK("deadlockDetector.scalability.t4.master"); + for (size_t n = 0; n < N; n++) { + // Create child lock. + moz_lock_t lock_2 = NEWLOCK("deadlockDetector.scalability.t4.child"); + + // First lock the master. + AUTOLOCK(m, lock_1); + + // Now lock and unlock the child a few times. + for (size_t k = 0; k < K; k++) { + AUTOLOCK(c, lock_2); + } + + // Destroy the child lock. + DELETELOCK(lock_2); + } + + // Cleanup the master lock. + DELETELOCK(lock_1); + + PASS(); +} + +#endif + +//----------------------------------------------------------------------------- + +MOZ_DEFINE_MALLOC_SIZE_OF(DeadlockDetectorMallocSizeOf) + int main(int argc, char** argv) { @@ -175,7 +216,8 @@ main(int argc, char** argv) #ifndef DD_TEST2 puts("Skipping not-requested OneLockNDeps() test"); #else - if (NS_FAILED(OneLockNDeps(1 << 14, 100))) // 16k + // NB: Using a larger test size to stress our traversal logic. + if (NS_FAILED(OneLockNDeps(1 << 17, 100))) // 131k rv = 1; #endif @@ -186,5 +228,16 @@ main(int argc, char** argv) rv = 1; #endif +#ifndef DD_TEST4 + puts("Skipping not-requested OneLockNDepsUsedSeveralTimes() test"); +#else + if (NS_FAILED(OneLockNDepsUsedSeveralTimes(1 << 17, 3))) // 131k + rv = 1; +#endif + + size_t memory_used = mozilla::BlockingResourceBase::SizeOfDeadlockDetector( + DeadlockDetectorMallocSizeOf); + printf_stderr("Used %d bytes\n", (int)memory_used); + return rv; } diff --git a/xpcom/tests/moz.build b/xpcom/tests/moz.build index 50817dd2129..0f69dcc3ac1 100644 --- a/xpcom/tests/moz.build +++ b/xpcom/tests/moz.build @@ -106,7 +106,7 @@ if CONFIG['MOZ_MEMORY']: # 'TestStaticAtoms', #] -if CONFIG['MOZ_DEBUG'] and CONFIG['OS_ARCH'] not in ('WINNT', 'Darwin'): +if CONFIG['MOZ_DEBUG'] and CONFIG['OS_ARCH'] not in ('WINNT'): # FIXME bug 523392: TestDeadlockDetector doesn't like Windows # FIXME bug 523378: also fails on OS X CPP_UNIT_TESTS += [ @@ -129,3 +129,8 @@ USE_LIBS += [ 'xpcomglue_s', 'xul', ] + +if CONFIG['MOZ_DMD']: + USE_LIBS += [ + 'dmd' + ]