ImageCapture.cpp (6430B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "ImageCapture.h" 8 9 #include "CaptureTask.h" 10 #include "MediaEngineSource.h" 11 #include "mozilla/dom/BlobEvent.h" 12 #include "mozilla/dom/DOMException.h" 13 #include "mozilla/dom/Document.h" 14 #include "mozilla/dom/Event.h" 15 #include "mozilla/dom/File.h" 16 #include "mozilla/dom/ImageCaptureError.h" 17 #include "mozilla/dom/ImageCaptureErrorEvent.h" 18 #include "mozilla/dom/ImageCaptureErrorEventBinding.h" 19 #include "mozilla/dom/VideoStreamTrack.h" 20 #include "nsGlobalWindowInner.h" 21 22 namespace mozilla { 23 24 LogModule* GetICLog() { 25 static LazyLogModule log("ImageCapture"); 26 return log; 27 } 28 29 namespace dom { 30 31 NS_IMPL_CYCLE_COLLECTION_INHERITED(ImageCapture, DOMEventTargetHelper, mTrack) 32 33 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageCapture) 34 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 35 36 NS_IMPL_ADDREF_INHERITED(ImageCapture, DOMEventTargetHelper) 37 NS_IMPL_RELEASE_INHERITED(ImageCapture, DOMEventTargetHelper) 38 39 ImageCapture::ImageCapture(VideoStreamTrack* aTrack, 40 nsPIDOMWindowInner* aOwnerWindow) 41 : DOMEventTargetHelper(aOwnerWindow), mTrack(aTrack) { 42 MOZ_ASSERT(aOwnerWindow); 43 MOZ_ASSERT(aTrack); 44 } 45 46 ImageCapture::~ImageCapture() { MOZ_ASSERT(NS_IsMainThread()); } 47 48 already_AddRefed<ImageCapture> ImageCapture::Constructor( 49 const GlobalObject& aGlobal, MediaStreamTrack& aTrack, ErrorResult& aRv) { 50 nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aGlobal.GetAsSupports()); 51 if (!win) { 52 aRv.Throw(NS_ERROR_FAILURE); 53 return nullptr; 54 } 55 56 if (!aTrack.AsVideoStreamTrack()) { 57 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); 58 return nullptr; 59 } 60 61 RefPtr<ImageCapture> object = 62 new ImageCapture(aTrack.AsVideoStreamTrack(), win); 63 64 return object.forget(); 65 } 66 67 MediaStreamTrack* ImageCapture::GetVideoStreamTrack() const { return mTrack; } 68 69 nsresult ImageCapture::TakePhotoByMediaEngine() { 70 // Callback for TakPhoto(), it also monitor the principal. If principal 71 // changes, it returns PHOTO_ERROR with security error. 72 class TakePhotoCallback : public MediaEnginePhotoCallback, 73 public PrincipalChangeObserver<MediaStreamTrack> { 74 public: 75 TakePhotoCallback(VideoStreamTrack* aVideoTrack, 76 ImageCapture* aImageCapture) 77 : mVideoTrack(aVideoTrack), 78 mImageCapture(aImageCapture), 79 mPrincipalChanged(false) { 80 MOZ_ASSERT(NS_IsMainThread()); 81 mVideoTrack->AddPrincipalChangeObserver(this); 82 } 83 84 void PrincipalChanged(MediaStreamTrack* aMediaStream) override { 85 mPrincipalChanged = true; 86 } 87 88 nsresult PhotoComplete(already_AddRefed<Blob> aBlob) override { 89 RefPtr<Blob> blob = aBlob; 90 91 if (mPrincipalChanged) { 92 return PhotoError(NS_ERROR_DOM_SECURITY_ERR); 93 } 94 return mImageCapture->PostBlobEvent(blob); 95 } 96 97 nsresult PhotoError(nsresult aRv) override { 98 return mImageCapture->PostErrorEvent(ImageCaptureError::PHOTO_ERROR, aRv); 99 } 100 101 protected: 102 ~TakePhotoCallback() { 103 MOZ_ASSERT(NS_IsMainThread()); 104 mVideoTrack->RemovePrincipalChangeObserver(this); 105 } 106 107 const RefPtr<VideoStreamTrack> mVideoTrack; 108 const RefPtr<ImageCapture> mImageCapture; 109 bool mPrincipalChanged; 110 }; 111 112 RefPtr<MediaEnginePhotoCallback> callback = 113 new TakePhotoCallback(mTrack, this); 114 return mTrack->GetSource().TakePhoto(callback); 115 } 116 117 void ImageCapture::TakePhoto(ErrorResult& aResult) { 118 // According to spec, MediaStreamTrack.readyState must be "live"; however 119 // gecko doesn't implement it yet (bug 910249). Instead of readyState, we 120 // check MediaStreamTrack.enable before bug 910249 is fixed. 121 // The error code should be INVALID_TRACK, but spec doesn't define it in 122 // ImageCaptureError. So it returns PHOTO_ERROR here before spec updates. 123 if (!mTrack->Enabled()) { 124 PostErrorEvent(ImageCaptureError::PHOTO_ERROR, NS_ERROR_FAILURE); 125 return; 126 } 127 128 // Try if MediaEngine supports taking photo. 129 nsresult rv = TakePhotoByMediaEngine(); 130 131 // It falls back to MediaTrackGraph image capture if MediaEngine doesn't 132 // support TakePhoto(). 133 if (rv == NS_ERROR_NOT_IMPLEMENTED) { 134 IC_LOG( 135 "MediaEngine doesn't support TakePhoto(), it falls back to " 136 "MediaTrackGraph."); 137 RefPtr<CaptureTask> task = new CaptureTask(this); 138 139 // It adds itself into MediaTrackGraph, so ImageCapture doesn't need to 140 // hold the reference. 141 task->AttachTrack(); 142 } 143 } 144 145 nsresult ImageCapture::PostBlobEvent(Blob* aBlob) { 146 MOZ_ASSERT(NS_IsMainThread()); 147 if (!CheckPrincipal()) { 148 // Media is not same-origin, don't allow the data out. 149 return PostErrorEvent(ImageCaptureError::PHOTO_ERROR, 150 NS_ERROR_DOM_SECURITY_ERR); 151 } 152 153 BlobEventInit init; 154 init.mBubbles = false; 155 init.mCancelable = false; 156 init.mData = aBlob; 157 158 RefPtr<BlobEvent> blob_event = 159 BlobEvent::Constructor(this, u"photo"_ns, init); 160 161 return DispatchTrustedEvent(blob_event); 162 } 163 164 nsresult ImageCapture::PostErrorEvent(uint16_t aErrorCode, nsresult aReason) { 165 MOZ_ASSERT(NS_IsMainThread()); 166 nsresult rv = CheckCurrentGlobalCorrectness(); 167 NS_ENSURE_SUCCESS(rv, rv); 168 169 nsString errorMsg; 170 if (NS_FAILED(aReason)) { 171 nsCString name, message; 172 rv = NS_GetNameAndMessageForDOMNSResult(aReason, name, message); 173 if (NS_SUCCEEDED(rv)) { 174 CopyASCIItoUTF16(message, errorMsg); 175 } 176 } 177 178 RefPtr<ImageCaptureError> error = new ImageCaptureError( 179 static_cast<EventTarget*>(this), aErrorCode, errorMsg); 180 181 ImageCaptureErrorEventInit init; 182 init.mBubbles = false; 183 init.mCancelable = false; 184 init.mImageCaptureError = error; 185 186 RefPtr<Event> event = 187 ImageCaptureErrorEvent::Constructor(this, u"error"_ns, init); 188 189 return DispatchTrustedEvent(event); 190 } 191 192 bool ImageCapture::CheckPrincipal() { 193 MOZ_ASSERT(NS_IsMainThread()); 194 nsCOMPtr<nsIPrincipal> principal = mTrack->GetPrincipal(); 195 if (!GetOwnerWindow()) { 196 return false; 197 } 198 nsCOMPtr<Document> doc = GetOwnerWindow()->GetExtantDoc(); 199 return doc && principal && doc->NodePrincipal()->Subsumes(principal); 200 } 201 202 } // namespace dom 203 } // namespace mozilla