Bug 1191731 - Update poster API to allow application control over when poster is saved. r=bz, r=dhylands
authorAndrew Osmond <aosmond@mozilla.com>
Tue, 15 Sep 2015 12:40:32 -0400
changeset 263566 378958ae220f4de9fd3934fffe2561f53e7727b0
parent 263565 211edf7e53aa460bcf63909ed882b3735565a0d3
child 263567 74448998d12d506a0d6b563dc09ffcad1f5a1627
push id65352
push userkwierso@gmail.com
push dateMon, 21 Sep 2015 16:52:59 +0000
treeherdermozilla-inbound@ee92eb117e92 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, dhylands
bugs1191731
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1191731 - Update poster API to allow application control over when poster is saved. r=bz, r=dhylands
dom/base/nsGkAtomList.h
dom/camera/DOMCameraControl.cpp
dom/camera/DOMCameraControl.h
dom/webidl/CameraControl.webidl
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -845,16 +845,17 @@ GK_ATOM(onpairingconsentreq, "onpairingc
 GK_ATOM(onpaste, "onpaste")
 GK_ATOM(onpendingchange, "onpendingchange")
 GK_ATOM(onpichange, "onpichange")
 GK_ATOM(onpicture, "onpicture")
 GK_ATOM(onpopuphidden, "onpopuphidden")
 GK_ATOM(onpopuphiding, "onpopuphiding")
 GK_ATOM(onpopupshowing, "onpopupshowing")
 GK_ATOM(onpopupshown, "onpopupshown")
+GK_ATOM(onposter, "onposter")
 GK_ATOM(onpreviewstatechange, "onpreviewstatechange")
 GK_ATOM(onpullphonebookreq, "onpullphonebookreq")
 GK_ATOM(onpullvcardentryreq, "onpullvcardentryreq")
 GK_ATOM(onpullvcardlistingreq, "onpullvcardlistingreq")
 GK_ATOM(onpush, "onpush")
 GK_ATOM(onpushsubscriptionchange, "onpushsubscriptionchange")
 GK_ATOM(onpschange, "onpschange")
 GK_ATOM(onptychange, "onptychange")
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -126,54 +126,16 @@ StartRecordingHelper::HandleEvent(nsIDOM
   nsString eventType;
   aEvent->GetType(eventType);
   mState = eventType.EqualsLiteral("success");
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS(mozilla::StartRecordingHelper, nsIDOMEventListener)
 
-class mozilla::RecorderPosterHelper : public nsIDOMEventListener
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIDOMEVENTLISTENER
-
-  explicit RecorderPosterHelper(nsDOMCameraControl* aDOMCameraControl)
-    : mDOMCameraControl(aDOMCameraControl)
-    , mState(CameraControlListener::kPosterFailed)
-  {
-    MOZ_COUNT_CTOR(RecorderPosterHelper);
-  }
-
-protected:
-  virtual ~RecorderPosterHelper()
-  {
-    MOZ_COUNT_DTOR(RecorderPosterHelper);
-    mDOMCameraControl->OnRecorderStateChange(mState, 0, 0);
-  }
-
-protected:
-  nsRefPtr<nsDOMCameraControl> mDOMCameraControl;
-  CameraControlListener::RecorderState mState;
-};
-
-NS_IMETHODIMP
-RecorderPosterHelper::HandleEvent(nsIDOMEvent* aEvent)
-{
-  nsString eventType;
-  aEvent->GetType(eventType);
-  if (eventType.EqualsLiteral("success")) {
-    mState = CameraControlListener::kPosterCreated;
-  }
-  return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS(mozilla::RecorderPosterHelper, nsIDOMEventListener)
-
 nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration()
   : CameraConfiguration()
   , mMaxFocusAreas(0)
   , mMaxMeteringAreas(0)
 {
   MOZ_COUNT_CTOR(nsDOMCameraControl::DOMCameraConfiguration);
 }
 
@@ -792,28 +754,21 @@ nsDOMCameraControl::StartRecording(const
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
 
   nsRefPtr<Promise> promise = CreatePromise(aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  // Must supply both the poster path and storage area or neither
-  if (aOptions.mPosterFilepath.IsEmpty() ==
-      static_cast<bool>(aOptions.mPosterStorageArea.get())) {
-    promise->MaybeReject(NS_ERROR_ILLEGAL_VALUE);
-    return promise.forget();
-  }
-
   // If we are trying to start recording, already recording or are still
   // waiting for a poster to be created/fail, we need to wait
   if (mStartRecordingPromise || mRecording ||
       mRecordingStoppedDeferred ||
-      !mOptions.mPosterFilepath.IsEmpty()) {
+      mOptions.mCreatePoster) {
     promise->MaybeReject(NS_ERROR_IN_PROGRESS);
     return promise.forget();
   }
 
   aRv = NotifyRecordingStatusChange(NS_LITERAL_STRING("starting"));
   if (aRv.Failed()) {
     return nullptr;
   }
@@ -846,26 +801,25 @@ nsDOMCameraControl::OnCreatedFileDescrip
   nsresult rv = NS_ERROR_FAILURE;
 
   if (!mCameraControl) {
     rv = NS_ERROR_NOT_AVAILABLE;
   } else if (!mRecording) {
     // Race condition where StopRecording comes in before we issue
     // the start recording request to Gonk
     rv = NS_ERROR_ABORT;
-    mOptions.mPosterFilepath.Truncate();
-    mOptions.mPosterStorageArea = nullptr;
+    mOptions.mCreatePoster = false;
   } else if (aSucceeded && mDSFileDescriptor->mFileDescriptor.IsValid()) {
     ICameraControl::StartRecordingOptions o;
 
     o.rotation = mOptions.mRotation;
     o.maxFileSizeBytes = mOptions.mMaxFileSizeBytes;
     o.maxVideoLengthMs = mOptions.mMaxVideoLengthMs;
     o.autoEnableLowLightTorch = mOptions.mAutoEnableLowLightTorch;
-    o.createPoster = !mOptions.mPosterFilepath.IsEmpty();
+    o.createPoster = mOptions.mCreatePoster;
     rv = mCameraControl->StartRecording(mDSFileDescriptor.get(), &o);
     if (NS_SUCCEEDED(rv)) {
       return;
     }
   }
 
   OnUserError(CameraControlListener::kInStartRecording, rv);
 
@@ -1341,41 +1295,33 @@ nsDOMCameraControl::OnPreviewStateChange
   DispatchPreviewStateEvent(aState);
 }
 
 void
 nsDOMCameraControl::OnPoster(BlobImpl* aPoster)
 {
   DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!mOptions.mPosterFilepath.IsEmpty());
-
-  // Destructor will trigger an error notification if any step fails
-  nsRefPtr<RecorderPosterHelper> listener = new RecorderPosterHelper(this);
-  if (NS_WARN_IF(!aPoster)) {
-    return;
-  }
+  MOZ_ASSERT(mOptions.mCreatePoster);
 
   nsRefPtr<Blob> blob = Blob::Create(GetParentObject(), aPoster);
   if (NS_WARN_IF(!blob)) {
+    OnRecorderStateChange(CameraControlListener::kPosterFailed, 0, 0);
     return;
   }
 
-  if (NS_WARN_IF(!mOptions.mPosterStorageArea)) {
-    return;
-  }
+  BlobEventInit eventInit;
+  eventInit.mData = blob;
 
-  ErrorResult rv;
-  nsRefPtr<DOMRequest> request =
-    mOptions.mPosterStorageArea->AddNamed(blob, mOptions.mPosterFilepath, rv);
-  if (NS_WARN_IF(rv.Failed())) {
-    return;
-  }
+  nsRefPtr<BlobEvent> event = BlobEvent::Constructor(this,
+                                                     NS_LITERAL_STRING("poster"),
+                                                     eventInit);
 
-  RegisterStorageRequestEvents(request, listener);
+  DispatchTrustedEvent(event);
+  OnRecorderStateChange(CameraControlListener::kPosterCreated, 0, 0);
 }
 
 void
 nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState aState,
                                           int32_t aArg, int32_t aTrackNum)
 {
   // For now, we do nothing with 'aStatus' and 'aTrackNum'.
   DOM_CAMERA_LOGT("%s:%d : this=%p, state=%u\n", __func__, __LINE__, this, aState);
@@ -1392,35 +1338,33 @@ nsDOMCameraControl::OnRecorderStateChang
           promise->MaybeResolve(JS::UndefinedHandleValue);
         }
 
         state = NS_LITERAL_STRING("Started");
       }
       break;
 
     case CameraControlListener::kRecorderStopped:
-      if (!mOptions.mPosterFilepath.IsEmpty()) {
+      if (mOptions.mCreatePoster) {
         mRecordingStoppedDeferred = true;
         return;
       }
 
       NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown"));
       state = NS_LITERAL_STRING("Stopped");
       break;
 
     case CameraControlListener::kPosterCreated:
       state = NS_LITERAL_STRING("PosterCreated");
-      mOptions.mPosterFilepath.Truncate();
-      mOptions.mPosterStorageArea = nullptr;
+      mOptions.mCreatePoster = false;
       break;
 
     case CameraControlListener::kPosterFailed:
       state = NS_LITERAL_STRING("PosterFailed");
-      mOptions.mPosterFilepath.Truncate();
-      mOptions.mPosterStorageArea = nullptr;
+      mOptions.mCreatePoster = false;
       break;
 
     case CameraControlListener::kRecorderPaused:
       state = NS_LITERAL_STRING("Paused");
       break;
 
     case CameraControlListener::kRecorderResumed:
       state = NS_LITERAL_STRING("Resumed");
@@ -1454,17 +1398,17 @@ nsDOMCameraControl::OnRecorderStateChang
 
     default:
       MOZ_ASSERT_UNREACHABLE("Unanticipated video recorder error");
       return;
   }
 
   DispatchStateEvent(NS_LITERAL_STRING("recorderstatechange"), state);
 
-  if (mRecordingStoppedDeferred && mOptions.mPosterFilepath.IsEmpty()) {
+  if (mRecordingStoppedDeferred && !mOptions.mCreatePoster) {
     mRecordingStoppedDeferred = false;
     OnRecorderStateChange(CameraControlListener::kRecorderStopped, 0, 0);
   }
 }
 
 void
 nsDOMCameraControl::OnConfigurationChange(DOMCameraConfiguration* aConfiguration)
 {
--- a/dom/camera/DOMCameraControl.h
+++ b/dom/camera/DOMCameraControl.h
@@ -138,16 +138,17 @@ public:
   IMPL_EVENT_HANDLER(facesdetected)
   IMPL_EVENT_HANDLER(shutter)
   IMPL_EVENT_HANDLER(close)
   IMPL_EVENT_HANDLER(recorderstatechange)
   IMPL_EVENT_HANDLER(previewstatechange)
   IMPL_EVENT_HANDLER(focus)
   IMPL_EVENT_HANDLER(picture)
   IMPL_EVENT_HANDLER(configurationchange)
+  IMPL_EVENT_HANDLER(poster)
 
 protected:
   virtual ~nsDOMCameraControl();
 
   class DOMCameraConfiguration final : public dom::CameraConfiguration
   {
   public:
     NS_INLINE_DECL_REFCOUNTING(DOMCameraConfiguration)
--- a/dom/webidl/CameraControl.webidl
+++ b/dom/webidl/CameraControl.webidl
@@ -105,21 +105,20 @@ dictionary CameraStartRecordingOptions
      camera has determined that the scene is poorly lit, the flash mode
      will be automatically changed to "torch" until stopRecording() is
      called. During this time, flashMode will reflect the new setting. If
      flashMode is changed while recording is in progress, the new setting
      will be left as-is on stopRecording(). If the camera does not
      support this setting, it will be ignored. */
   boolean autoEnableLowLightTorch = false;
 
-  /* If given, a poster JPG will be created from the recording and saved
-     at the given path. PosterCreated or PosterFailed recording state
+  /* If true, a poster JPG will be created from the recording and issued
+     via the poster event. PosterCreated or PosterFailed recording state
      changes will indicate whether or not it was created. */
-  DOMString posterFilepath = "";
-  DeviceStorage? posterStorageArea = null;
+  boolean createPoster = false;
 };
 
 /*
     attributes here affect the preview, any pictures taken, and/or
     any video recorded by the camera.
 */
 [Func="nsDOMCameraControl::HasSupport"]
 interface CameraControl : MediaStream
@@ -259,16 +258,20 @@ interface CameraControl : MediaStream
              'FileSizeLimitReached' if stopped due to file size limit
              'VideoLengthLimitReached' if stopped due to a time limit
              'TrackCompleted' if audio or video track complete when stopping
              'TrackFailed' if audio or video track incomplete when stopping
              'MediaRecorderFailed' if failed due to local error
              'MediaServerFailed' if failed due to media server */
   attribute EventHandler    onrecorderstatechange;
 
+  /* the event dispatched when a poster is successfully captured; it is of the
+     type BlobEvent, where the data attribute contains the poster. */
+  attribute EventHandler    onposter;
+
   /* the event dispatched when the viewfinder stops or starts,
      useful for synchronizing other UI elements.
 
      event type is CameraStateChangeEvent where:
          'newState' is the new preview state */
   attribute EventHandler    onpreviewstatechange;
 
   /* the size of the picture to be returned by a call to takePicture();