Bug 967817 - Finish memory reporters for Web Audio. r=roc, njn
authorEric Rahm <erahm@mozilla.com>
Sun, 13 Apr 2014 11:08:10 -0700
changeset 179508 7c18257d0163e793150777c8e2812cb660501adf
parent 179507 8d25da069190f1fbefac5e1fb26121c40d565938
child 179509 ba276673a564f5906115959a1823250123f8ca4a
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersroc, njn
bugs967817
milestone31.0a1
Bug 967817 - Finish memory reporters for Web Audio. r=roc, njn
content/media/AudioNodeEngine.h
content/media/AudioNodeStream.cpp
content/media/AudioNodeStream.h
content/media/AudioSegment.h
content/media/AudioStream.cpp
content/media/AudioStream.h
content/media/MediaDecoder.cpp
content/media/MediaSegment.h
content/media/MediaStreamGraph.cpp
content/media/MediaStreamGraph.h
content/media/MediaStreamGraphImpl.h
content/media/SharedBuffer.h
content/media/StreamBuffer.h
content/media/TimeVarying.h
content/media/VideoSegment.h
content/media/webaudio/AnalyserNode.cpp
content/media/webaudio/AnalyserNode.h
content/media/webaudio/AudioBufferSourceNode.cpp
content/media/webaudio/AudioBufferSourceNode.h
content/media/webaudio/AudioDestinationNode.cpp
content/media/webaudio/AudioDestinationNode.h
content/media/webaudio/AudioNode.cpp
content/media/webaudio/AudioNode.h
content/media/webaudio/AudioParam.h
content/media/webaudio/AudioParamTimeline.h
content/media/webaudio/BiquadFilterNode.cpp
content/media/webaudio/BiquadFilterNode.h
content/media/webaudio/ChannelMergerNode.cpp
content/media/webaudio/ChannelMergerNode.h
content/media/webaudio/ChannelSplitterNode.cpp
content/media/webaudio/ChannelSplitterNode.h
content/media/webaudio/ConvolverNode.cpp
content/media/webaudio/ConvolverNode.h
content/media/webaudio/DelayBuffer.cpp
content/media/webaudio/DelayBuffer.h
content/media/webaudio/DelayNode.cpp
content/media/webaudio/DelayNode.h
content/media/webaudio/DynamicsCompressorNode.cpp
content/media/webaudio/DynamicsCompressorNode.h
content/media/webaudio/FFTBlock.h
content/media/webaudio/GainNode.cpp
content/media/webaudio/GainNode.h
content/media/webaudio/MediaElementAudioSourceNode.h
content/media/webaudio/MediaStreamAudioDestinationNode.cpp
content/media/webaudio/MediaStreamAudioDestinationNode.h
content/media/webaudio/MediaStreamAudioSourceNode.cpp
content/media/webaudio/MediaStreamAudioSourceNode.h
content/media/webaudio/OscillatorNode.cpp
content/media/webaudio/OscillatorNode.h
content/media/webaudio/PannerNode.cpp
content/media/webaudio/PannerNode.h
content/media/webaudio/PeriodicWave.cpp
content/media/webaudio/PeriodicWave.h
content/media/webaudio/ScriptProcessorNode.cpp
content/media/webaudio/ScriptProcessorNode.h
content/media/webaudio/WaveShaperNode.cpp
content/media/webaudio/WaveShaperNode.h
content/media/webaudio/blink/DirectConvolver.h
content/media/webaudio/blink/DynamicsCompressor.cpp
content/media/webaudio/blink/DynamicsCompressor.h
content/media/webaudio/blink/DynamicsCompressorKernel.cpp
content/media/webaudio/blink/DynamicsCompressorKernel.h
content/media/webaudio/blink/FFTConvolver.cpp
content/media/webaudio/blink/FFTConvolver.h
content/media/webaudio/blink/HRTFDatabase.cpp
content/media/webaudio/blink/HRTFDatabase.h
content/media/webaudio/blink/HRTFDatabaseLoader.cpp
content/media/webaudio/blink/HRTFDatabaseLoader.h
content/media/webaudio/blink/HRTFElevation.cpp
content/media/webaudio/blink/HRTFElevation.h
content/media/webaudio/blink/HRTFKernel.h
content/media/webaudio/blink/HRTFPanner.cpp
content/media/webaudio/blink/HRTFPanner.h
content/media/webaudio/blink/PeriodicWave.cpp
content/media/webaudio/blink/PeriodicWave.h
content/media/webaudio/blink/Reverb.cpp
content/media/webaudio/blink/Reverb.h
content/media/webaudio/blink/ReverbAccumulationBuffer.h
content/media/webaudio/blink/ReverbConvolver.cpp
content/media/webaudio/blink/ReverbConvolver.h
content/media/webaudio/blink/ReverbConvolverStage.cpp
content/media/webaudio/blink/ReverbConvolverStage.h
content/media/webaudio/blink/ReverbInputBuffer.h
--- a/content/media/AudioNodeEngine.h
+++ b/content/media/AudioNodeEngine.h
@@ -39,16 +39,22 @@ public:
 
   struct Storage {
     Storage()
     {
       mDataToFree = nullptr;
       mSampleData = nullptr;
     }
     ~Storage() { free(mDataToFree); }
+    size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+    {
+      // NB: mSampleData might not be owned, if it is it just points to
+      //     mDataToFree.
+      return aMallocSizeOf(mDataToFree);
+    }
     void* mDataToFree;
     const float* mSampleData;
   };
 
   /**
    * This can be called on any thread.
    */
   uint32_t GetChannels() const { return mContents.Length(); }
@@ -69,19 +75,25 @@ public:
     s->mSampleData = aData;
   }
 
   /**
    * Put this object into an error state where there are no channels.
    */
   void Clear() { mContents.Clear(); }
 
-  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+  virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
   {
-    return mContents.SizeOfExcludingThis(aMallocSizeOf);
+    size_t amount = ThreadSharedObject::SizeOfExcludingThis(aMallocSizeOf);
+    amount += mContents.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < mContents.Length(); i++) {
+      amount += mContents[i].SizeOfExcludingThis(aMallocSizeOf);
+    }
+
+    return amount;
   }
 
 private:
   AutoFallibleTArray<Storage,2> mContents;
 };
 
 /**
  * Allocates an AudioChunk with fresh buffers of WEBAUDIO_BLOCK_SIZE float samples.
@@ -323,16 +335,35 @@ public:
     MOZ_ASSERT(mNode != nullptr);
     mNodeMutex.AssertCurrentThreadOwns();
     mNode = nullptr;
   }
 
   uint16_t InputCount() const { return mInputCount; }
   uint16_t OutputCount() const { return mOutputCount; }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    // NB: |mNode| is tracked separately so it is excluded here.
+    return 0;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  void SizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
+                           AudioNodeSizes& aUsage) const
+  {
+    aUsage.mEngine = SizeOfIncludingThis(aMallocSizeOf);
+    aUsage.mDomNode = mNode->SizeOfIncludingThis(aMallocSizeOf);
+    aUsage.mNodeType = mNode->NodeType();
+  }
+
 private:
   dom::AudioNode* mNode;
   Mutex mNodeMutex;
   const uint16_t mInputCount;
   const uint16_t mOutputCount;
 };
 
 }
--- a/content/media/AudioNodeStream.cpp
+++ b/content/media/AudioNodeStream.cpp
@@ -25,16 +25,54 @@ namespace mozilla {
  * Note: This must be a different value than MEDIA_STREAM_DEST_TRACK_ID
  */
 
 AudioNodeStream::~AudioNodeStream()
 {
   MOZ_COUNT_DTOR(AudioNodeStream);
 }
 
+size_t
+AudioNodeStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = 0;
+
+  // Not reported:
+  // - mEngine
+
+  amount += ProcessedMediaStream::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mLastChunks.SizeOfExcludingThis(aMallocSizeOf);
+  for (size_t i = 0; i < mLastChunks.Length(); i++) {
+    // NB: This is currently unshared only as there are instances of
+    //     double reporting in DMD otherwise.
+    amount += mLastChunks[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+  }
+
+  return amount;
+}
+
+size_t
+AudioNodeStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
+void
+AudioNodeStream::SizeOfAudioNodesIncludingThis(MallocSizeOf aMallocSizeOf,
+                                               AudioNodeSizes& aUsage) const
+{
+  // Explicitly separate out the stream memory.
+  aUsage.mStream = SizeOfIncludingThis(aMallocSizeOf);
+
+  if (mEngine) {
+    // This will fill out the rest of |aUsage|.
+    mEngine->SizeOfIncludingThis(aMallocSizeOf, aUsage);
+  }
+}
+
 void
 AudioNodeStream::SetStreamTimeParameter(uint32_t aIndex, AudioContext* aContext,
                                         double aStreamTime)
 {
   class Message : public ControlMessage {
   public:
     Message(AudioNodeStream* aStream, uint32_t aIndex, MediaStream* aRelativeToStream,
             double aStreamTime)
--- a/content/media/AudioNodeStream.h
+++ b/content/media/AudioNodeStream.h
@@ -150,16 +150,22 @@ public:
                                       double aSeconds);
   /**
    * Get the destination stream time in seconds corresponding to a position on
    * this stream.
    */
   double DestinationTimeFromTicks(AudioNodeStream* aDestination,
                                   TrackTicks aPosition);
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
+  void SizeOfAudioNodesIncludingThis(MallocSizeOf aMallocSizeOf,
+                                     AudioNodeSizes& aUsage) const;
+
 protected:
   void AdvanceOutputSegment();
   void FinishOutput();
   void AccumulateInputChunk(uint32_t aInputIndex, const AudioChunk& aChunk,
                             AudioChunk* aBlock,
                             nsTArray<float>* aDownmixBuffer);
   void UpMixDownMixChunk(const AudioChunk* aChunk, uint32_t aOutputChannelCount,
                          nsTArray<const void*>& aOutputChannels,
--- a/content/media/AudioSegment.h
+++ b/content/media/AudioSegment.h
@@ -14,21 +14,38 @@
 #include "mozilla/TimeStamp.h"
 #endif
 
 namespace mozilla {
 
 template<typename T>
 class SharedChannelArrayBuffer : public ThreadSharedObject {
 public:
-  SharedChannelArrayBuffer(nsTArray<nsTArray<T>>* aBuffers)
+  SharedChannelArrayBuffer(nsTArray<nsTArray<T> >* aBuffers)
   {
     mBuffers.SwapElements(*aBuffers);
   }
-  nsTArray<nsTArray<T>> mBuffers;
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = 0;
+    amount += mBuffers.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < mBuffers.Length(); i++) {
+      amount += mBuffers[i].SizeOfExcludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  nsTArray<nsTArray<T> > mBuffers;
 };
 
 class AudioStream;
 class AudioMixer;
 
 /**
  * For auto-arrays etc, guess this as the common number of channels.
  */
@@ -109,16 +126,37 @@ struct AudioChunk {
     mBuffer = nullptr;
     mChannelData.Clear();
     mDuration = aDuration;
     mVolume = 1.0f;
     mBufferFormat = AUDIO_FORMAT_SILENCE;
   }
   int ChannelCount() const { return mChannelData.Length(); }
 
+  size_t SizeOfExcludingThisIfUnshared(MallocSizeOf aMallocSizeOf) const
+  {
+    return SizeOfExcludingThis(aMallocSizeOf, true);
+  }
+
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf, bool aUnshared) const
+  {
+    size_t amount = 0;
+
+    // Possibly owned:
+    // - mBuffer - Can hold data that is also in the decoded audio queue. If it
+    //             is not shared, or unshared == false it gets counted.
+    if (mBuffer && (!aUnshared || !mBuffer->IsShared())) {
+      amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    // Memory in the array is owned by mBuffer.
+    amount += mChannelData.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
   TrackTicks mDuration; // in frames within the buffer
   nsRefPtr<ThreadSharedObject> mBuffer; // the buffer object whose lifetime is managed; null means data is all zeroes
   nsTArray<const void*> mChannelData; // one pointer per channel; empty if and only if mBuffer is null
   float mVolume; // volume multiplier to apply (1.0f if mBuffer is nonnull)
   SampleFormat mBufferFormat; // format of frames in mBuffer (only meaningful if mBuffer is nonnull)
 #ifdef MOZILLA_INTERNAL_API
   mozilla::TimeStamp mTimeStamp;           // time at which this has been fetched from the MediaEngine
 #endif
@@ -226,13 +264,18 @@ public:
 
   int ChannelCount() {
     NS_WARN_IF_FALSE(!mChunks.IsEmpty(),
         "Cannot query channel count on a AudioSegment with no chunks.");
     return mChunks.IsEmpty() ? 0 : mChunks[0].mChannelData.Length();
   }
 
   static Type StaticType() { return AUDIO; }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 };
 
 }
 
 #endif /* MOZILLA_AUDIOSEGMENT_H_ */
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -167,16 +167,32 @@ AudioStream::~AudioStream()
 {
   LOG(("AudioStream: delete %p, state %d", this, mState));
   Shutdown();
   if (mDumpFile) {
     fclose(mDumpFile);
   }
 }
 
+size_t
+AudioStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = aMallocSizeOf(this);
+
+  // Possibly add in the future:
+  // - mTimeStretcher
+  // - mLatencyLog
+  // - mCubebStream
+
+  amount += mInserts.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
+
+  return amount;
+}
+
 /*static*/ void AudioStream::InitLibrary()
 {
 #ifdef PR_LOGGING
   gAudioStreamLog = PR_NewLogModule("AudioStream");
 #endif
   PrefChanged(PREF_VOLUME_SCALE, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
   PrefChanged(PREF_CUBEB_LATENCY, nullptr);
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -164,16 +164,23 @@ public:
       return mCount;
     }
     mStart += (mCount - aSize);
     mCount = aSize;
     mStart %= mCapacity;
     return mCount;
   }
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t amount = 0;
+    amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
 private:
   nsAutoArrayPtr<uint8_t> mBuffer;
   uint32_t mCapacity;
   uint32_t mStart;
   uint32_t mCount;
 };
 
 class AudioInitTask;
@@ -275,16 +282,18 @@ public:
   // This should be called before attempting to use the time stretcher.
   nsresult EnsureTimeStretcherInitialized();
   // Set playback rate as a multiple of the intrinsic playback rate. This is to
   // be called only with aPlaybackRate > 0.0.
   nsresult SetPlaybackRate(double aPlaybackRate);
   // Switch between resampling (if false) and time stretching (if true, default).
   nsresult SetPreservesPitch(bool aPreservesPitch);
 
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
+
 private:
   friend class AudioInitTask;
 
   // So we can call it asynchronously from AudioInitTask
   nsresult OpenCubeb(cubeb_stream_params &aParams,
                      LatencyRequest aLatencyRequest);
 
   void CheckForStart();
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -1765,16 +1765,18 @@ MediaMemoryTracker::CollectReports(nsIHa
 
   REPORT("explicit/media/decoded/audio", audio,
          "Memory used by decoded audio chunks.");
 
   REPORT("explicit/media/resources", resources,
          "Memory used by media resources including streaming buffers, caches, "
          "etc.");
 
+#undef REPORT
+
   return NS_OK;
 }
 
 MediaDecoderOwner*
 MediaDecoder::GetOwner()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mOwner;
--- a/content/media/MediaSegment.h
+++ b/content/media/MediaSegment.h
@@ -113,16 +113,26 @@ public:
    * Replace contents with disabled data of the same duration
    */
   virtual void ReplaceWithDisabled() = 0;
   /**
    * Remove all contents, setting duration to 0.
    */
   virtual void Clear() = 0;
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return 0;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 protected:
   MediaSegment(Type aType) : mDuration(0), mType(aType)
   {
     MOZ_COUNT_CTOR(MediaSegment);
   }
 
   TrackTicks mDuration; // total of mDurations of all chunks
   Type mType;
@@ -240,16 +250,30 @@ public:
   }
 
 #ifdef MOZILLA_INTERNAL_API
   void GetStartTime(TimeStamp &aTime) {
     aTime = mChunks[0].mTimeStamp;
   }
 #endif
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = mChunks.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < mChunks.Length(); i++) {
+      amount += mChunks[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+    }
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 protected:
   MediaSegmentBase(Type aType) : MediaSegment(aType) {}
 
   /**
    * Appends the contents of aSource to this segment, clearing aSource.
    */
   void AppendFromInternal(MediaSegmentBase<C, Chunk>* aSource)
   {
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -8,18 +8,20 @@
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/unused.h"
 
 #include "AudioSegment.h"
 #include "VideoSegment.h"
 #include "nsContentUtils.h"
 #include "nsIAppShell.h"
 #include "nsIObserver.h"
+#include "nsPrintfCString.h"
 #include "nsServiceManagerUtils.h"
 #include "nsWidgetsCID.h"
+#include "prerror.h"
 #include "prlog.h"
 #include "mozilla/Attributes.h"
 #include "TrackUnionStream.h"
 #include "ImageContainer.h"
 #include "AudioChannelService.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "AudioNodeExternalInputStream.h"
@@ -1233,16 +1235,35 @@ MediaStreamGraphImpl::RunThread()
   }
   NS_ASSERTION(!messageQueue.IsEmpty(),
                "Shouldn't have started a graph with empty message queue!");
 
   uint32_t ticksProcessed = 0;
   AutoProfilerUnregisterThread autoUnregister;
 
   for (;;) {
+    // Check if a memory report has been requested.
+    {
+      MonitorAutoLock lock(mMemoryReportMonitor);
+      if (mNeedsMemoryReport) {
+        mNeedsMemoryReport = false;
+
+        for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+          AudioNodeStream* stream = mStreams[i]->AsAudioNodeStream();
+          if (stream) {
+            AudioNodeSizes usage;
+            stream->SizeOfAudioNodesIncludingThis(MallocSizeOf, usage);
+            mAudioStreamSizes.AppendElement(usage);
+          }
+        }
+
+        lock.Notify();
+      }
+    }
+
     // Update mCurrentTime to the min of the playing audio times, or using the
     // wall-clock time change if no audio is playing.
     UpdateCurrentTime();
 
     // Calculate independent action times for each batch of messages (each
     // batch corresponding to an event loop task). This isolates the performance
     // of different scripts to some extent.
     for (uint32_t i = 0; i < messageQueue.Length(); ++i) {
@@ -1514,17 +1535,17 @@ public:
     NS_ASSERTION(mGraph->mDetectedNotRunning,
                  "We should know the graph thread control loop isn't running!");
 
     mGraph->ShutdownThreads();
 
     // mGraph's thread is not running so it's OK to do whatever here
     if (mGraph->IsEmpty()) {
       // mGraph is no longer needed, so delete it.
-      delete mGraph;
+      mGraph->Destroy();
     } else {
       // The graph is not empty.  We must be in a forced shutdown, or a
       // non-realtime graph that has finished processing.  Some later
       // AppendMessage will detect that the manager has been emptied, and
       // delete it.
       NS_ASSERTION(mGraph->mForceShutDown || !mGraph->mRealtime,
                    "Not in forced shutdown?");
       for (uint32_t i = 0; i < mGraph->mStreams.Length(); ++i) {
@@ -1744,17 +1765,17 @@ MediaStreamGraphImpl::AppendMessage(Cont
     // graph has finished processing.
     aMessage->RunDuringShutdown();
     delete aMessage;
     if (IsEmpty() &&
         mLifecycleState >= LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION) {
       if (gGraph == this) {
         gGraph = nullptr;
       }
-      delete this;
+      Destroy();
     }
     return;
   }
 
   mCurrentTaskMessageQueue.AppendElement(aMessage);
   EnsureRunInStableState();
 }
 
@@ -1777,16 +1798,55 @@ MediaStream::MediaStream(DOMMediaStream*
   MOZ_COUNT_CTOR(MediaStream);
   // aWrapper should not already be connected to a MediaStream! It needs
   // to be hooked up to this stream, and since this stream is only just
   // being created now, aWrapper must not be connected to anything.
   NS_ASSERTION(!aWrapper || !aWrapper->GetStream(),
                "Wrapper already has another media stream hooked up to it!");
 }
 
+size_t
+MediaStream::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = 0;
+
+  // Not owned:
+  // - mGraph - Not reported here
+  // - mConsumers - elements
+  // Future:
+  // - mWrapper
+  // - mVideoOutputs - elements
+  // - mLastPlayedVideoFrame
+  // - mListeners - elements
+  // - mAudioOutputStreams - elements
+
+  amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mAudioOutputs.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mVideoOutputs.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mExplicitBlockerCount.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mListeners.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mMainThreadListeners.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mDisabledTrackIDs.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mBlocked.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mGraphUpdateIndices.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mConsumers.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mAudioOutputStreams.SizeOfExcludingThis(aMallocSizeOf);
+  for (size_t i = 0; i < mAudioOutputStreams.Length(); i++) {
+    amount += mAudioOutputStreams[i].SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  return amount;
+}
+
+size_t
+MediaStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 void
 MediaStream::Init()
 {
   MediaStreamGraphImpl* graph = GraphImpl();
   mBlocked.SetAtAndAfter(graph->mCurrentTime, true);
   mExplicitBlockerCount.SetAtAndAfter(graph->mCurrentTime, true);
   mExplicitBlockerCount.SetAtAndAfter(graph->mStateComputedTime, false);
 }
@@ -2589,24 +2649,40 @@ MediaStreamGraphImpl::MediaStreamGraphIm
   , mPostedRunInStableStateEvent(false)
   , mDetectedNotRunning(false)
   , mPostedRunInStableState(false)
   , mRealtime(aRealtime)
   , mNonRealtimeProcessing(false)
   , mStreamOrderDirty(false)
   , mLatencyLog(AsyncLatencyLogger::Get())
   , mMixer(nullptr)
+  , mMemoryReportMonitor("MSGIMemory")
+  , mSelfRef(MOZ_THIS_IN_INITIALIZER_LIST())
+  , mAudioStreamSizes()
+  , mNeedsMemoryReport(false)
 {
 #ifdef PR_LOGGING
   if (!gMediaStreamGraphLog) {
     gMediaStreamGraphLog = PR_NewLogModule("MediaStreamGraph");
   }
 #endif
 
   mCurrentTimeStamp = mInitialTimeStamp = mLastMainThreadUpdate = TimeStamp::Now();
+
+  RegisterWeakMemoryReporter(this);
+}
+
+void
+MediaStreamGraphImpl::Destroy()
+{
+  // First unregister from memory reporting.
+  UnregisterWeakMemoryReporter(this);
+
+  // Clear the self reference which will destroy this instance.
+  mSelfRef = nullptr;
 }
 
 NS_IMPL_ISUPPORTS1(MediaStreamGraphShutdownObserver, nsIObserver)
 
 static bool gShutdownObserverRegistered = false;
 
 NS_IMETHODIMP
 MediaStreamGraphShutdownObserver::Observe(nsISupports *aSubject,
@@ -2630,30 +2706,32 @@ MediaStreamGraph::GetInstance()
 
   if (!gGraph) {
     if (!gShutdownObserverRegistered) {
       gShutdownObserverRegistered = true;
       nsContentUtils::RegisterShutdownObserver(new MediaStreamGraphShutdownObserver());
     }
 
     gGraph = new MediaStreamGraphImpl(true);
+
     STREAM_LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", gGraph));
 
     AudioStream::InitPreferredSampleRate();
   }
 
   return gGraph;
 }
 
 MediaStreamGraph*
 MediaStreamGraph::CreateNonRealtimeInstance()
 {
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
 
   MediaStreamGraphImpl* graph = new MediaStreamGraphImpl(false);
+
   return graph;
 }
 
 void
 MediaStreamGraph::DestroyNonRealtimeInstance(MediaStreamGraph* aGraph)
 {
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
   MOZ_ASSERT(aGraph->IsNonRealtime(), "Should not destroy the global graph here");
@@ -2664,16 +2742,86 @@ MediaStreamGraph::DestroyNonRealtimeInst
 
   if (!graph->mNonRealtimeProcessing) {
     // Start the graph, but don't produce anything
     graph->StartNonRealtimeProcessing(1, 0);
   }
   graph->ForceShutDown();
 }
 
+NS_IMPL_ISUPPORTS1(MediaStreamGraphImpl, nsIMemoryReporter)
+
+struct ArrayClearer
+{
+  ArrayClearer(nsTArray<AudioNodeSizes>& aArray) : mArray(aArray) {}
+  ~ArrayClearer() { mArray.Clear(); }
+  nsTArray<AudioNodeSizes>& mArray;
+};
+
+NS_IMETHODIMP
+MediaStreamGraphImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
+                                     nsISupports* aData)
+{
+  // Clears out the report array after we're done with it.
+  ArrayClearer reportCleanup(mAudioStreamSizes);
+
+  {
+    MonitorAutoLock memoryReportLock(mMemoryReportMonitor);
+    mNeedsMemoryReport = true;
+
+    {
+      // Wake up the MSG thread.
+      MonitorAutoLock monitorLock(mMonitor);
+      EnsureImmediateWakeUpLocked(monitorLock);
+    }
+
+    // Wait for the report to complete.
+    nsresult rv;
+    while ((rv = memoryReportLock.Wait()) != NS_OK) {
+      if (PR_GetError() != PR_PENDING_INTERRUPT_ERROR) {
+        return rv;
+      }
+    }
+  }
+
+#define REPORT(_path, _amount, _desc)                                       \
+  do {                                                                      \
+    nsresult rv;                                                            \
+    rv = aHandleReport->Callback(EmptyCString(), _path,                     \
+                                 KIND_HEAP, UNITS_BYTES, _amount,           \
+                                 NS_LITERAL_CSTRING(_desc), aData);         \
+    NS_ENSURE_SUCCESS(rv, rv);                                              \
+  } while (0)
+
+  for (size_t i = 0; i < mAudioStreamSizes.Length(); i++) {
+    const AudioNodeSizes& usage = mAudioStreamSizes[i];
+    const char* const nodeType =  usage.mNodeType.get();
+
+    nsPrintfCString domNodePath("explicit/webaudio/audio-node/%s/dom-nodes",
+                                  nodeType);
+    REPORT(domNodePath, usage.mDomNode,
+           "Memory used by AudioNode DOM objects (Web Audio).");
+
+    nsPrintfCString enginePath("explicit/webaudio/audio-node/%s/engine-objects",
+                                nodeType);
+    REPORT(enginePath, usage.mEngine,
+           "Memory used by AudioNode engine objects (Web Audio).");
+
+    nsPrintfCString streamPath("explicit/webaudio/audio-node/%s/stream-objects",
+                                nodeType);
+    REPORT(streamPath, usage.mStream,
+           "Memory used by AudioNode stream objects (Web Audio).");
+
+  }
+
+#undef REPORT
+
+  return NS_OK;
+}
+
 SourceMediaStream*
 MediaStreamGraph::CreateSourceStream(DOMMediaStream* aWrapper)
 {
   SourceMediaStream* stream = new SourceMediaStream(aWrapper);
   NS_ADDREF(stream);
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
   stream->SetGraphImpl(graph);
   graph->AppendMessage(new CreateMessage(stream));
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -221,16 +221,27 @@ public:
  * The listener is allowed to synchronously remove itself from the stream, but
  * not add or remove any other listeners.
  */
 class MainThreadMediaStreamListener {
 public:
   virtual void NotifyMainThreadStateChanged() = 0;
 };
 
+/**
+ * Helper struct used to keep track of memory usage by AudioNodes.
+ */
+struct AudioNodeSizes
+{
+  size_t mDomNode;
+  size_t mStream;
+  size_t mEngine;
+  nsCString mNodeType;
+};
+
 class MediaStreamGraphImpl;
 class SourceMediaStream;
 class ProcessedMediaStream;
 class MediaInputPort;
 class AudioNodeEngine;
 class AudioNodeExternalInputStream;
 class AudioNodeStream;
 struct AudioChunk;
@@ -505,16 +516,19 @@ public:
   }
 
   // Return true if the main thread needs to observe updates from this stream.
   virtual bool MainThreadNeedsUpdates() const
   {
     return true;
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
+
 protected:
   virtual void AdvanceTimeVaryingValuesToCurrentTime(GraphTime aCurrentTime, GraphTime aBlockedTime)
   {
     mBufferStartTime += aBlockedTime;
     mGraphUpdateIndices.InsertTimeAtStart(aBlockedTime);
     mGraphUpdateIndices.AdvanceCurrentTime(aCurrentTime);
     mExplicitBlockerCount.AdvanceCurrentTime(aCurrentTime);
 
@@ -572,16 +586,23 @@ protected:
     GraphTime mAudioPlaybackStartTime;
     // Amount of time that we've wanted to play silence because of the stream
     // blocking.
     MediaTime mBlockedAudioTime;
     // Last tick written to the audio output.
     TrackTicks mLastTickWritten;
     RefPtr<AudioStream> mStream;
     TrackID mTrackID;
+
+    size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+    {
+      size_t amount = 0;
+      amount += mStream->SizeOfIncludingThis(aMallocSizeOf);
+      return amount;
+    }
   };
   nsTArray<AudioOutputStream> mAudioOutputStreams;
 
   /**
    * When true, this means the stream will be finished once all
    * buffered data has been consumed.
    */
   bool mFinished;
@@ -915,16 +936,32 @@ public:
    */
   MediaStreamGraphImpl* GraphImpl();
   MediaStreamGraph* Graph();
   /**
    * Sets the graph that owns this stream.  Should only be called once.
    */
   void SetGraphImpl(MediaStreamGraphImpl* aGraph);
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t amount = 0;
+
+    // Not owned:
+    // - mSource
+    // - mDest
+    // - mGraph
+    return amount;
+  }
+
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   friend class MediaStreamGraphImpl;
   friend class MediaStream;
   friend class ProcessedMediaStream;
   // Never modified after Init()
   MediaStream* mSource;
   ProcessedMediaStream* mDest;
   uint32_t mFlags;
@@ -1011,16 +1048,29 @@ public:
 
   /**
    * Forward SetTrackEnabled() to the input MediaStream(s) and translate the ID
    */
   virtual void ForwardTrackEnabled(TrackID aOutputID, bool aEnabled) {};
 
   bool InCycle() const { return mInCycle; }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = MediaStream::SizeOfExcludingThis(aMallocSizeOf);
+    // Not owned:
+    // - mInputs elements
+    amount += mInputs.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 
 protected:
   // This state is all accessed only on the media graph thread.
 
   // The list of all inputs that are currently enabled or waiting to be enabled.
   nsTArray<MediaInputPort*> mInputs;
   bool mAutofinish;
   // True if and only if this stream is in a cycle.
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -5,16 +5,17 @@
 
 #ifndef MOZILLA_MEDIASTREAMGRAPHIMPL_H_
 #define MOZILLA_MEDIASTREAMGRAPHIMPL_H_
 
 #include "MediaStreamGraph.h"
 
 #include "mozilla/Monitor.h"
 #include "mozilla/TimeStamp.h"
+#include "nsIMemoryReporter.h"
 #include "nsIThread.h"
 #include "nsIRunnable.h"
 #include "Latency.h"
 #include "mozilla/WeakPtr.h"
 
 namespace mozilla {
 
 template <typename T>
@@ -101,27 +102,36 @@ protected:
 /**
  * The implementation of a media stream graph. This class is private to this
  * file. It's not in the anonymous namespace because MediaStream needs to
  * be able to friend it.
  *
  * Currently we have one global instance per process, and one per each
  * OfflineAudioContext object.
  */
-class MediaStreamGraphImpl : public MediaStreamGraph {
+class MediaStreamGraphImpl : public MediaStreamGraph,
+                             public nsIMemoryReporter {
 public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIMEMORYREPORTER
+
   /**
    * Set aRealtime to true in order to create a MediaStreamGraph which provides
    * support for real-time audio and video.  Set it to false in order to create
    * a non-realtime instance which just churns through its inputs and produces
    * output.  Those objects currently only support audio, and are used to
    * implement OfflineAudioContext.  They do not support MediaStream inputs.
    */
   explicit MediaStreamGraphImpl(bool aRealtime);
-  virtual ~MediaStreamGraphImpl();
+
+  /**
+   * Unregisters memory reporting and deletes this instance. This should be
+   * called instead of calling the destructor directly.
+   */
+  void Destroy();
 
   // Main thread only.
   /**
    * This runs every time we need to sync state from the media graph thread
    * to the main thread while the main thread is not in the middle
    * of a script. It runs during a "stable state" (per HTML5) or during
    * an event posted to the main thread.
    */
@@ -573,13 +583,39 @@ public:
   /**
    * Hold a ref to the Latency logger
    */
   nsRefPtr<AsyncLatencyLogger> mLatencyLog;
   /**
    * If this is not null, all the audio output for the MSG will be mixed down.
    */
   nsAutoPtr<AudioMixer> mMixer;
+
+private:
+  virtual ~MediaStreamGraphImpl();
+
+  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+
+  /**
+   * Used to signal that a memory report has been requested.
+   */
+  Monitor mMemoryReportMonitor;
+  /**
+   * This class uses manual memory management, and all pointers to it are raw
+   * pointers. However, in order for it to implement nsIMemoryReporter, it needs
+   * to implement nsISupports and so be ref-counted. So it maintains a single
+   * nsRefPtr to itself, giving it a ref-count of 1 during its entire lifetime,
+   * and Destroy() nulls this self-reference in order to trigger self-deletion.
+   */
+  nsRefPtr<MediaStreamGraphImpl> mSelfRef;
+  /**
+   * Used to pass memory report information across threads.
+   */
+  nsTArray<AudioNodeSizes> mAudioStreamSizes;
+  /**
+   * Indicates that the MSG thread should gather data for a memory report.
+   */
+  bool mNeedsMemoryReport;
 };
 
 }
 
 #endif /* MEDIASTREAMGRAPHIMPL_H_ */
--- a/content/media/SharedBuffer.h
+++ b/content/media/SharedBuffer.h
@@ -16,16 +16,27 @@ namespace mozilla {
 /**
  * Base class for objects with a thread-safe refcount and a virtual
  * destructor.
  */
 class ThreadSharedObject {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadSharedObject)
 
+  bool IsShared() { return mRefCnt.get() > 1; }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return 0;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~ThreadSharedObject() {}
 };
 
 /**
  * Heap-allocated chunk of arbitrary data with threadsafe refcounting.
  * Typically you would allocate one of these, fill it in, and then treat it as
@@ -47,18 +58,19 @@ public:
     }
     void* m = moz_xmalloc(size.value());
     nsRefPtr<SharedBuffer> p = new (m) SharedBuffer();
     NS_ASSERTION((reinterpret_cast<char*>(p.get() + 1) - reinterpret_cast<char*>(p.get())) % 4 == 0,
                  "SharedBuffers should be at least 4-byte aligned");
     return p.forget();
   }
 
-  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
-    return aMallocSizeOf(this);
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
 private:
   SharedBuffer() {}
 };
 
 }
 
--- a/content/media/StreamBuffer.h
+++ b/content/media/StreamBuffer.h
@@ -158,16 +158,25 @@ public:
     {
       return mSegment.forget();
     }
     void ForgetUpTo(TrackTicks aTime)
     {
       mSegment->ForgetUpTo(aTime);
     }
 
+    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+    {
+      size_t amount = aMallocSizeOf(this);
+      if (mSegment) {
+        amount += mSegment->SizeOfIncludingThis(aMallocSizeOf);
+      }
+      return amount;
+    }
+
   protected:
     friend class StreamBuffer;
 
     // Start offset is in ticks at rate mRate
     TrackTicks mStart;
     // The segment data starts at the start of the owning StreamBuffer, i.e.,
     // there's mStart silence/no video at the beginning.
     nsAutoPtr<MediaSegment> mSegment;
@@ -193,16 +202,26 @@ public:
   {
     MOZ_COUNT_CTOR(StreamBuffer);
   }
   ~StreamBuffer()
   {
     MOZ_COUNT_DTOR(StreamBuffer);
   }
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t amount = 0;
+    amount += mTracks.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < mTracks.Length(); i++) {
+      amount += mTracks[i]->SizeOfIncludingThis(aMallocSizeOf);
+    }
+    return amount;
+  }
+
   /**
    * Takes ownership of aSegment. Don't do this while iterating, or while
    * holding a Track reference.
    * aSegment must have aStart worth of null data.
    */
   Track& AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart, MediaSegment* aSegment)
   {
     NS_ASSERTION(TimeToTicksRoundDown(aRate, mTracksKnownTime) <= aStart,
--- a/content/media/TimeVarying.h
+++ b/content/media/TimeVarying.h
@@ -210,16 +210,21 @@ public:
     NS_ASSERTION(&aOther != this, "Can't self-append");
     SetAtAndAfter(aTimeOffset, aOther.mCurrent);
     for (uint32_t i = 0; i < aOther.mChanges.Length(); ++i) {
       const Entry& e = aOther.mChanges[i];
       SetAtAndAfter(aTimeOffset + e.mTime, e.mValue);
     }
   }
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return mChanges.SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   struct Entry {
     Entry(Time aTime, const T& aValue) : mTime(aTime), mValue(aValue) {}
 
     // The time at which the value changes to mValue
     Time mTime;
     T mValue;
   };
--- a/content/media/VideoSegment.h
+++ b/content/media/VideoSegment.h
@@ -71,16 +71,23 @@ struct VideoChunk {
   void SetNull(TrackTicks aDuration)
   {
     mDuration = aDuration;
     mFrame.SetNull();
     mTimeStamp = TimeStamp();
   }
   void SetForceBlack(bool aForceBlack) { mFrame.SetForceBlack(aForceBlack); }
 
+  size_t SizeOfExcludingThisIfUnshared(MallocSizeOf aMallocSizeOf) const
+  {
+    // Future:
+    // - mFrame
+    return 0;
+  }
+
   TrackTicks mDuration;
   VideoFrame mFrame;
   mozilla::TimeStamp mTimeStamp;
 };
 
 class VideoSegment : public MediaSegmentBase<VideoSegment, VideoChunk> {
 public:
   typedef mozilla::layers::Image Image;
@@ -116,13 +123,18 @@ public:
          !i.IsEnded(); i.Next()) {
       VideoChunk& chunk = *i;
       chunk.SetForceBlack(true);
     }
   }
 
   // Segment-generic methods not in MediaSegmentBase
   static Type StaticType() { return VIDEO; }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 };
 
 }
 
 #endif /* MOZILLA_VIDEOSEGMENT_H_ */
--- a/content/media/webaudio/AnalyserNode.cpp
+++ b/content/media/webaudio/AnalyserNode.cpp
@@ -67,16 +67,21 @@ public:
     MutexAutoLock lock(NodeMutex());
 
     if (Node() &&
         aInput.mChannelData.Length() > 0) {
       nsRefPtr<TransferBuffer> transfer = new TransferBuffer(aStream, aInput);
       NS_DispatchToMainThread(transfer);
     }
   }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 };
 
 AnalyserNode::AnalyserNode(AudioContext* aContext)
   : AudioNode(aContext,
               1,
               ChannelCountMode::Explicit,
               ChannelInterpretation::Speakers)
   , mAnalysisBlock(2048)
@@ -85,16 +90,32 @@ AnalyserNode::AnalyserNode(AudioContext*
   , mSmoothingTimeConstant(.8)
   , mWriteIndex(0)
 {
   mStream = aContext->Graph()->CreateAudioNodeStream(new AnalyserNodeEngine(this),
                                                      MediaStreamGraph::INTERNAL_STREAM);
   AllocateBuffer();
 }
 
+size_t
+AnalyserNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mAnalysisBlock.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
+  amount += mOutputBuffer.SizeOfExcludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+AnalyserNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 AnalyserNode::WrapObject(JSContext* aCx)
 {
   return AnalyserNodeBinding::Wrap(aCx, this);
 }
 
 void
 AnalyserNode::SetFftSize(uint32_t aValue, ErrorResult& aRv)
--- a/content/media/webaudio/AnalyserNode.h
+++ b/content/media/webaudio/AnalyserNode.h
@@ -48,16 +48,24 @@ public:
   }
   void SetMaxDecibels(double aValue, ErrorResult& aRv);
   double SmoothingTimeConstant() const
   {
     return mSmoothingTimeConstant;
   }
   void SetSmoothingTimeConstant(double aValue, ErrorResult& aRv);
 
+  virtual const char* NodeType() const
+  {
+    return "AnalyserNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   friend class AnalyserNodeEngine;
   void AppendChunk(const AudioChunk& aChunk);
   bool AllocateBuffer();
   bool FFTAnalysis();
   void ApplyBlackmanWindow(float* aBuffer, uint32_t aSize);
 
 private:
--- a/content/media/webaudio/AudioBufferSourceNode.cpp
+++ b/content/media/webaudio/AudioBufferSourceNode.cpp
@@ -477,16 +477,41 @@ public:
     // We've finished if we've gone past mStop, or if we're past mDuration when
     // looping is disabled.
     if (streamPosition >= mStop ||
         (!mLoop && mBufferPosition >= mBufferEnd && !mRemainingResamplerTail)) {
       *aFinished = true;
     }
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Not owned:
+    // - mBuffer - shared w/ AudioNode
+    // - mPlaybackRateTimeline - shared w/ AudioNode
+
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+
+    // NB: We need to modify speex if we want the full memory picture, internal
+    //     fields that need measuring noted below.
+    // - mResampler->mem
+    // - mResampler->sinc_table
+    // - mResampler->last_sample
+    // - mResampler->magic_samples
+    // - mResampler->samp_frac_num
+    amount += aMallocSizeOf(mResampler);
+
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   double mStart; // including the fractional position between ticks
   // Low pass filter effects from the resampler mean that samples before the
   // start time are influenced by resampling the buffer.  mBeginProcessing
   // includes the extent of this filter.  The special value of -TRACK_TICKS_MAX
   // indicates that the resampler has begun processing.
   TrackTicks mBeginProcessing;
   TrackTicks mStop;
   nsRefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
@@ -529,16 +554,34 @@ AudioBufferSourceNode::AudioBufferSource
 
 AudioBufferSourceNode::~AudioBufferSourceNode()
 {
   if (Context()) {
     Context()->UnregisterAudioBufferSourceNode(this);
   }
 }
 
+size_t
+AudioBufferSourceNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  if (mBuffer) {
+    amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  amount += mPlaybackRate->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+AudioBufferSourceNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 AudioBufferSourceNode::WrapObject(JSContext* aCx)
 {
   return AudioBufferSourceNodeBinding::Wrap(aCx, this);
 }
 
 void
 AudioBufferSourceNode::Start(double aWhen, double aOffset,
--- a/content/media/webaudio/AudioBufferSourceNode.h
+++ b/content/media/webaudio/AudioBufferSourceNode.h
@@ -103,16 +103,24 @@ public:
     SendLoopParametersToStream();
   }
   void SendDopplerShiftToStream(double aDopplerShift);
 
   IMPL_EVENT_HANDLER(ended)
 
   virtual void NotifyMainThreadStateChanged() MOZ_OVERRIDE;
 
+  virtual const char* NodeType() const
+  {
+    return "AudioBufferSourceNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   friend class AudioBufferSourceNodeEngine;
   // START is sent during Start().
   // STOP is sent during Stop().
   // BUFFERSTART and BUFFEREND are sent when SetBuffer() and Start() have
   // been called (along with sending the buffer).
   enum EngineParameters {
     SAMPLE_RATE,
--- a/content/media/webaudio/AudioDestinationNode.cpp
+++ b/content/media/webaudio/AudioDestinationNode.cpp
@@ -142,16 +142,28 @@ public:
     }
 
     nsRefPtr<OfflineAudioCompletionEvent> event =
         new OfflineAudioCompletionEvent(context, nullptr, nullptr);
     event->InitEvent(renderedBuffer);
     context->DispatchTrustedEvent(event);
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    amount += mInputChannels.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   // The input to the destination node is recorded in the mInputChannels buffer.
   // When this buffer fills up with mLength frames, the buffered input is sent
   // to the main thread in order to dispatch OfflineAudioCompletionEvent.
   InputChannels mInputChannels;
   // An index representing the next offset in mInputChannels to be written to.
   uint32_t mWriteIndex;
   // How many frames the OfflineAudioContext intends to produce.
@@ -183,16 +195,21 @@ public:
       mVolume = aParam;
     }
   }
 
   enum Parameters {
     VOLUME,
   };
 
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   float mVolume;
 };
 
 static bool UseAudioChannelService()
 {
   return Preferences::GetBool("media.useAudioChannelService");
 }
@@ -251,16 +268,31 @@ AudioDestinationNode::AudioDestinationNo
                                      /* useCapture = */ true,
                                      /* wantsUntrusted = */ false);
     }
 
     CreateAudioChannelAgent();
   }
 }
 
+size_t
+AudioDestinationNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  // Might be useful in the future:
+  // - mAudioChannelAgent
+  return amount;
+}
+
+size_t
+AudioDestinationNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 void
 AudioDestinationNode::DestroyMediaStream()
 {
   if (mAudioChannelAgent && !Context()->IsOffline()) {
     mAudioChannelAgent->StopPlaying();
     mAudioChannelAgent = nullptr;
 
     nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
--- a/content/media/webaudio/AudioDestinationNode.h
+++ b/content/media/webaudio/AudioDestinationNode.h
@@ -69,16 +69,24 @@ public:
 
   // An amount that should be added to the MediaStream's current time to
   // get the AudioContext.currentTime.
   double ExtraCurrentTime();
 
   // When aIsOnlyNode is true, this is the only node for the AudioContext.
   void SetIsOnlyNodeForContext(bool aIsOnlyNode);
 
+  virtual const char* NodeType() const
+  {
+    return "AudioDestinationNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   bool CheckAudioChannelPermissions(AudioChannel aValue);
   void CreateAudioChannelAgent();
 
   void SetCanPlay(bool aCanPlay);
 
   void NotifyStableState();
   void ScheduleStableStateNotification();
--- a/content/media/webaudio/AudioNode.cpp
+++ b/content/media/webaudio/AudioNode.cpp
@@ -72,16 +72,47 @@ AudioNode::~AudioNode()
   MOZ_ASSERT(mInputNodes.IsEmpty());
   MOZ_ASSERT(mOutputNodes.IsEmpty());
   MOZ_ASSERT(mOutputParams.IsEmpty());
   if (mContext) {
     mContext->UpdateNodeCount(-1);
   }
 }
 
+size_t
+AudioNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  // Not owned:
+  // - mContext
+  // - mStream
+  size_t amount = 0;
+
+  amount += mInputNodes.SizeOfExcludingThis(aMallocSizeOf);
+  for (size_t i = 0; i < mInputNodes.Length(); i++) {
+    amount += mInputNodes[i].SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  // Just measure the array. The entire audio node graph is measured via the
+  // MediaStreamGraph's streams, so we don't want to double-count the elements.
+  amount += mOutputNodes.SizeOfExcludingThis(aMallocSizeOf);
+
+  amount += mOutputParams.SizeOfExcludingThis(aMallocSizeOf);
+  for (size_t i = 0; i < mOutputParams.Length(); i++) {
+    amount += mOutputParams[i]->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  return amount;
+}
+
+size_t
+AudioNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 template <class InputNode>
 static uint32_t
 FindIndexOfNode(const nsTArray<InputNode>& aInputNodes, const AudioNode* aNode)
 {
   for (uint32_t i = 0; i < aInputNodes.Length(); ++i) {
     if (aInputNodes[i].mInputNode == aNode) {
       return i;
     }
--- a/content/media/webaudio/AudioNode.h
+++ b/content/media/webaudio/AudioNode.h
@@ -10,16 +10,17 @@
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/AudioNodeBinding.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "AudioContext.h"
 #include "MediaStreamGraph.h"
 #include "WebAudioUtils.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace mozilla {
 
 namespace dom {
 
 class AudioContext;
 class AudioBufferSourceNode;
 class AudioParam;
@@ -165,16 +166,26 @@ public:
   struct InputNode {
     ~InputNode()
     {
       if (mStreamPort) {
         mStreamPort->Destroy();
       }
     }
 
+    size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+    {
+      size_t amount = 0;
+      if (mStreamPort) {
+        amount += mStreamPort->SizeOfIncludingThis(aMallocSizeOf);
+      }
+
+      return amount;
+    }
+
     // Weak reference.
     AudioNode* mInputNode;
     nsRefPtr<MediaInputPort> mStreamPort;
     // The index of the input port this node feeds into.
     // This is not used for connections to AudioParams.
     uint32_t mInputPort;
     // The index of the output port this node comes out of.
     uint32_t mOutputPort;
@@ -203,16 +214,21 @@ public:
   void MarkActive() { Context()->RegisterActiveNode(this); }
   // Active nodes call MarkInactive() when they have finished producing sound
   // for the foreseeable future.
   // Do not call MarkInactive from a node destructor.  If the destructor is
   // called, then the node is already inactive.
   // MarkInactive() may delete |this|.
   void MarkInactive() { Context()->UnregisterActiveNode(this); }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
+
+  virtual const char* NodeType() const = 0;
+
 private:
   friend class AudioBufferSourceNode;
   // This could possibly delete 'this'.
   void DisconnectFromGraph();
 
 protected:
   static void Callback(AudioNode* aNode) { /* not implemented */ }
 
--- a/content/media/webaudio/AudioParam.h
+++ b/content/media/webaudio/AudioParam.h
@@ -148,16 +148,37 @@ public:
     return mInputNodes.AppendElement();
   }
 
   void DisconnectFromGraphAndDestroyStream();
 
   // May create the stream if it doesn't exist
   MediaStream* Stream();
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = AudioParamTimeline::SizeOfExcludingThis(aMallocSizeOf);
+    // Not owned:
+    // - mNode
+
+    // Just count the array, actual nodes are counted in mNode.
+    amount += mInputNodes.SizeOfExcludingThis(aMallocSizeOf);
+
+    if (mNodeStreamPort) {
+      amount += mNodeStreamPort->SizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 protected:
   nsCycleCollectingAutoRefCnt mRefCnt;
   NS_DECL_OWNINGTHREAD
 
 private:
   nsRefPtr<AudioNode> mNode;
   // For every InputNode, there is a corresponding entry in mOutputParams of the
   // InputNode's mInputNode.
--- a/content/media/webaudio/AudioParamTimeline.h
+++ b/content/media/webaudio/AudioParamTimeline.h
@@ -47,16 +47,27 @@ public:
   // Get the value of the AudioParam at time aTime + aCounter.
   // aCounter here is an offset to aTime if we try to get the value in ticks,
   // otherwise it should always be zero.  aCounter is meant to be used when
   // getting the value of an a-rate AudioParam for each tick inside an
   // AudioNodeEngine implementation.
   template<class TimeType>
   float GetValueAtTime(TimeType aTime, size_t aCounter = 0);
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return mStream ? mStream->SizeOfIncludingThis(aMallocSizeOf) : 0;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+
 private:
   float AudioNodeInputValue(size_t aCounter) const;
 
 protected:
   // This is created lazily when needed.
   nsRefPtr<MediaStream> mStream;
 };
 
--- a/content/media/webaudio/BiquadFilterNode.cpp
+++ b/content/media/webaudio/BiquadFilterNode.cpp
@@ -206,16 +206,32 @@ public:
       SetParamsOnBiquad(mBiquads[i], aStream->SampleRate(), mType, freq, q, gain, detune);
 
       mBiquads[i].process(input,
                           static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i])),
                           aInput.GetDuration());
     }
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Not owned:
+    // - mSource - probably not owned
+    // - mDestination - probably not owned
+    // - AudioParamTimelines - counted in the AudioNode
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    amount += mBiquads.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   AudioNodeStream* mSource;
   AudioNodeStream* mDestination;
   BiquadFilterType mType;
   AudioParamTimeline mFrequency;
   AudioParamTimeline mDetune;
   AudioParamTimeline mQ;
   AudioParamTimeline mGain;
@@ -237,16 +253,47 @@ BiquadFilterNode::BiquadFilterNode(Audio
   , mGain(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
                          SendGainToStream, 0.f))
 {
   BiquadFilterNodeEngine* engine = new BiquadFilterNodeEngine(this, aContext->Destination());
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
 }
 
+
+size_t
+BiquadFilterNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+
+  if (mFrequency) {
+    amount += mFrequency->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  if (mDetune) {
+    amount += mDetune->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  if (mQ) {
+    amount += mQ->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  if (mGain) {
+    amount += mGain->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  return amount;
+}
+
+size_t
+BiquadFilterNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 BiquadFilterNode::WrapObject(JSContext* aCx)
 {
   return BiquadFilterNodeBinding::Wrap(aCx, this);
 }
 
 void
 BiquadFilterNode::SetType(BiquadFilterType aType)
--- a/content/media/webaudio/BiquadFilterNode.h
+++ b/content/media/webaudio/BiquadFilterNode.h
@@ -51,16 +51,24 @@ public:
   {
     return mGain;
   }
 
   void GetFrequencyResponse(const Float32Array& aFrequencyHz,
                             const Float32Array& aMagResponse,
                             const Float32Array& aPhaseResponse);
 
+  virtual const char* NodeType() const
+  {
+    return "BiquadFilterNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   static void SendFrequencyToStream(AudioNode* aNode);
   static void SendDetuneToStream(AudioNode* aNode);
   static void SendQToStream(AudioNode* aNode);
   static void SendGainToStream(AudioNode* aNode);
 
 private:
   BiquadFilterType mType;
--- a/content/media/webaudio/ChannelMergerNode.cpp
+++ b/content/media/webaudio/ChannelMergerNode.cpp
@@ -48,16 +48,21 @@ public:
         AudioBlockCopyChannelWithScale(
             static_cast<const float*>(aInput[i].mChannelData[j]),
             aInput[i].mVolume,
             static_cast<float*>(const_cast<void*>(aOutput[0].mChannelData[channelIndex])));
         ++channelIndex;
       }
     }
   }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 };
 
 ChannelMergerNode::ChannelMergerNode(AudioContext* aContext,
                                      uint16_t aInputCount)
   : AudioNode(aContext,
               2,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
--- a/content/media/webaudio/ChannelMergerNode.h
+++ b/content/media/webaudio/ChannelMergerNode.h
@@ -21,16 +21,26 @@ public:
                     uint16_t aInputCount);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   virtual uint16_t NumberOfInputs() const MOZ_OVERRIDE { return mInputCount; }
 
+  virtual const char* NodeType() const
+  {
+    return "ChannelMergerNode";
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   const uint16_t mInputCount;
 };
 
 }
 }
 
 #endif
--- a/content/media/webaudio/ChannelSplitterNode.cpp
+++ b/content/media/webaudio/ChannelSplitterNode.cpp
@@ -40,16 +40,21 @@ public:
             aInput[0].mVolume,
             static_cast<float*>(const_cast<void*>(aOutput[i].mChannelData[0])));
       } else {
         // Pad with silent channels if needed
         aOutput[i].SetNull(WEBAUDIO_BLOCK_SIZE);
       }
     }
   }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 };
 
 ChannelSplitterNode::ChannelSplitterNode(AudioContext* aContext,
                                          uint16_t aOutputCount)
   : AudioNode(aContext,
               2,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
--- a/content/media/webaudio/ChannelSplitterNode.h
+++ b/content/media/webaudio/ChannelSplitterNode.h
@@ -21,16 +21,26 @@ public:
                       uint16_t aOutputCount);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   virtual uint16_t NumberOfOutputs() const MOZ_OVERRIDE { return mOutputCount; }
 
+  virtual const char* NodeType() const
+  {
+    return "ChannelSplitterNode";
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   const uint16_t mOutputCount;
 };
 
 }
 }
 
 #endif
--- a/content/media/webaudio/ConvolverNode.cpp
+++ b/content/media/webaudio/ConvolverNode.cpp
@@ -149,16 +149,35 @@ public:
       mLeftOverData = mBufferLength;
       MOZ_ASSERT(mLeftOverData > 0);
     }
     AllocateAudioBlock(2, aOutput);
 
     mReverb->process(&input, aOutput, WEBAUDIO_BLOCK_SIZE);
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    if (mBuffer && !mBuffer->IsShared()) {
+      amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    if (mReverb) {
+      amount += mReverb->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   nsRefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
   nsAutoPtr<WebCore::Reverb> mReverb;
   int32_t mBufferLength;
   int32_t mLeftOverData;
   float mSampleRate;
   bool mUseBackgroundThreads;
   bool mNormalize;
@@ -170,16 +189,34 @@ ConvolverNode::ConvolverNode(AudioContex
               ChannelCountMode::Clamped_max,
               ChannelInterpretation::Speakers)
   , mNormalize(true)
 {
   ConvolverNodeEngine* engine = new ConvolverNodeEngine(this, mNormalize);
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
 }
 
+size_t
+ConvolverNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  if (mBuffer) {
+    // NB: mBuffer might be shared with the associated engine, by convention
+    //     the AudioNode will report.
+    amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
+  }
+  return amount;
+}
+
+size_t
+ConvolverNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 ConvolverNode::WrapObject(JSContext* aCx)
 {
   return ConvolverNodeBinding::Wrap(aCx, this);
 }
 
 void
 ConvolverNode::SetBuffer(JSContext* aCx, AudioBuffer* aBuffer, ErrorResult& aRv)
--- a/content/media/webaudio/ConvolverNode.h
+++ b/content/media/webaudio/ConvolverNode.h
@@ -49,16 +49,24 @@ public:
   {
     if (aMode == ChannelCountMode::Max) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
     AudioNode::SetChannelCountModeValue(aMode, aRv);
   }
 
+  virtual const char* NodeType() const
+  {
+    return "ConvolverNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   nsRefPtr<AudioBuffer> mBuffer;
   bool mNormalize;
 };
 
 
 } //end namespace dom
 } //end namespace mozilla
--- a/content/media/webaudio/DelayBuffer.cpp
+++ b/content/media/webaudio/DelayBuffer.cpp
@@ -7,16 +7,29 @@
 #include "DelayBuffer.h"
 
 #include "mozilla/PodOperations.h"
 #include "AudioChannelFormat.h"
 #include "AudioNodeEngine.h"
 
 namespace mozilla {
 
+size_t
+DelayBuffer::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = 0;
+  amount += mChunks.SizeOfExcludingThis(aMallocSizeOf);
+  for (size_t i = 0; i < mChunks.Length(); i++) {
+    amount += mChunks[i].SizeOfExcludingThis(aMallocSizeOf, false);
+  }
+
+  amount += mUpmixChannels.SizeOfExcludingThis(aMallocSizeOf);
+  return amount;
+}
+
 void
 DelayBuffer::Write(const AudioChunk& aInputChunk)
 {
   // We must have a reference to the buffer if there are channels
   MOZ_ASSERT(aInputChunk.IsNull() == !aInputChunk.mChannelData.Length());
 #ifdef DEBUG
   MOZ_ASSERT(!mHaveWrittenBlock);
   mHaveWrittenBlock = true;
--- a/content/media/webaudio/DelayBuffer.h
+++ b/content/media/webaudio/DelayBuffer.h
@@ -70,16 +70,18 @@ public:
 
   void Reset() {
     mChunks.Clear();
     mCurrentDelay = -1.0;
   };
 
   int MaxDelayTicks() const { return mMaxDelayTicks; }
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
+
 private:
   void ReadChannels(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
                     const AudioChunk* aOutputChunk,
                     uint32_t aFirstChannel, uint32_t aNumChannelsToRead,
                     ChannelInterpretation aChannelInterpretation);
   bool EnsureBuffer();
   int PositionForDelay(int aDelay);
   int ChunkForPosition(int aPosition);
--- a/content/media/webaudio/DelayNode.cpp
+++ b/content/media/webaudio/DelayNode.cpp
@@ -154,16 +154,32 @@ public:
   {
     if (mLeftOverData <= 0) {
       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
     } else {
       UpdateOutputBlock(aOutput);
     }
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    // Not owned:
+    // - mSource - probably not owned
+    // - mDestination - probably not owned
+    // - mDelay - shares ref with AudioNode, don't count
+    amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   AudioNodeStream* mSource;
   AudioNodeStream* mDestination;
   AudioParamTimeline mDelay;
   DelayBuffer mBuffer;
   double mMaxDelay;
   TrackTicks mLastOutputPosition;
   // How much data we have in our buffer which needs to be flushed out when our inputs
   // finish.
@@ -180,16 +196,30 @@ DelayNode::DelayNode(AudioContext* aCont
 {
   DelayNodeEngine* engine =
     new DelayNodeEngine(this, aContext->Destination(),
                         aContext->SampleRate() * aMaxDelay);
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
 }
 
+size_t
+DelayNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mDelay->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+DelayNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 DelayNode::WrapObject(JSContext* aCx)
 {
   return DelayNodeBinding::Wrap(aCx, this);
 }
 
 void
 DelayNode::SendDelayToStream(AudioNode* aNode)
--- a/content/media/webaudio/DelayNode.h
+++ b/content/media/webaudio/DelayNode.h
@@ -30,16 +30,24 @@ public:
     return mDelay;
   }
 
   virtual const DelayNode* AsDelayNode() const MOZ_OVERRIDE
   {
     return this;
   }
 
+  virtual const char* NodeType() const
+  {
+    return "DelayNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   static void SendDelayToStream(AudioNode* aNode);
   friend class DelayNodeEngine;
 
 private:
   nsRefPtr<AudioParam> mDelay;
 };
 
--- a/content/media/webaudio/DynamicsCompressorNode.cpp
+++ b/content/media/webaudio/DynamicsCompressorNode.cpp
@@ -125,16 +125,33 @@ public:
 
     AllocateAudioBlock(channelCount, aOutput);
     mCompressor->process(&aInput, aOutput, aInput.GetDuration());
 
     SendReductionParamToMainThread(aStream,
                                    mCompressor->parameterValue(DynamicsCompressor::ParamReduction));
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Not owned:
+    // - mSource (probably)
+    // - mDestination (probably)
+    // - Don't count the AudioParamTimelines, their inner refs are owned by the
+    // AudioNode.
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    amount += mCompressor->sizeOfIncludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   void SendReductionParamToMainThread(AudioNodeStream* aStream, float aReduction)
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     class Command : public nsRunnable
     {
     public:
@@ -200,16 +217,34 @@ DynamicsCompressorNode::DynamicsCompress
   , mRelease(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
                             SendReleaseToStream, 0.25f))
 {
   DynamicsCompressorNodeEngine* engine = new DynamicsCompressorNodeEngine(this, aContext->Destination());
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
 }
 
+size_t
+DynamicsCompressorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mThreshold->SizeOfIncludingThis(aMallocSizeOf);
+  amount += mKnee->SizeOfIncludingThis(aMallocSizeOf);
+  amount += mRatio->SizeOfIncludingThis(aMallocSizeOf);
+  amount += mAttack->SizeOfIncludingThis(aMallocSizeOf);
+  amount += mRelease->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+DynamicsCompressorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 DynamicsCompressorNode::WrapObject(JSContext* aCx)
 {
   return DynamicsCompressorNodeBinding::Wrap(aCx, this);
 }
 
 void
 DynamicsCompressorNode::SendThresholdToStream(AudioNode* aNode)
--- a/content/media/webaudio/DynamicsCompressorNode.h
+++ b/content/media/webaudio/DynamicsCompressorNode.h
@@ -51,16 +51,24 @@ public:
   }
 
   // Called GetRelease to prevent clashing with the nsISupports::Release name
   AudioParam* GetRelease() const
   {
     return mRelease;
   }
 
+  virtual const char* NodeType() const
+  {
+    return "DynamicsCompressorNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   static void SendThresholdToStream(AudioNode* aNode);
   static void SendKneeToStream(AudioNode* aNode);
   static void SendRatioToStream(AudioNode* aNode);
   static void SendAttackToStream(AudioNode* aNode);
   static void SendReleaseToStream(AudioNode* aNode);
 
 private:
--- a/content/media/webaudio/FFTBlock.h
+++ b/content/media/webaudio/FFTBlock.h
@@ -123,16 +123,30 @@ public:
   {
     return mOutputBuffer[aIndex].r;
   }
   float ImagData(uint32_t aIndex) const
   {
     return mOutputBuffer[aIndex].i;
   }
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t amount = 0;
+    amount += aMallocSizeOf(mFFT);
+    amount += aMallocSizeOf(mIFFT);
+    amount += mOutputBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   FFTBlock(const FFTBlock& other) MOZ_DELETE;
   void operator=(const FFTBlock& other) MOZ_DELETE;
 
   void EnsureFFT()
   {
     if (!mFFT) {
       mFFT = kiss_fftr_alloc(mFFTSize, 0, nullptr, nullptr);
--- a/content/media/webaudio/GainNode.cpp
+++ b/content/media/webaudio/GainNode.cpp
@@ -96,16 +96,30 @@ public:
         const float* inputBuffer = static_cast<const float*> (aInput.mChannelData[channel]);
         float* buffer = static_cast<float*> (const_cast<void*>
                           (aOutput->mChannelData[channel]));
         AudioBlockCopyChannelWithScale(inputBuffer, computedGain, buffer);
       }
     }
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Not owned:
+    // - mSource (probably)
+    // - mDestination (probably)
+    // - mGain - Internal ref owned by AudioNode
+    return AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   AudioNodeStream* mSource;
   AudioNodeStream* mDestination;
   AudioParamTimeline mGain;
 };
 
 GainNode::GainNode(AudioContext* aContext)
   : AudioNode(aContext,
               2,
@@ -114,16 +128,30 @@ GainNode::GainNode(AudioContext* aContex
   , mGain(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
                          SendGainToStream, 1.0f))
 {
   GainNodeEngine* engine = new GainNodeEngine(this, aContext->Destination());
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
 }
 
+size_t
+GainNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mGain->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+GainNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 GainNode::WrapObject(JSContext* aCx)
 {
   return GainNodeBinding::Wrap(aCx, this);
 }
 
 void
 GainNode::SendGainToStream(AudioNode* aNode)
--- a/content/media/webaudio/GainNode.h
+++ b/content/media/webaudio/GainNode.h
@@ -25,16 +25,24 @@ public:
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   AudioParam* Gain() const
   {
     return mGain;
   }
 
+  virtual const char* NodeType() const
+  {
+    return "GainNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   static void SendGainToStream(AudioNode* aNode);
 
 private:
   nsRefPtr<AudioParam> mGain;
 };
 
 }
--- a/content/media/webaudio/MediaElementAudioSourceNode.h
+++ b/content/media/webaudio/MediaElementAudioSourceNode.h
@@ -14,14 +14,24 @@ namespace dom {
 
 class MediaElementAudioSourceNode : public MediaStreamAudioSourceNode
 {
 public:
   MediaElementAudioSourceNode(AudioContext* aContext,
                               DOMMediaStream* aStream);
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  virtual const char* NodeType() const
+  {
+    return "MediaElementAudioSourceNode";
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
 };
 
 }
 }
 
 #endif
--- a/content/media/webaudio/MediaStreamAudioDestinationNode.cpp
+++ b/content/media/webaudio/MediaStreamAudioDestinationNode.cpp
@@ -43,16 +43,21 @@ public:
   {
     *aOutput = aInput;
     StreamBuffer::Track* track = mOutputStream->EnsureTrack(MEDIA_STREAM_DEST_TRACK_ID,
                                                             aStream->SampleRate());
     AudioSegment* segment = track->Get<AudioSegment>();
     segment->AppendAndConsumeChunk(aOutput);
   }
 
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   ProcessedMediaStream* mOutputStream;
 };
 
 // This callback is used to ensure that only the audio data for this track is audible
 static bool FilterAudioNodeStreamTrack(StreamBuffer::Track* aTrack)
 {
   return aTrack->GetID() == MEDIA_STREAM_DEST_TRACK_ID;
@@ -76,16 +81,32 @@ MediaStreamAudioDestinationNode::MediaSt
   mPort = tus->AllocateInputPort(mStream, 0);
 
   nsIDocument* doc = aContext->GetParentObject()->GetExtantDoc();
   if (doc) {
     mDOMStream->CombineWithPrincipal(doc->NodePrincipal());
   }
 }
 
+size_t
+MediaStreamAudioDestinationNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  // Future:
+  // - mDOMStream
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mPort->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+MediaStreamAudioDestinationNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 void
 MediaStreamAudioDestinationNode::DestroyMediaStream()
 {
   AudioNode::DestroyMediaStream();
   if (mPort) {
     mPort->Destroy();
     mPort = nullptr;
   }
--- a/content/media/webaudio/MediaStreamAudioDestinationNode.h
+++ b/content/media/webaudio/MediaStreamAudioDestinationNode.h
@@ -29,16 +29,24 @@ public:
 
   virtual void DestroyMediaStream() MOZ_OVERRIDE;
 
   DOMMediaStream* DOMStream() const
   {
     return mDOMStream;
   }
 
+  virtual const char* NodeType() const
+  {
+    return "MediaStreamAudioDestinationNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   nsRefPtr<DOMMediaStream> mDOMStream;
   nsRefPtr<MediaInputPort> mPort;
 };
 
 }
 }
 
--- a/content/media/webaudio/MediaStreamAudioSourceNode.cpp
+++ b/content/media/webaudio/MediaStreamAudioSourceNode.cpp
@@ -44,16 +44,32 @@ MediaStreamAudioSourceNode::MediaStreamA
                                                MediaInputPort::FLAG_BLOCK_INPUT);
   mInputStream->AddConsumerToKeepAlive(this);
 }
 
 MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode()
 {
 }
 
+size_t
+MediaStreamAudioSourceNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  // Future:
+  // - mInputStream
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mInputPort->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+MediaStreamAudioSourceNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 void
 MediaStreamAudioSourceNode::DestroyMediaStream()
 {
   if (mInputPort) {
     mInputPort->Destroy();
     mInputPort = nullptr;
   }
   AudioNode::DestroyMediaStream();
--- a/content/media/webaudio/MediaStreamAudioSourceNode.h
+++ b/content/media/webaudio/MediaStreamAudioSourceNode.h
@@ -26,16 +26,24 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamAudioSourceNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   virtual void DestroyMediaStream() MOZ_OVERRIDE;
 
   virtual uint16_t NumberOfInputs() const MOZ_OVERRIDE { return 0; }
 
+  virtual const char* NodeType() const
+  {
+    return "MediaStreamAudioSourceNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   nsRefPtr<MediaInputPort> mInputPort;
   nsRefPtr<DOMMediaStream> mInputStream;
 };
 
 }
 }
 
--- a/content/media/webaudio/OscillatorNode.cpp
+++ b/content/media/webaudio/OscillatorNode.cpp
@@ -458,16 +458,42 @@ public:
         ComputeCustom(output, ticks, start, end);
         break;
       default:
         ComputeSilence(aOutput);
     };
 
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+
+    // Not owned:
+    // - mSource
+    // - mDestination
+    // - mFrequency (internal ref owned by node)
+    // - mDetune (internal ref owned by node)
+
+    if (mCustom) {
+      amount += mCustom->SizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    if (mPeriodicWave) {
+      amount += mPeriodicWave->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   DCBlocker mDCBlocker;
   AudioNodeStream* mSource;
   AudioNodeStream* mDestination;
   TrackTicks mStart;
   TrackTicks mStop;
   AudioParamTimeline mFrequency;
   AudioParamTimeline mDetune;
   OscillatorType mType;
@@ -505,16 +531,34 @@ OscillatorNode::OscillatorNode(AudioCont
   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
   mStream->AddMainThreadListener(this);
 }
 
 OscillatorNode::~OscillatorNode()
 {
 }
 
+size_t
+OscillatorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+
+  // For now only report if we know for sure that it's not shared.
+  amount += mPeriodicWave->SizeOfExcludingThisIfNotShared(aMallocSizeOf);
+  amount += mFrequency->SizeOfIncludingThis(aMallocSizeOf);
+  amount += mDetune->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+OscillatorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 OscillatorNode::WrapObject(JSContext* aCx)
 {
   return OscillatorNodeBinding::Wrap(aCx, this);
 }
 
 void
 OscillatorNode::SendFrequencyToStream(AudioNode* aNode)
--- a/content/media/webaudio/OscillatorNode.h
+++ b/content/media/webaudio/OscillatorNode.h
@@ -112,16 +112,24 @@ public:
     mType = OscillatorType::Custom;
     SendTypeToStream();
   }
 
   IMPL_EVENT_HANDLER(ended)
 
   virtual void NotifyMainThreadStateChanged() MOZ_OVERRIDE;
 
+  virtual const char* NodeType() const
+  {
+    return "OscillatorNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   static void SendFrequencyToStream(AudioNode* aNode);
   static void SendDetuneToStream(AudioNode* aNode);
   void SendTypeToStream();
   void SendPeriodicWaveToStream();
 
 private:
   OscillatorType mType;
--- a/content/media/webaudio/PannerNode.cpp
+++ b/content/media/webaudio/PannerNode.cpp
@@ -183,16 +183,31 @@ public:
 
   void EqualPowerPanningFunction(const AudioChunk& aInput, AudioChunk* aOutput);
   void HRTFPanningFunction(const AudioChunk& aInput, AudioChunk* aOutput);
 
   float LinearGainFunction(float aDistance);
   float InverseGainFunction(float aDistance);
   float ExponentialGainFunction(float aDistance);
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    if (mHRTFPanner) {
+      amount += mHRTFPanner->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
   nsAutoPtr<HRTFPanner> mHRTFPanner;
   typedef void (PannerNodeEngine::*PanningModelFunction)(const AudioChunk& aInput, AudioChunk* aOutput);
   PanningModelFunction mPanningModelFunction;
   typedef float (PannerNodeEngine::*DistanceModelFunction)(float aDistance);
   DistanceModelFunction mDistanceModelFunction;
   ThreeDPoint mPosition;
   ThreeDPoint mOrientation;
   ThreeDPoint mVelocity;
@@ -237,16 +252,30 @@ PannerNode::PannerNode(AudioContext* aCo
 
 PannerNode::~PannerNode()
 {
   if (Context()) {
     Context()->UnregisterPannerNode(this);
   }
 }
 
+size_t
+PannerNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mSources.SizeOfExcludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+PannerNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 PannerNode::WrapObject(JSContext* aCx)
 {
   return PannerNodeBinding::Wrap(aCx, this);
 }
 
 void PannerNode::DestroyMediaStream()
 {
--- a/content/media/webaudio/PannerNode.h
+++ b/content/media/webaudio/PannerNode.h
@@ -240,16 +240,24 @@ public:
     SendDoubleParameterToStream(CONE_OUTER_GAIN, mConeOuterGain);
   }
 
   float ComputeDopplerShift();
   void SendDopplerToSourcesIfNeeded();
   void FindConnectedSources();
   void FindConnectedSources(AudioNode* aNode, nsTArray<AudioBufferSourceNode*>& aSources, std::set<AudioNode*>& aSeenNodes);
 
+  virtual const char* NodeType() const
+  {
+    return "PannerNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   friend class AudioListener;
   friend class PannerNodeEngine;
   enum EngineParameters {
     LISTENER_POSITION,
     LISTENER_FRONT_VECTOR, // unit length
     LISTENER_RIGHT_VECTOR, // unit length, orthogonal to LISTENER_FRONT_VECTOR
     LISTENER_VELOCITY,
--- a/content/media/webaudio/PeriodicWave.cpp
+++ b/content/media/webaudio/PeriodicWave.cpp
@@ -39,16 +39,29 @@ PeriodicWave::PeriodicWave(AudioContext*
     return;
   }
   PodCopy(buffer, aRealData, aLength);
   mCoefficients->SetData(0, buffer, buffer);
   PodCopy(buffer+aLength, aImagData, aLength);
   mCoefficients->SetData(1, nullptr, buffer+aLength);
 }
 
+size_t
+PeriodicWave::SizeOfExcludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const
+{
+  // Not owned:
+  // - mContext
+  size_t amount = 0;
+  if (!mCoefficients->IsShared()) {
+    amount += mCoefficients->SizeOfIncludingThis(aMallocSizeOf);
+  }
+
+  return amount;
+}
+
 JSObject*
 PeriodicWave::WrapObject(JSContext* aCx)
 {
   return PeriodicWaveBinding::Wrap(aCx, this);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/media/webaudio/PeriodicWave.h
+++ b/content/media/webaudio/PeriodicWave.h
@@ -42,16 +42,18 @@ public:
     return mLength;
   }
 
   ThreadSharedFloatArrayBufferList* GetThreadSharedBuffer() const
   {
     return mCoefficients;
   }
 
+  size_t SizeOfExcludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const;
+
 private:
   nsRefPtr<AudioContext> mContext;
   nsRefPtr<ThreadSharedFloatArrayBufferList> mCoefficients;
   uint32_t mLength;
 };
 
 }
 }
--- a/content/media/webaudio/ScriptProcessorNode.cpp
+++ b/content/media/webaudio/ScriptProcessorNode.cpp
@@ -33,17 +33,29 @@ class SharedBuffers
 private:
   class OutputQueue
   {
   public:
     explicit OutputQueue(const char* aName)
       : mMutex(aName)
     {}
 
-    Mutex& Lock() { return mMutex; }
+    size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+    {
+      mMutex.AssertCurrentThreadOwns();
+
+      size_t amount = 0;
+      for (size_t i = 0; i < mBufferList.size(); i++) {
+        amount += mBufferList[i].SizeOfExcludingThis(aMallocSizeOf, false);
+      }
+
+      return amount;
+    }
+
+    Mutex& Lock() const { return const_cast<OutputQueue*>(this)->mMutex; }
 
     size_t ReadyToConsume() const
     {
       mMutex.AssertCurrentThreadOwns();
       MOZ_ASSERT(!NS_IsMainThread());
       return mBufferList.size();
     }
 
@@ -90,16 +102,28 @@ public:
     : mOutputQueue("SharedBuffers::outputQueue")
     , mDelaySoFar(TRACK_TICKS_MAX)
     , mSampleRate(aSampleRate)
     , mLatency(0.0)
     , mDroppingBuffers(false)
   {
   }
 
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t amount = aMallocSizeOf(this);
+
+    {
+      MutexAutoLock lock(mOutputQueue.Lock());
+      amount += mOutputQueue.SizeOfExcludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+  }
+
   // main thread
   void FinishProducingOutputBuffer(ThreadSharedFloatArrayBufferList* aBuffer,
                                    uint32_t aBufferSize)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     TimeStamp now = TimeStamp::Now();
 
@@ -288,16 +312,36 @@ public:
     if (mInputWriteIndex >= mBufferSize) {
       SendBuffersToMainThread(aStream);
       mInputWriteIndex -= mBufferSize;
       mSeenNonSilenceInput = false;
       AllocateInputBlock();
     }
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Not owned:
+    // - mSharedBuffers
+    // - mSource (probably)
+    // - mDestination (probably)
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    amount += mInputChannels.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < mInputChannels.Length(); i++) {
+      amount += mInputChannels[i].SizeOfExcludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   void AllocateInputBlock()
   {
     for (unsigned i = 0; i < mInputChannels.Length(); ++i) {
       if (!mInputChannels[i]) {
         mInputChannels[i] = new float[mBufferSize];
       }
     }
@@ -445,16 +489,30 @@ ScriptProcessorNode::ScriptProcessorNode
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
 }
 
 ScriptProcessorNode::~ScriptProcessorNode()
 {
 }
 
+size_t
+ScriptProcessorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  amount += mSharedBuffers->SizeOfIncludingThis(aMallocSizeOf);
+  return amount;
+}
+
+size_t
+ScriptProcessorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
 JSObject*
 ScriptProcessorNode::WrapObject(JSContext* aCx)
 {
   return ScriptProcessorNodeBinding::Wrap(aCx, this);
 }
 
 }
 }
--- a/content/media/webaudio/ScriptProcessorNode.h
+++ b/content/media/webaudio/ScriptProcessorNode.h
@@ -84,16 +84,24 @@ public:
 
   uint32_t NumberOfOutputChannels() const
   {
     return mNumberOfOutputChannels;
   }
 
   using DOMEventTargetHelper::DispatchTrustedEvent;
 
+  virtual const char* NodeType() const
+  {
+    return "ScriptProcessorNode";
+  }
+
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE;
+
 private:
   nsAutoPtr<SharedBuffers> mSharedBuffers;
   const uint32_t mBufferSize;
   const uint32_t mNumberOfOutputChannels;
 };
 
 }
 }
--- a/content/media/webaudio/WaveShaperNode.cpp
+++ b/content/media/webaudio/WaveShaperNode.cpp
@@ -125,16 +125,26 @@ public:
 
     WebAudioUtils::SpeexResamplerProcess(mDownSampler, aChannel,
                                          inputData, &inSamples,
                                          aOutputData, &outSamples);
 
     MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE*aBlocks && outSamples == WEBAUDIO_BLOCK_SIZE);
   }
 
+  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
+  {
+    size_t amount = 0;
+    // Future: properly measure speex memory
+    amount += aMallocSizeOf(mUpSampler);
+    amount += aMallocSizeOf(mDownSampler);
+    amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
 private:
   void Destroy()
   {
     if (mUpSampler) {
       speex_resampler_destroy(mUpSampler);
       mUpSampler = nullptr;
     }
     if (mDownSampler) {
@@ -239,16 +249,29 @@ public:
         mResampler.DownSample(i, outputBuffer, 4);
         break;
       default:
         NS_NOTREACHED("We should never reach here");
       }
     }
   }
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
+    amount += mCurve.SizeOfExcludingThis(aMallocSizeOf);
+    amount += mResampler.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   nsTArray<float> mCurve;
   OverSampleType mType;
   Resampler mResampler;
 };
 
 WaveShaperNode::WaveShaperNode(AudioContext* aContext)
   : AudioNode(aContext,
--- a/content/media/webaudio/WaveShaperNode.h
+++ b/content/media/webaudio/WaveShaperNode.h
@@ -34,16 +34,33 @@ public:
   void SetCurve(const Nullable<Float32Array>& aData);
 
   OverSampleType Oversample() const
   {
     return mType;
   }
   void SetOversample(OverSampleType aType);
 
+  virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    // Possibly track in the future:
+    // - mCurve
+    return AudioNode::SizeOfExcludingThis(aMallocSizeOf);
+  }
+
+  virtual const char* NodeType() const
+  {
+    return "WaveShaperNode";
+  }
+
+  virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
+  {
+    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+  }
+
 private:
   void ClearCurve();
 
 private:
   JS::Heap<JSObject*> mCurve;
   OverSampleType mType;
 };
 
--- a/content/media/webaudio/blink/DirectConvolver.h
+++ b/content/media/webaudio/blink/DirectConvolver.h
@@ -25,27 +25,36 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef DirectConvolver_h
 #define DirectConvolver_h
 
 #include "nsTArray.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 class DirectConvolver {
 public:
     DirectConvolver(size_t inputBlockSize);
 
     void process(const nsTArray<float>* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess);
 
     void reset();
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+    {
+        size_t amount = aMallocSizeOf(this);
+        amount += m_buffer.SizeOfExcludingThis(aMallocSizeOf);
+        return amount;
+    }
+
+
 private:
     size_t m_inputBlockSize;
 
     nsTArray<float> m_buffer;
 };
 
 } // namespace WebCore
 
--- a/content/media/webaudio/blink/DynamicsCompressor.cpp
+++ b/content/media/webaudio/blink/DynamicsCompressor.cpp
@@ -47,16 +47,39 @@ DynamicsCompressor::DynamicsCompressor(f
     m_lastFilterStageRatio = -1;
     m_lastAnchor = -1;
     m_lastFilterStageGain = -1;
 
     setNumberOfChannels(numberOfChannels);
     initializeParameters();
 }
 
+size_t DynamicsCompressor::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+    amount += m_preFilterPacks.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_preFilterPacks.Length(); i++) {
+        if (m_preFilterPacks[i]) {
+            amount += m_preFilterPacks[i]->sizeOfIncludingThis(aMallocSizeOf);
+        }
+    }
+
+    amount += m_postFilterPacks.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_postFilterPacks.Length(); i++) {
+        if (m_postFilterPacks[i]) {
+            amount += m_postFilterPacks[i]->sizeOfIncludingThis(aMallocSizeOf);
+        }
+    }
+
+    amount += m_sourceChannels.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_destinationChannels.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_compressor.sizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+}
+
 void DynamicsCompressor::setParameterValue(unsigned parameterID, float value)
 {
     MOZ_ASSERT(parameterID < ParamLast);
     if (parameterID < ParamLast)
         m_parameters[parameterID] = value;
 }
 
 void DynamicsCompressor::initializeParameters()
--- a/content/media/webaudio/blink/DynamicsCompressor.h
+++ b/content/media/webaudio/blink/DynamicsCompressor.h
@@ -29,16 +29,17 @@
 #ifndef DynamicsCompressor_h
 #define DynamicsCompressor_h
 
 #include "DynamicsCompressorKernel.h"
 #include "ZeroPole.h"
 
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace mozilla {
 struct AudioChunk;
 }
 
 namespace WebCore {
 
 using mozilla::AudioChunk;
@@ -81,32 +82,38 @@ public:
     float parameterValue(unsigned parameterID);
 
     float sampleRate() const { return m_sampleRate; }
     float nyquist() const { return m_sampleRate / 2; }
 
     double tailTime() const { return 0; }
     double latencyTime() const { return m_compressor.latencyFrames() / static_cast<double>(sampleRate()); }
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 protected:
     unsigned m_numberOfChannels;
 
     // m_parameters holds the tweakable compressor parameters.
     float m_parameters[ParamLast];
     void initializeParameters();
 
     float m_sampleRate;
 
     // Emphasis filter controls.
     float m_lastFilterStageRatio;
     float m_lastAnchor;
     float m_lastFilterStageGain;
 
     typedef struct {
         ZeroPole filters[4];
+        size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+        {
+            return aMallocSizeOf(this);
+        }
     } ZeroPoleFilterPack4;
 
     // Per-channel emphasis filters.
     nsTArray<nsAutoPtr<ZeroPoleFilterPack4> > m_preFilterPacks;
     nsTArray<nsAutoPtr<ZeroPoleFilterPack4> > m_postFilterPacks;
 
     nsAutoArrayPtr<const float*> m_sourceChannels;
     nsAutoArrayPtr<float*> m_destinationChannels;
--- a/content/media/webaudio/blink/DynamicsCompressorKernel.cpp
+++ b/content/media/webaudio/blink/DynamicsCompressorKernel.cpp
@@ -68,16 +68,27 @@ DynamicsCompressorKernel::DynamicsCompre
 
     // Initializes most member variables
     reset();
 
     m_meteringReleaseK =
         static_cast<float>(WebAudioUtils::DiscreteTimeConstantForSampleRate(meteringReleaseTimeConstant, sampleRate));
 }
 
+size_t DynamicsCompressorKernel::sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = 0;
+    amount += m_preDelayBuffers.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_preDelayBuffers.Length(); i++) {
+        amount += m_preDelayBuffers[i].SizeOfExcludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+}
+
 void DynamicsCompressorKernel::setNumberOfChannels(unsigned numberOfChannels)
 {
     if (m_preDelayBuffers.Length() == numberOfChannels)
         return;
 
     m_preDelayBuffers.Clear();
     for (unsigned i = 0; i < numberOfChannels; ++i)
         m_preDelayBuffers.AppendElement(new float[MaxPreDelayFrames]);
--- a/content/media/webaudio/blink/DynamicsCompressorKernel.h
+++ b/content/media/webaudio/blink/DynamicsCompressorKernel.h
@@ -26,16 +26,17 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef DynamicsCompressorKernel_h
 #define DynamicsCompressorKernel_h
 
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 class DynamicsCompressorKernel {
 public:
     DynamicsCompressorKernel(float sampleRate, unsigned numberOfChannels);
 
     void setNumberOfChannels(unsigned);
@@ -64,16 +65,18 @@ public:
     void reset();
 
     unsigned latencyFrames() const { return m_lastPreDelayFrames; }
 
     float sampleRate() const { return m_sampleRate; }
 
     float meteringGain() const { return m_meteringGain; }
 
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 protected:
     float m_sampleRate;
 
     float m_detectorAverage;
     float m_compressorGain;
 
     // Metering
     float m_meteringReleaseK;
--- a/content/media/webaudio/blink/FFTConvolver.cpp
+++ b/content/media/webaudio/blink/FFTConvolver.cpp
@@ -40,16 +40,31 @@ FFTConvolver::FFTConvolver(size_t fftSiz
   m_inputBuffer.SetLength(fftSize);
   PodZero(m_inputBuffer.Elements(), fftSize);
   m_outputBuffer.SetLength(fftSize);
   PodZero(m_outputBuffer.Elements(), fftSize);
   m_lastOverlapBuffer.SetLength(fftSize / 2);
   PodZero(m_lastOverlapBuffer.Elements(), fftSize / 2);
 }
 
+size_t FFTConvolver::sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = 0;
+    amount += m_frame.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_inputBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_outputBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_lastOverlapBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    return amount;
+}
+
+size_t FFTConvolver::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+  return aMallocSizeOf(this) + sizeOfExcludingThis(aMallocSizeOf);
+}
+
 void FFTConvolver::process(FFTBlock* fftKernel, const float* sourceP, float* destP, size_t framesToProcess)
 {
     size_t halfSize = fftSize() / 2;
 
     // framesToProcess must be an exact multiple of halfSize,
     // or halfSize is a multiple of framesToProcess when halfSize > framesToProcess.
     bool isGood = !(halfSize % framesToProcess && framesToProcess % halfSize);
     MOZ_ASSERT(isGood);
--- a/content/media/webaudio/blink/FFTConvolver.h
+++ b/content/media/webaudio/blink/FFTConvolver.h
@@ -26,16 +26,17 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef FFTConvolver_h
 #define FFTConvolver_h
 
 #include "nsTArray.h"
 #include "mozilla/FFTBlock.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 typedef nsTArray<float> AudioFloatArray;
 using mozilla::FFTBlock;
 
 class FFTConvolver {
 public:
@@ -52,16 +53,19 @@ public:
     //
     // Processing in-place is allowed...
     void process(FFTBlock* fftKernel, const float* sourceP, float* destP, size_t framesToProcess);
 
     void reset();
 
     size_t fftSize() const { return m_frame.FFTSize(); }
 
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     FFTBlock m_frame;
 
     // Buffer input until we get fftSize / 2 samples then do an FFT
     size_t m_readWriteIndex;
     AudioFloatArray m_inputBuffer;
 
     // Stores output which we read a little at a time
--- a/content/media/webaudio/blink/HRTFDatabase.cpp
+++ b/content/media/webaudio/blink/HRTFDatabase.cpp
@@ -74,16 +74,27 @@ HRTFDatabase::HRTFDatabase(float sampleR
                 float x = static_cast<float>(jj) / static_cast<float>(InterpolationFactor);
                 m_elevations[i + jj] = HRTFElevation::createByInterpolatingSlices(m_elevations[i].get(), m_elevations[j].get(), x, sampleRate);
                 MOZ_ASSERT(m_elevations[i + jj].get());
             }
         }
     }
 }
 
+size_t HRTFDatabase::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+    amount += m_elevations.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_elevations.Length(); i++) {
+      amount += m_elevations[i]->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+}
+
 void HRTFDatabase::getKernelsFromAzimuthElevation(double azimuthBlend, unsigned azimuthIndex, double elevationAngle, HRTFKernel* &kernelL, HRTFKernel* &kernelR,
                                                   double& frameDelayL, double& frameDelayR)
 {
     unsigned elevationIndex = indexFromElevationAngle(elevationAngle);
     MOZ_ASSERT(elevationIndex < m_elevations.Length() && m_elevations.Length() > 0);
     
     if (!m_elevations.Length()) {
         kernelL = 0;
--- a/content/media/webaudio/blink/HRTFDatabase.h
+++ b/content/media/webaudio/blink/HRTFDatabase.h
@@ -27,16 +27,17 @@
  */
 
 #ifndef HRTFDatabase_h
 #define HRTFDatabase_h
 
 #include "HRTFElevation.h"
 #include "nsAutoRef.h"
 #include "nsTArray.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 class HRTFKernel;
 
 class HRTFDatabase {
 public:
     static nsReturnRef<HRTFDatabase> create(float sampleRate);
@@ -50,16 +51,18 @@ public:
     // Returns the number of different azimuth angles.
     static unsigned numberOfAzimuths() { return HRTFElevation::NumberOfTotalAzimuths; }
 
     float sampleRate() const { return m_sampleRate; }
 
     // Number of elevations loaded from resource.
     static const unsigned NumberOfRawElevations;
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     HRTFDatabase(const HRTFDatabase& other) MOZ_DELETE;
     void operator=(const HRTFDatabase& other) MOZ_DELETE;
 
     explicit HRTFDatabase(float sampleRate);
 
     // Minimum and maximum elevation angles (inclusive) for a HRTFDatabase.
     static const int MinElevation;
--- a/content/media/webaudio/blink/HRTFDatabaseLoader.cpp
+++ b/content/media/webaudio/blink/HRTFDatabaseLoader.cpp
@@ -83,16 +83,30 @@ HRTFDatabaseLoader::~HRTFDatabaseLoader(
         s_loaderMap->RemoveEntry(m_databaseSampleRate);
         if (s_loaderMap->Count() == 0) {
             delete s_loaderMap;
             s_loaderMap = nullptr;
         }
     }
 }
 
+size_t HRTFDatabaseLoader::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+
+    // NB: Need to make sure we're not competing with the loader thread.
+    const_cast<HRTFDatabaseLoader*>(this)->waitForLoaderThreadCompletion();
+
+    if (m_hrtfDatabase) {
+        amount += m_hrtfDatabase->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+}
+
 class HRTFDatabaseLoader::ProxyReleaseEvent MOZ_FINAL : public nsRunnable {
 public:
     explicit ProxyReleaseEvent(HRTFDatabaseLoader* loader) : mLoader(loader) {}
     NS_IMETHOD Run() MOZ_OVERRIDE
     {
         mLoader->MainThreadRelease();
         return NS_OK;
     }
--- a/content/media/webaudio/blink/HRTFDatabaseLoader.h
+++ b/content/media/webaudio/blink/HRTFDatabaseLoader.h
@@ -26,16 +26,17 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef HRTFDatabaseLoader_h
 #define HRTFDatabaseLoader_h
 
 #include "nsHashKeys.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/MemoryReporting.h"
 #include "mozilla/Mutex.h"
 #include "HRTFDatabase.h"
 
 template <class EntryType> class nsTHashtable;
 template <class T> class nsAutoRef;
 
 namespace WebCore {
 
@@ -88,16 +89,18 @@ public:
 
     float databaseSampleRate() const { return m_databaseSampleRate; }
 
     static void shutdown();
     
     // Called in asynchronous loading thread.
     void load();
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     // Both constructor and destructor must be called from the main thread.
     explicit HRTFDatabaseLoader(float sampleRate);
     ~HRTFDatabaseLoader();
     
     void ProxyRelease(); // any thread
     void MainThreadRelease(); // main thread only
     class ProxyReleaseEvent;
--- a/content/media/webaudio/blink/HRTFElevation.cpp
+++ b/content/media/webaudio/blink/HRTFElevation.cpp
@@ -45,16 +45,28 @@ const int numberOfElevations = MOZ_ARRAY
 
 const unsigned HRTFElevation::NumberOfTotalAzimuths = 360 / 15 * 8;
 
 const int rawSampleRate = irc_composite_c_r0195_sample_rate;
 
 // Number of frames in an individual impulse response.
 const size_t ResponseFrameSize = 256;
 
+size_t HRTFElevation::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+
+    amount += m_kernelListL.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_kernelListL.Length(); i++) {
+        amount += m_kernelListL[i]->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+}
+
 size_t HRTFElevation::fftSizeForSampleRate(float sampleRate)
 {
     // The IRCAM HRTF impulse responses were 512 sample-frames @44.1KHz,
     // but these have been truncated to 256 samples.
     // An FFT-size of twice impulse response size is used (for convolution).
     // So for sample rates of 44.1KHz an FFT size of 512 is good.
     // We double the FFT-size only for sample rates at least double this.
     // If the FFT size is too large then the impulse response will be padded
--- a/content/media/webaudio/blink/HRTFElevation.h
+++ b/content/media/webaudio/blink/HRTFElevation.h
@@ -26,16 +26,17 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef HRTFElevation_h
 #define HRTFElevation_h
 
 #include "HRTFKernel.h"
 #include "nsAutoRef.h"
+#include "mozilla/MemoryReporting.h"
 
 struct SpeexResamplerState_;
 typedef struct SpeexResamplerState_ SpeexResamplerState;
 
 namespace WebCore {
 
 // HRTFElevation contains all of the HRTFKernels (one left ear and one right ear per azimuth angle) for a particular elevation.
 
@@ -58,16 +59,18 @@ public:
     // The interpolated delays based on azimuthBlend: 0 -> 1 are returned in frameDelayL and frameDelayR.
     void getKernelsFromAzimuth(double azimuthBlend, unsigned azimuthIndex, HRTFKernel* &kernelL, HRTFKernel* &kernelR, double& frameDelayL, double& frameDelayR);
     
     // Total number of azimuths after interpolation.
     static const unsigned NumberOfTotalAzimuths;
 
     static size_t fftSizeForSampleRate(float sampleRate);
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     HRTFElevation(const HRTFElevation& other) MOZ_DELETE;
     void operator=(const HRTFElevation& other) MOZ_DELETE;
 
     HRTFElevation(HRTFKernelList *kernelListL, int elevation, float sampleRate)
         : m_elevationAngle(elevation)
         , m_sampleRate(sampleRate)
     {
--- a/content/media/webaudio/blink/HRTFKernel.h
+++ b/content/media/webaudio/blink/HRTFKernel.h
@@ -28,16 +28,17 @@
 
 #ifndef HRTFKernel_h
 #define HRTFKernel_h
 
 #include "nsAutoPtr.h"
 #include "nsAutoRef.h"
 #include "nsTArray.h"
 #include "mozilla/FFTBlock.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 using mozilla::FFTBlock;
 
 // HRTF stands for Head-Related Transfer Function.
 // HRTFKernel is a frequency-domain representation of an impulse-response used as part of the spatialized panning system.
 // For a given azimuth / elevation angle there will be one HRTFKernel for the left ear transfer function, and one for the right ear.
@@ -59,16 +60,23 @@ public:
     FFTBlock* fftFrame() { return m_fftFrame.get(); }
     
     size_t fftSize() const { return m_fftFrame->FFTSize(); }
     float frameDelay() const { return m_frameDelay; }
 
     float sampleRate() const { return m_sampleRate; }
     double nyquist() const { return 0.5 * sampleRate(); }
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+    {
+        size_t amount = aMallocSizeOf(this);
+        amount += m_fftFrame->SizeOfIncludingThis(aMallocSizeOf);
+        return amount;
+    }
+
 private:
     HRTFKernel(const HRTFKernel& other) MOZ_DELETE;
     void operator=(const HRTFKernel& other) MOZ_DELETE;
 
     // Note: this is destructive on the passed in |impulseResponse|.
     HRTFKernel(float* impulseResponse, size_t fftSize, float sampleRate);
     
     HRTFKernel(nsAutoPtr<FFTBlock> fftFrame, float frameDelay, float sampleRate)
--- a/content/media/webaudio/blink/HRTFPanner.cpp
+++ b/content/media/webaudio/blink/HRTFPanner.cpp
@@ -65,16 +65,37 @@ HRTFPanner::HRTFPanner(float sampleRate,
     m_tempR2.SetLength(RenderingQuantum);
 }
 
 HRTFPanner::~HRTFPanner()
 {
     MOZ_COUNT_DTOR(HRTFPanner);
 }
 
+size_t HRTFPanner::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+
+    if (m_databaseLoader) {
+        m_databaseLoader->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    amount += m_convolverL1.sizeOfExcludingThis(aMallocSizeOf);
+    amount += m_convolverR1.sizeOfExcludingThis(aMallocSizeOf);
+    amount += m_convolverL2.sizeOfExcludingThis(aMallocSizeOf);
+    amount += m_convolverR2.sizeOfExcludingThis(aMallocSizeOf);
+    amount += m_delayLine.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_tempL1.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_tempL2.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_tempR1.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_tempR2.SizeOfExcludingThis(aMallocSizeOf);
+
+    return amount;
+}
+
 void HRTFPanner::reset()
 {
     m_azimuthIndex1 = UninitializedAzimuth;
     m_azimuthIndex2 = UninitializedAzimuth;
     // m_elevation1 and m_elevation2 are initialized in pan()
     m_crossfadeSelection = CrossfadeSelection1;
     m_crossfadeX = 0.0f;
     m_crossfadeIncr = 0.0f;
--- a/content/media/webaudio/blink/HRTFPanner.h
+++ b/content/media/webaudio/blink/HRTFPanner.h
@@ -22,16 +22,17 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef HRTFPanner_h
 #define HRTFPanner_h
 
 #include "FFTConvolver.h"
 #include "DelayBuffer.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace mozilla {
 struct AudioChunk;
 }
 
 namespace WebCore {
 
 class HRTFDatabaseLoader;
@@ -48,16 +49,18 @@ public:
     void reset();
 
     size_t fftSize() const { return m_convolverL1.fftSize(); }
 
     float sampleRate() const { return m_sampleRate; }
 
     int maxTailFrames() const;
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     // Given an azimuth angle in the range -180 -> +180, returns the corresponding azimuth index for the database,
     // and azimuthBlend which is an interpolation value from 0 -> 1.
     int calculateDesiredAzimuthIndexAndBlend(double azimuth, double& azimuthBlend);
 
     mozilla::RefPtr<HRTFDatabaseLoader> m_databaseLoader;
 
     float m_sampleRate;
--- a/content/media/webaudio/blink/PeriodicWave.cpp
+++ b/content/media/webaudio/blink/PeriodicWave.cpp
@@ -90,16 +90,30 @@ PeriodicWave::PeriodicWave(float sampleR
     , m_numberOfRanges(NumberOfRanges)
     , m_centsPerRange(CentsPerRange)
 {
     float nyquist = 0.5 * m_sampleRate;
     m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
     m_rateScale = m_periodicWaveSize / m_sampleRate;
 }
 
+size_t PeriodicWave::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+
+    amount += m_bandLimitedTables.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_bandLimitedTables.Length(); i++) {
+        if (m_bandLimitedTables[i]) {
+            amount += m_bandLimitedTables[i]->SizeOfIncludingThis(aMallocSizeOf);
+        }
+    }
+
+    return amount;
+}
+
 void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor)
 {
     // Negative frequencies are allowed, in which case we alias
     // to the positive frequency.
     fundamentalFrequency = fabsf(fundamentalFrequency);
 
     // Calculate the pitch range.
     float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5;
--- a/content/media/webaudio/blink/PeriodicWave.h
+++ b/content/media/webaudio/blink/PeriodicWave.h
@@ -27,16 +27,17 @@
  */
 
 #ifndef PeriodicWave_h
 #define PeriodicWave_h
 
 #include "mozilla/dom/OscillatorNodeBinding.h"
 #include <nsAutoPtr.h>
 #include <nsTArray.h>
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 typedef nsTArray<float> AudioFloatArray;
 
 class PeriodicWave {
 public:
     static PeriodicWave* createSine(float sampleRate);
@@ -63,16 +64,18 @@ public:
 
     // Returns the scalar multiplier to the oscillator frequency to calculate
     // wave buffer phase increment.
     float rateScale() const { return m_rateScale; }
 
     unsigned periodicWaveSize() const { return m_periodicWaveSize; }
     float sampleRate() const { return m_sampleRate; }
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     explicit PeriodicWave(float sampleRate);
 
     void generateBasicWaveform(mozilla::dom::OscillatorType);
 
     float m_sampleRate;
     unsigned m_periodicWaveSize;
     unsigned m_numberOfRanges;
--- a/content/media/webaudio/blink/Reverb.cpp
+++ b/content/media/webaudio/blink/Reverb.cpp
@@ -100,16 +100,31 @@ Reverb::Reverb(ThreadSharedFloatArrayBuf
             }
         }
     }
 
     initialize(irChannels, impulseResponseBufferLength, renderSliceSize,
                maxFFTSize, numberOfChannels, useBackgroundThreads);
 }
 
+size_t Reverb::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+    amount += m_convolvers.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_convolvers.Length(); i++) {
+        if (m_convolvers[i]) {
+            amount += m_convolvers[i]->sizeOfIncludingThis(aMallocSizeOf);
+        }
+    }
+
+    amount += m_tempBuffer.SizeOfExcludingThis(aMallocSizeOf, false);
+    return amount;
+}
+
+
 void Reverb::initialize(const nsTArray<const float*>& impulseResponseBuffer,
                         size_t impulseResponseBufferLength, size_t renderSliceSize,
                         size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads)
 {
     m_impulseResponseLength = impulseResponseBufferLength;
 
     // The reverb can handle a mono impulse response and still do stereo processing
     size_t numResponseChannels = impulseResponseBuffer.Length();
--- a/content/media/webaudio/blink/Reverb.h
+++ b/content/media/webaudio/blink/Reverb.h
@@ -28,16 +28,17 @@
 
 #ifndef Reverb_h
 #define Reverb_h
 
 #include "ReverbConvolver.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "AudioSegment.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace mozilla {
 class ThreadSharedFloatArrayBufferList;
 }
 
 namespace WebCore {
 
 class DirectConvolver;
@@ -53,16 +54,18 @@ public:
     Reverb(mozilla::ThreadSharedFloatArrayBufferList* impulseResponseBuffer, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize, float sampleRate);
 
     void process(const mozilla::AudioChunk* sourceBus, mozilla::AudioChunk* destinationBus, size_t framesToProcess);
     void reset();
 
     size_t impulseResponseLength() const { return m_impulseResponseLength; }
     size_t latencyFrames() const;
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     void initialize(const nsTArray<const float*>& impulseResponseBuffer, size_t impulseResponseBufferLength, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads);
 
     size_t m_impulseResponseLength;
 
     nsTArray<nsAutoPtr<ReverbConvolver> > m_convolvers;
 
     // For "True" stereo processing
--- a/content/media/webaudio/blink/ReverbAccumulationBuffer.h
+++ b/content/media/webaudio/blink/ReverbAccumulationBuffer.h
@@ -25,16 +25,17 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef ReverbAccumulationBuffer_h
 #define ReverbAccumulationBuffer_h
 
 #include "nsTArray.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 typedef nsTArray<float> AudioFloatArray;
 
 // ReverbAccumulationBuffer is a circular delay buffer with one client reading from it and multiple clients
 // writing/accumulating to it at different delay offsets from the read position.  The read operation will zero the memory
 // just read from the buffer, so it will be ready for accumulation the next time around.
@@ -53,16 +54,21 @@ public:
 
     size_t readIndex() const { return m_readIndex; }
     void updateReadIndex(int* readIndex, size_t numberOfFrames) const;
 
     size_t readTimeFrame() const { return m_readTimeFrame; }
 
     void reset();
 
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+    {
+        return m_buffer.SizeOfExcludingThis(aMallocSizeOf);
+    }
+
 private:
     AudioFloatArray m_buffer;
     size_t m_readIndex;
     size_t m_readTimeFrame; // for debugging (frame on continuous timeline)
 };
 
 } // namespace WebCore
 
--- a/content/media/webaudio/blink/ReverbConvolver.cpp
+++ b/content/media/webaudio/blink/ReverbConvolver.cpp
@@ -146,16 +146,45 @@ ReverbConvolver::~ReverbConvolver()
             m_moreInputBuffered = true;
             m_backgroundThreadCondition.Signal();
         }
 
         m_backgroundThread.Stop();
     }
 }
 
+size_t ReverbConvolver::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+    amount += m_stages.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_stages.Length(); i++) {
+        if (m_stages[i]) {
+            amount += m_stages[i]->sizeOfIncludingThis(aMallocSizeOf);
+        }
+    }
+
+    amount += m_backgroundStages.SizeOfExcludingThis(aMallocSizeOf);
+    for (size_t i = 0; i < m_backgroundStages.Length(); i++) {
+        if (m_backgroundStages[i]) {
+            amount += m_backgroundStages[i]->sizeOfIncludingThis(aMallocSizeOf);
+        }
+    }
+
+    // NB: The buffer sizes are static, so even though they might be accessed
+    //     in another thread it's safe to measure them.
+    amount += m_accumulationBuffer.sizeOfExcludingThis(aMallocSizeOf);
+    amount += m_inputBuffer.sizeOfExcludingThis(aMallocSizeOf);
+
+    // Possible future measurements:
+    // - m_backgroundThread
+    // - m_backgroundThreadLock
+    // - m_backgroundThreadCondition
+    return amount;
+}
+
 void ReverbConvolver::backgroundThreadEntry()
 {
     while (!m_wantsToExit) {
         // Wait for realtime thread to give us more input
         m_moreInputBuffered = false;
         {
             AutoLock locker(m_backgroundThreadLock);
             while (!m_moreInputBuffered && !m_wantsToExit)
--- a/content/media/webaudio/blink/ReverbConvolver.h
+++ b/content/media/webaudio/blink/ReverbConvolver.h
@@ -27,16 +27,17 @@
  */
 
 #ifndef ReverbConvolver_h
 #define ReverbConvolver_h
 
 #include "ReverbAccumulationBuffer.h"
 #include "ReverbInputBuffer.h"
 #include "nsAutoPtr.h"
+#include "mozilla/MemoryReporting.h"
 #ifdef LOG
 #undef LOG
 #endif
 #include "base/condition_variable.h"
 #include "base/lock.h"
 #include "base/thread.h"
 
 namespace WebCore {
@@ -60,16 +61,18 @@ public:
     size_t impulseResponseLength() const { return m_impulseResponseLength; }
 
     ReverbInputBuffer* inputBuffer() { return &m_inputBuffer; }
 
     bool useBackgroundThreads() const { return m_useBackgroundThreads; }
     void backgroundThreadEntry();
 
     size_t latencyFrames() const;
+
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 private:
     nsTArray<nsAutoPtr<ReverbConvolverStage> > m_stages;
     nsTArray<nsAutoPtr<ReverbConvolverStage> > m_backgroundStages;
     size_t m_impulseResponseLength;
 
     ReverbAccumulationBuffer m_accumulationBuffer;
 
     // One or more background threads read from this input buffer which is fed from the realtime thread.
--- a/content/media/webaudio/blink/ReverbConvolverStage.cpp
+++ b/content/media/webaudio/blink/ReverbConvolverStage.cpp
@@ -82,16 +82,39 @@ ReverbConvolverStage::ReverbConvolverSta
     m_framesProcessed = 0; // total frames processed so far
 
     size_t delayBufferSize = m_preDelayLength < fftSize ? fftSize : m_preDelayLength;
     delayBufferSize = delayBufferSize < renderSliceSize ? renderSliceSize : delayBufferSize;
     m_preDelayBuffer.SetLength(delayBufferSize);
     PodZero(m_preDelayBuffer.Elements(), m_preDelayBuffer.Length());
 }
 
+size_t ReverbConvolverStage::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+    size_t amount = aMallocSizeOf(this);
+
+    if (m_fftKernel) {
+        amount += m_fftKernel->SizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    if (m_fftConvolver) {
+        amount += m_fftConvolver->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    amount += m_preDelayBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_temporaryBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    amount += m_directKernel.SizeOfExcludingThis(aMallocSizeOf);
+
+    if (m_directConvolver) {
+        amount += m_directConvolver->sizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    return amount;
+}
+
 void ReverbConvolverStage::processInBackground(ReverbConvolver* convolver, size_t framesToProcess)
 {
     ReverbInputBuffer* inputBuffer = convolver->inputBuffer();
     float* source = inputBuffer->directReadFrom(&m_inputReadIndex, framesToProcess);
     process(source, framesToProcess);
 }
 
 void ReverbConvolverStage::process(const float* source, size_t framesToProcess)
--- a/content/media/webaudio/blink/ReverbConvolverStage.h
+++ b/content/media/webaudio/blink/ReverbConvolverStage.h
@@ -29,16 +29,17 @@
 #ifndef ReverbConvolverStage_h
 #define ReverbConvolverStage_h
 
 #include "DirectConvolver.h"
 #include "FFTConvolver.h"
 
 #include "nsTArray.h"
 #include "mozilla/FFTBlock.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 using mozilla::FFTBlock;
 
 class ReverbAccumulationBuffer;
 class ReverbConvolver;
 
@@ -55,16 +56,18 @@ public:
 
     void processInBackground(ReverbConvolver* convolver, size_t framesToProcess);
 
     void reset();
 
     // Useful for background processing
     int inputReadIndex() const { return m_inputReadIndex; }
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
 private:
     nsAutoPtr<FFTBlock> m_fftKernel;
     nsAutoPtr<FFTConvolver> m_fftConvolver;
 
     nsTArray<float> m_preDelayBuffer;
 
     ReverbAccumulationBuffer* m_accumulationBuffer;
     int m_accumulationReadIndex;
--- a/content/media/webaudio/blink/ReverbInputBuffer.h
+++ b/content/media/webaudio/blink/ReverbInputBuffer.h
@@ -25,16 +25,17 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef ReverbInputBuffer_h
 #define ReverbInputBuffer_h
 
 #include "nsTArray.h"
+#include "mozilla/MemoryReporting.h"
 
 namespace WebCore {
 
 // ReverbInputBuffer is used to buffer input samples for deferred processing by the background threads.
 class ReverbInputBuffer {
 public:
     ReverbInputBuffer(size_t length);
 
@@ -49,16 +50,22 @@ public:
     // The individual background threads read here (and hope that they can keep up with the buffer writing).
     // readIndex is updated with the next readIndex to read from...
     // The assumption is that the buffer's length is evenly divisible by numberOfFrames.
     // FIXME: remove numberOfFrames restriction...
     float* directReadFrom(int* readIndex, size_t numberOfFrames);
 
     void reset();
 
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+    {
+        return m_buffer.SizeOfExcludingThis(aMallocSizeOf);
+    }
+
+
 private:
     nsTArray<float> m_buffer;
     size_t m_writeIndex;
 };
 
 } // namespace WebCore
 
 #endif // ReverbInputBuffer_h