FileMediaResource.cpp (6887B)
1 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "FileMediaResource.h" 7 8 #include "mozilla/AbstractThread.h" 9 #include "mozilla/dom/BlobImpl.h" 10 #include "mozilla/dom/BlobURLProtocolHandler.h" 11 #include "nsContentUtils.h" 12 #include "nsIFile.h" 13 #include "nsIFileChannel.h" 14 #include "nsIFileStreams.h" 15 #include "nsITimedChannel.h" 16 #include "nsNetUtil.h" 17 18 namespace mozilla { 19 20 void FileMediaResource::EnsureSizeInitialized() { 21 mLock.AssertCurrentThreadOwns(); 22 NS_ASSERTION(mInput, "Must have file input stream"); 23 if (mSizeInitialized && mNotifyDataEndedProcessed) { 24 return; 25 } 26 27 if (!mSizeInitialized) { 28 // Get the file size and inform the decoder. 29 uint64_t size; 30 nsresult res = mInput->Available(&size); 31 if (NS_SUCCEEDED(res) && size <= INT64_MAX) { 32 mSize = (int64_t)size; 33 } 34 } 35 mSizeInitialized = true; 36 if (!mNotifyDataEndedProcessed && mSize >= 0) { 37 mCallback->AbstractMainThread()->Dispatch(NewRunnableMethod<nsresult>( 38 "MediaResourceCallback::NotifyDataEnded", mCallback.get(), 39 &MediaResourceCallback::NotifyDataEnded, NS_OK)); 40 } 41 mNotifyDataEndedProcessed = true; 42 } 43 44 nsresult FileMediaResource::GetCachedRanges(MediaByteRangeSet& aRanges) { 45 MutexAutoLock lock(mLock); 46 47 EnsureSizeInitialized(); 48 if (mSize == -1) { 49 return NS_ERROR_FAILURE; 50 } 51 aRanges += MediaByteRange(0, mSize); 52 return NS_OK; 53 } 54 55 nsresult FileMediaResource::Open(nsIStreamListener** aStreamListener) { 56 NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); 57 MOZ_ASSERT(aStreamListener); 58 59 *aStreamListener = nullptr; 60 nsresult rv = NS_OK; 61 62 MutexAutoLock lock(mLock); 63 // The channel is already open. We need a synchronous stream that 64 // implements nsISeekableStream, so we have to find the underlying 65 // file and reopen it 66 nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(mChannel)); 67 if (fc) { 68 nsCOMPtr<nsIFile> file; 69 rv = fc->GetFile(getter_AddRefs(file)); 70 NS_ENSURE_SUCCESS(rv, rv); 71 72 rv = NS_NewLocalFileInputStream(getter_AddRefs(mInput), file, -1, -1, 73 nsIFileInputStream::SHARE_DELETE); 74 NS_ENSURE_SUCCESS(rv, rv); 75 } else if (dom::IsBlobURI(mURI)) { 76 RefPtr<dom::BlobImpl> blobImpl; 77 rv = NS_GetBlobForBlobURI(mURI, getter_AddRefs(blobImpl)); 78 NS_ENSURE_SUCCESS(rv, rv); 79 MOZ_ASSERT(blobImpl); 80 81 ErrorResult err; 82 blobImpl->CreateInputStream(getter_AddRefs(mInput), err); 83 if (NS_WARN_IF(err.Failed())) { 84 return err.StealNSResult(); 85 } 86 } 87 88 mSeekable = do_QueryInterface(mInput); 89 if (!mSeekable) { 90 // XXX The file may just be a .url or similar 91 // shortcut that points to a Web site. We need to fix this by 92 // doing an async open and waiting until we locate the real resource, 93 // then using that (if it's still a file!). 94 return NS_ERROR_FAILURE; 95 } 96 97 return NS_OK; 98 } 99 100 RefPtr<GenericPromise> FileMediaResource::Close() { 101 NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); 102 103 // Since mChennel is only accessed by main thread, there is no necessary to 104 // take the lock. 105 if (mChannel) { 106 mChannel->Cancel(NS_ERROR_PARSED_DATA_CACHED); 107 mChannel = nullptr; 108 } 109 110 return GenericPromise::CreateAndResolve(true, __func__); 111 } 112 113 already_AddRefed<nsIPrincipal> FileMediaResource::GetCurrentPrincipal() { 114 NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); 115 116 nsCOMPtr<nsIPrincipal> principal; 117 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); 118 if (!secMan || !mChannel) return nullptr; 119 secMan->GetChannelResultPrincipal(mChannel, getter_AddRefs(principal)); 120 return principal.forget(); 121 } 122 123 bool FileMediaResource::HadCrossOriginRedirects() { 124 MOZ_ASSERT(NS_IsMainThread()); 125 126 nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(mChannel); 127 if (!timedChannel) { 128 return false; 129 } 130 131 bool allRedirectsSameOrigin = false; 132 return NS_SUCCEEDED(timedChannel->GetAllRedirectsSameOrigin( 133 &allRedirectsSameOrigin)) && 134 !allRedirectsSameOrigin; 135 } 136 137 nsresult FileMediaResource::ReadFromCache(char* aBuffer, int64_t aOffset, 138 uint32_t aCount) { 139 MutexAutoLock lock(mLock); 140 141 EnsureSizeInitialized(); 142 if (!aCount) { 143 return NS_OK; 144 } 145 int64_t offset = 0; 146 nsresult res = mSeekable->Tell(&offset); 147 NS_ENSURE_SUCCESS(res, res); 148 res = mSeekable->Seek(nsISeekableStream::NS_SEEK_SET, aOffset); 149 NS_ENSURE_SUCCESS(res, res); 150 uint32_t bytesRead = 0; 151 do { 152 uint32_t x = 0; 153 uint32_t bytesToRead = aCount - bytesRead; 154 res = mInput->Read(aBuffer, bytesToRead, &x); 155 bytesRead += x; 156 if (!x) { 157 res = NS_ERROR_FAILURE; 158 } 159 } while (bytesRead != aCount && res == NS_OK); 160 161 // Reset read head to original position so we don't disturb any other 162 // reading thread. 163 nsresult seekres = mSeekable->Seek(nsISeekableStream::NS_SEEK_SET, offset); 164 165 // If a read failed in the loop above, we want to return its failure code. 166 NS_ENSURE_SUCCESS(res, res); 167 168 // Else we succeed if the reset-seek succeeds. 169 return seekres; 170 } 171 172 nsresult FileMediaResource::UnsafeRead(char* aBuffer, uint32_t aCount, 173 uint32_t* aBytes) { 174 EnsureSizeInitialized(); 175 return mInput->Read(aBuffer, aCount, aBytes); 176 } 177 178 nsresult FileMediaResource::ReadAt(int64_t aOffset, char* aBuffer, 179 uint32_t aCount, uint32_t* aBytes) { 180 NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); 181 182 nsresult rv; 183 { 184 MutexAutoLock lock(mLock); 185 rv = UnsafeSeek(nsISeekableStream::NS_SEEK_SET, aOffset); 186 if (NS_FAILED(rv)) return rv; 187 rv = UnsafeRead(aBuffer, aCount, aBytes); 188 } 189 return rv; 190 } 191 192 already_AddRefed<MediaByteBuffer> FileMediaResource::UnsafeMediaReadAt( 193 int64_t aOffset, uint32_t aCount) { 194 RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer(); 195 bool ok = bytes->SetLength(aCount, fallible); 196 NS_ENSURE_TRUE(ok, nullptr); 197 nsresult rv = UnsafeSeek(nsISeekableStream::NS_SEEK_SET, aOffset); 198 NS_ENSURE_SUCCESS(rv, nullptr); 199 char* curr = reinterpret_cast<char*>(bytes->Elements()); 200 const char* start = curr; 201 while (aCount > 0) { 202 uint32_t bytesRead; 203 rv = UnsafeRead(curr, aCount, &bytesRead); 204 NS_ENSURE_SUCCESS(rv, nullptr); 205 if (!bytesRead) { 206 break; 207 } 208 aCount -= bytesRead; 209 curr += bytesRead; 210 } 211 bytes->SetLength(curr - start); 212 return bytes.forget(); 213 } 214 215 nsresult FileMediaResource::UnsafeSeek(int32_t aWhence, int64_t aOffset) { 216 NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); 217 218 if (!mSeekable) return NS_ERROR_FAILURE; 219 EnsureSizeInitialized(); 220 return mSeekable->Seek(aWhence, aOffset); 221 } 222 223 } // namespace mozilla