Bug 1267918 - Add GMPCrashHelper for HTMLMediaElement. r=gerald draft
authorChris Pearce <cpearce@mozilla.com>
Wed, 29 Jun 2016 11:42:07 +1200
changeset 383060 72ab6f52524325c468e0d0f3b499f82c112b771f
parent 383059 364891f5fc7d399e47d51e5b2ce1ed576581340e
child 383061 0de63512801599bd81552375343da63dbdb66169
push id21911
push usercpearce@mozilla.com
push dateFri, 01 Jul 2016 04:05:57 +0000
reviewersgerald
bugs1267918
milestone50.0a1
Bug 1267918 - Add GMPCrashHelper for HTMLMediaElement. r=gerald This ensures that unencrypted GMP decoding crash reporting works. MozReview-Commit-ID: 84TAV5F9Ie0
dom/html/HTMLMediaElement.h
dom/media/AbstractMediaDecoder.h
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
dom/media/platforms/PlatformDecoderModule.h
dom/media/platforms/agnostic/gmp/GMPAudioDecoder.cpp
dom/media/platforms/agnostic/gmp/GMPAudioDecoder.h
dom/media/platforms/agnostic/gmp/GMPVideoDecoder.cpp
dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
dom/media/platforms/wrappers/H264Converter.cpp
dom/media/platforms/wrappers/H264Converter.h
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -13,16 +13,17 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsIObserver.h"
 #include "mozilla/CORSMode.h"
 #include "DecoderTraits.h"
 #include "nsIAudioChannelAgent.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/TextTrackManager.h"
+#include "mozilla/WeakPtr.h"
 #include "MediaDecoder.h"
 #ifdef MOZ_EME
 #include "mozilla/dom/MediaKeys.h"
 #endif
 #include "mozilla/StateWatching.h"
 #include "nsGkAtoms.h"
 #include "PrincipalChangeObserver.h"
 
@@ -76,29 +77,32 @@ class MediaSource;
 class TextTrackList;
 class AudioTrackList;
 class VideoTrackList;
 
 class HTMLMediaElement : public nsGenericHTMLElement,
                          public nsIDOMHTMLMediaElement,
                          public MediaDecoderOwner,
                          public nsIAudioChannelAgentCallback,
-                         public PrincipalChangeObserver<DOMMediaStream>
+                         public PrincipalChangeObserver<DOMMediaStream>,
+                         public SupportsWeakPtr<HTMLMediaElement>
 {
   friend AutoNotifyAudioChannelAgent;
 
 public:
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::layers::ImageContainer ImageContainer;
   typedef mozilla::VideoFrameContainer VideoFrameContainer;
   typedef mozilla::MediaStream MediaStream;
   typedef mozilla::MediaResource MediaResource;
   typedef mozilla::MediaDecoderOwner MediaDecoderOwner;
   typedef mozilla::MetadataTags MetadataTags;
 
+  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(HTMLMediaElement)
+
   CORSMode GetCORSMode() {
     return mCORSMode;
   }
 
   explicit HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
   /**
    * This is used when the browser is constructing a video element to play
--- a/dom/media/AbstractMediaDecoder.h
+++ b/dom/media/AbstractMediaDecoder.h
@@ -11,16 +11,18 @@
 #include "mozilla/StateMirroring.h"
 
 #include "MediaEventSource.h"
 #include "MediaInfo.h"
 #include "nsISupports.h"
 #include "nsDataHashtable.h"
 #include "nsThreadUtils.h"
 
+class GMPCrashHelper;
+
 namespace mozilla
 {
 
 namespace layers
 {
   class ImageContainer;
 } // namespace layers
 class MediaResource;
@@ -84,16 +86,18 @@ public:
 
   // Returns the owner of this media decoder. The owner should only be used
   // on the main thread.
   virtual MediaDecoderOwner* GetOwner() = 0;
 
   // Set by Reader if the current audio track can be offloaded
   virtual void SetPlatformCanOffloadAudio(bool aCanOffloadAudio) {}
 
+  virtual already_AddRefed<GMPCrashHelper> GetCrashHelper() { return nullptr; }
+
   // Stack based class to assist in notifying the frame statistics of
   // parsed and decoded frames. Use inside video demux & decode functions
   // to ensure all parsed and decoded frames are reported on all return paths.
   class AutoNotifyDecoded {
   public:
     explicit AutoNotifyDecoded(AbstractMediaDecoder* aDecoder)
       : mParsed(0), mDecoded(0), mDropped(0), mDecoder(aDecoder) {}
     ~AutoNotifyDecoded() {
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -25,16 +25,17 @@
 #include "mozilla/dom/AudioTrack.h"
 #include "mozilla/dom/AudioTrackList.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/VideoTrack.h"
 #include "mozilla/dom/VideoTrackList.h"
 #include "nsPrintfCString.h"
 #include "mozilla/Telemetry.h"
+#include "GMPService.h"
 
 #ifdef MOZ_ANDROID_OMX
 #include "AndroidBridge.h"
 #endif
 
 using namespace mozilla::dom;
 using namespace mozilla::layers;
 using namespace mozilla::media;
@@ -1103,16 +1104,44 @@ MediaDecoder::IsEndedOrShutdown() const
 
 bool
 MediaDecoder::OwnerHasError() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mShuttingDown || mOwner->HasError();
 }
 
+class MediaElementGMPCrashHelper : public GMPCrashHelper
+{
+public:
+  explicit MediaElementGMPCrashHelper(HTMLMediaElement* aElement)
+    : mElement(aElement)
+  {
+    MOZ_ASSERT(NS_IsMainThread()); // WeakPtr isn't thread safe.
+  }
+  already_AddRefed<nsPIDOMWindowInner> GetPluginCrashedEventTarget() override
+  {
+    MOZ_ASSERT(NS_IsMainThread()); // WeakPtr isn't thread safe.
+    if (!mElement) {
+      return nullptr;
+    }
+    return do_AddRef(mElement->OwnerDoc()->GetInnerWindow());
+  }
+private:
+  WeakPtr<HTMLMediaElement> mElement;
+};
+
+already_AddRefed<GMPCrashHelper>
+MediaDecoder::GetCrashHelper()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  return mOwner->GetMediaElement() ?
+    MakeAndAddRef<MediaElementGMPCrashHelper>(mOwner->GetMediaElement()) : nullptr;
+}
+
 bool
 MediaDecoder::IsEnded() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mPlayState == PLAY_STATE_ENDED ||
          (mWasEndedWhenEnteredDormant && (mPlayState != PLAY_STATE_SHUTDOWN));
 }
 
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -238,16 +238,18 @@ public:
   // has shutdown.
   // Call on the main thread only.
   virtual bool IsEndedOrShutdown() const;
 
   // Return true if the MediaDecoderOwner's error attribute is not null.
   // If the MediaDecoder is shutting down, OwnerHasError will return true.
   bool OwnerHasError() const;
 
+  already_AddRefed<GMPCrashHelper> GetCrashHelper() override;
+
 protected:
   // Updates the media duration. This is called while the media is being
   // played, calls before the media has reached loaded metadata are ignored.
   // The duration is assumed to be an estimate, and so a degree of
   // instability is expected; if the incoming duration is not significantly
   // different from the existing duration, the change request is ignored.
   // If the incoming duration is significantly different, the duration is
   // changed, this causes a durationchanged event to fire to the media
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -173,16 +173,20 @@ MediaFormatReader::Init()
 
   InitLayersBackendType();
 
   mAudio.mTaskQueue =
     new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER));
   mVideo.mTaskQueue =
     new TaskQueue(GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER));
 
+  // Note: GMPCrashHelper must be created on main thread, as it may use
+  // weak references, which aren't threadsafe.
+  mCrashHelper = mDecoder->GetCrashHelper();
+
   return NS_OK;
 }
 
 #ifdef MOZ_EME
 class DispatchKeyNeededEvent : public Runnable {
 public:
   DispatchKeyNeededEvent(AbstractMediaDecoder* aDecoder,
                          nsTArray<uint8_t>& aInitData,
@@ -406,30 +410,32 @@ MediaFormatReader::EnsureDecoderCreated(
 
   MonitorAutoLock mon(decoder.mMonitor);
 
   switch (aTrack) {
     case TrackType::kAudioTrack: {
       decoder.mDecoder = mPlatform->CreateDecoder({
         decoder.mInfo ? *decoder.mInfo->GetAsAudioInfo() : mInfo.mAudio,
         decoder.mTaskQueue,
-        decoder.mCallback.get()
+        decoder.mCallback.get(),
+        mCrashHelper
       });
       break;
     }
 
     case TrackType::kVideoTrack: {
       // Decoders use the layers backend to decide if they can use hardware decoding,
       // so specify LAYERS_NONE if we want to forcibly disable it.
       decoder.mDecoder = mPlatform->CreateDecoder({
         mVideo.mInfo ? *mVideo.mInfo->GetAsVideoInfo() : mInfo.mVideo,
         decoder.mTaskQueue,
         decoder.mCallback.get(),
         mLayersBackendType,
         GetImageContainer(),
+        mCrashHelper
       });
       break;
     }
     default:
       break;
   }
   if (decoder.mDecoder ) {
     decoder.mDescription = decoder.mDecoder->GetDescriptionName();
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -566,13 +566,14 @@ private:
   MozPromiseHolder<SeekPromise> mSeekPromise;
 
   RefPtr<VideoFrameContainer> mVideoFrameContainer;
   layers::ImageContainer* GetImageContainer();
 
 #ifdef MOZ_EME
   RefPtr<CDMProxy> mCDMProxy;
 #endif
+  RefPtr<GMPCrashHelper> mCrashHelper;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/PlatformDecoderModule.h
+++ b/dom/media/platforms/PlatformDecoderModule.h
@@ -7,16 +7,17 @@
 #if !defined(PlatformDecoderModule_h_)
 #define PlatformDecoderModule_h_
 
 #include "MediaDecoderReader.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "nsTArray.h"
 #include "mozilla/RefPtr.h"
+#include "GMPService.h"
 #include <queue>
 
 namespace mozilla {
 class TrackInfo;
 class AudioInfo;
 class VideoInfo;
 class MediaRawData;
 class DecoderDoctorDiagnostics;
@@ -57,23 +58,25 @@ struct CreateDecoderParams {
   }
 
   const TrackInfo& mConfig;
   TaskQueue* mTaskQueue = nullptr;
   MediaDataDecoderCallback* mCallback = nullptr;
   DecoderDoctorDiagnostics* mDiagnostics = nullptr;
   layers::ImageContainer* mImageContainer = nullptr;
   layers::LayersBackend mLayersBackend = layers::LayersBackend::LAYERS_NONE;
+  RefPtr<GMPCrashHelper> mCrashHelper;
 
 private:
   void Set(TaskQueue* aTaskQueue) { mTaskQueue = aTaskQueue; }
   void Set(MediaDataDecoderCallback* aCallback) { mCallback = aCallback; }
   void Set(DecoderDoctorDiagnostics* aDiagnostics) { mDiagnostics = aDiagnostics; }
   void Set(layers::ImageContainer* aImageContainer) { mImageContainer = aImageContainer; }
   void Set(layers::LayersBackend aLayersBackend) { mLayersBackend = aLayersBackend; }
+  void Set(GMPCrashHelper* aCrashHelper) { mCrashHelper = aCrashHelper; }
   template <typename T1, typename T2, typename... Ts>
   void Set(T1 a1, T2 a2, Ts... as)
   {
     // Parameter pack expansion trick, to call Set() on each argument.
     using expander = int[];
     (void)expander {
       (Set(a1), 0), (Set(a2), 0), (Set(as), 0)...
     };
--- a/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.cpp
@@ -126,16 +126,17 @@ AudioCallbackAdapter::Terminated()
   mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
 }
 
 GMPAudioDecoderParams::GMPAudioDecoderParams(const CreateDecoderParams& aParams)
   : mConfig(aParams.AudioConfig())
   , mTaskQueue(aParams.mTaskQueue)
   , mCallback(nullptr)
   , mAdapter(nullptr)
+  , mCrashHelper(aParams.mCrashHelper)
 {}
 
 GMPAudioDecoderParams&
 GMPAudioDecoderParams::WithCallback(MediaDataDecoderProxy* aWrapper)
 {
   MOZ_ASSERT(aWrapper);
   MOZ_ASSERT(!mCallback); // Should only be called once per instance.
   mCallback = aWrapper->Callback();
@@ -153,16 +154,17 @@ GMPAudioDecoderParams::WithAdapter(Audio
   return *this;
 }
 
 GMPAudioDecoder::GMPAudioDecoder(const GMPAudioDecoderParams& aParams)
   : mConfig(aParams.mConfig)
   , mCallback(aParams.mCallback)
   , mGMP(nullptr)
   , mAdapter(aParams.mAdapter)
+  , mCrashHelper(aParams.mCrashHelper)
 {
   MOZ_ASSERT(!mAdapter || mCallback == mAdapter->Callback());
   if (!mAdapter) {
     mAdapter = new AudioCallbackAdapter(mCallback);
   }
 }
 
 void
@@ -225,17 +227,17 @@ GMPAudioDecoder::Init()
   mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   MOZ_ASSERT(mMPS);
 
   RefPtr<InitPromise> promise(mInitPromise.Ensure(__func__));
 
   nsTArray<nsCString> tags;
   InitTags(tags);
   UniquePtr<GetGMPAudioDecoderCallback> callback(new GMPInitDoneCallback(this));
-  if (NS_FAILED(mMPS->GetGMPAudioDecoder(nullptr, &tags, GetNodeId(), Move(callback)))) {
+  if (NS_FAILED(mMPS->GetGMPAudioDecoder(mCrashHelper, &tags, GetNodeId(), Move(callback)))) {
     mInitPromise.Reject(MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
   }
 
   return promise;
 }
 
 nsresult
 GMPAudioDecoder::Input(MediaRawData* aSample)
--- a/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.h
+++ b/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.h
@@ -52,16 +52,17 @@ struct GMPAudioDecoderParams {
   explicit GMPAudioDecoderParams(const CreateDecoderParams& aParams);
   GMPAudioDecoderParams& WithCallback(MediaDataDecoderProxy* aWrapper);
   GMPAudioDecoderParams& WithAdapter(AudioCallbackAdapter* aAdapter);
 
   const AudioInfo& mConfig;
   TaskQueue* mTaskQueue;
   MediaDataDecoderCallbackProxy* mCallback;
   AudioCallbackAdapter* mAdapter;
+  RefPtr<GMPCrashHelper> mCrashHelper;
 };
 
 class GMPAudioDecoder : public MediaDataDecoder {
 public:
   explicit GMPAudioDecoder(const GMPAudioDecoderParams& aParams);
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
@@ -98,13 +99,14 @@ private:
   void GMPInitDone(GMPAudioDecoderProxy* aGMP);
 
   const AudioInfo mConfig;
   MediaDataDecoderCallbackProxy* mCallback;
   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
   GMPAudioDecoderProxy* mGMP;
   nsAutoPtr<AudioCallbackAdapter> mAdapter;
   MozPromiseHolder<InitPromise> mInitPromise;
+  RefPtr<GMPCrashHelper> mCrashHelper;
 };
 
 } // namespace mozilla
 
 #endif // GMPAudioDecoder_h_
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.cpp
@@ -106,16 +106,17 @@ VideoCallbackAdapter::Terminated()
 
 GMPVideoDecoderParams::GMPVideoDecoderParams(const CreateDecoderParams& aParams)
   : mConfig(aParams.VideoConfig())
   , mTaskQueue(aParams.mTaskQueue)
   , mCallback(nullptr)
   , mAdapter(nullptr)
   , mImageContainer(aParams.mImageContainer)
   , mLayersBackend(aParams.mLayersBackend)
+  , mCrashHelper(aParams.mCrashHelper)
 {}
 
 GMPVideoDecoderParams&
 GMPVideoDecoderParams::WithCallback(MediaDataDecoderProxy* aWrapper)
 {
   MOZ_ASSERT(aWrapper);
   MOZ_ASSERT(!mCallback); // Should only be called once per instance.
   mCallback = aWrapper->Callback();
@@ -135,16 +136,17 @@ GMPVideoDecoderParams::WithAdapter(Video
 
 GMPVideoDecoder::GMPVideoDecoder(const GMPVideoDecoderParams& aParams)
   : mConfig(aParams.mConfig)
   , mCallback(aParams.mCallback)
   , mGMP(nullptr)
   , mHost(nullptr)
   , mAdapter(aParams.mAdapter)
   , mConvertNALUnitLengths(false)
+  , mCrashHelper(aParams.mCrashHelper)
 {
   MOZ_ASSERT(!mAdapter || mCallback == mAdapter->Callback());
   if (!mAdapter) {
     mAdapter = new VideoCallbackAdapter(mCallback,
                                         VideoInfo(mConfig.mDisplay.width,
                                                   mConfig.mDisplay.height),
                                         aParams.mImageContainer);
   }
@@ -276,17 +278,17 @@ GMPVideoDecoder::Init()
   mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   MOZ_ASSERT(mMPS);
 
   RefPtr<InitPromise> promise(mInitPromise.Ensure(__func__));
 
   nsTArray<nsCString> tags;
   InitTags(tags);
   UniquePtr<GetGMPVideoDecoderCallback> callback(new GMPInitDoneCallback(this));
-  if (NS_FAILED(mMPS->GetGMPVideoDecoder(nullptr, &tags, GetNodeId(), Move(callback)))) {
+  if (NS_FAILED(mMPS->GetGMPVideoDecoder(mCrashHelper, &tags, GetNodeId(), Move(callback)))) {
     mInitPromise.Reject(MediaDataDecoder::DecoderFailureReason::INIT_ERROR, __func__);
   }
 
   return promise;
 }
 
 nsresult
 GMPVideoDecoder::Input(MediaRawData* aSample)
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
@@ -57,16 +57,17 @@ struct GMPVideoDecoderParams {
   GMPVideoDecoderParams& WithAdapter(VideoCallbackAdapter* aAdapter);
 
   const VideoInfo& mConfig;
   TaskQueue* mTaskQueue;
   MediaDataDecoderCallbackProxy* mCallback;
   VideoCallbackAdapter* mAdapter;
   layers::ImageContainer* mImageContainer;
   layers::LayersBackend mLayersBackend;
+  RefPtr<GMPCrashHelper> mCrashHelper;
 };
 
 class GMPVideoDecoder : public MediaDataDecoder {
 public:
   explicit GMPVideoDecoder(const GMPVideoDecoderParams& aParams);
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
@@ -106,13 +107,14 @@ private:
   const VideoInfo mConfig;
   MediaDataDecoderCallbackProxy* mCallback;
   nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
   GMPVideoDecoderProxy* mGMP;
   GMPVideoHost* mHost;
   nsAutoPtr<VideoCallbackAdapter> mAdapter;
   bool mConvertNALUnitLengths;
   MozPromiseHolder<InitPromise> mInitPromise;
+  RefPtr<GMPCrashHelper> mCrashHelper;
 };
 
 } // namespace mozilla
 
 #endif // GMPVideoDecoder_h_
--- a/dom/media/platforms/wrappers/H264Converter.cpp
+++ b/dom/media/platforms/wrappers/H264Converter.cpp
@@ -20,16 +20,17 @@ H264Converter::H264Converter(PlatformDec
   : mPDM(aPDM)
   , mOriginalConfig(aParams.VideoConfig())
   , mCurrentConfig(aParams.VideoConfig())
   , mLayersBackend(aParams.mLayersBackend)
   , mImageContainer(aParams.mImageContainer)
   , mTaskQueue(aParams.mTaskQueue)
   , mCallback(aParams.mCallback)
   , mDecoder(nullptr)
+  , mGMPCrashHelper(aParams.mCrashHelper)
   , mNeedAVCC(aPDM->DecoderNeedsConversion(aParams.mConfig) == PlatformDecoderModule::kNeedAVCC)
   , mLastError(NS_OK)
 {
   CreateDecoder(aParams.mDiagnostics);
 }
 
 H264Converter::~H264Converter()
 {
@@ -142,17 +143,18 @@ H264Converter::CreateDecoder(DecoderDoct
   }
 
   mDecoder = mPDM->CreateVideoDecoder({
     mNeedAVCC ? mCurrentConfig : mOriginalConfig,
     mTaskQueue,
     mCallback,
     aDiagnostics,
     mImageContainer,
-    mLayersBackend
+    mLayersBackend,
+    mGMPCrashHelper
   });
 
   if (!mDecoder) {
     mLastError = NS_ERROR_FAILURE;
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
--- a/dom/media/platforms/wrappers/H264Converter.h
+++ b/dom/media/platforms/wrappers/H264Converter.h
@@ -60,15 +60,16 @@ private:
   VideoInfo mCurrentConfig;
   layers::LayersBackend mLayersBackend;
   RefPtr<layers::ImageContainer> mImageContainer;
   const RefPtr<TaskQueue> mTaskQueue;
   nsTArray<RefPtr<MediaRawData>> mMediaRawSamples;
   MediaDataDecoderCallback* mCallback;
   RefPtr<MediaDataDecoder> mDecoder;
   MozPromiseRequestHolder<InitPromise> mInitPromiseRequest;
+  RefPtr<GMPCrashHelper> mGMPCrashHelper;
   bool mNeedAVCC;
   nsresult mLastError;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_H264Converter_h