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