/* vim:set ts=2 sw=2 sts=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "FileMediaResource.h" #include "nsContentUtils.h" #include "nsHostObjectProtocolHandler.h" #include "nsIFileChannel.h" #include "nsIFileStreams.h" #include "nsNetUtil.h" namespace mozilla { void FileMediaResource::EnsureSizeInitialized() { mLock.AssertCurrentThreadOwns(); NS_ASSERTION(mInput, "Must have file input stream"); if (mSizeInitialized) { return; } mSizeInitialized = true; // Get the file size and inform the decoder. uint64_t size; nsresult res = mInput->Available(&size); if (NS_SUCCEEDED(res) && size <= INT64_MAX) { mSize = (int64_t)size; mCallback->NotifyDataEnded(NS_OK); } } nsresult FileMediaResource::GetCachedRanges(MediaByteRangeSet& aRanges) { MutexAutoLock lock(mLock); EnsureSizeInitialized(); if (mSize == -1) { return NS_ERROR_FAILURE; } aRanges += MediaByteRange(0, mSize); return NS_OK; } nsresult FileMediaResource::Open(nsIStreamListener** aStreamListener) { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); MOZ_ASSERT(aStreamListener); *aStreamListener = nullptr; nsresult rv = NS_OK; // The channel is already open. We need a synchronous stream that // implements nsISeekableStream, so we have to find the underlying // file and reopen it nsCOMPtr fc(do_QueryInterface(mChannel)); if (fc) { nsCOMPtr file; rv = fc->GetFile(getter_AddRefs(file)); NS_ENSURE_SUCCESS(rv, rv); rv = NS_NewLocalFileInputStream( getter_AddRefs(mInput), file, -1, -1, nsIFileInputStream::SHARE_DELETE); } else if (IsBlobURI(mURI)) { rv = NS_GetStreamForBlobURI(mURI, getter_AddRefs(mInput)); } NS_ENSURE_SUCCESS(rv, rv); mSeekable = do_QueryInterface(mInput); if (!mSeekable) { // XXX The file may just be a .url or similar // shortcut that points to a Web site. We need to fix this by // doing an async open and waiting until we locate the real resource, // then using that (if it's still a file!). return NS_ERROR_FAILURE; } return NS_OK; } nsresult FileMediaResource::Close() { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); // Since mChennel is only accessed by main thread, there is no necessary to // take the lock. if (mChannel) { mChannel->Cancel(NS_ERROR_PARSED_DATA_CACHED); mChannel = nullptr; } return NS_OK; } already_AddRefed FileMediaResource::GetCurrentPrincipal() { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); nsCOMPtr principal; nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); if (!secMan || !mChannel) return nullptr; secMan->GetChannelResultPrincipal(mChannel, getter_AddRefs(principal)); return principal.forget(); } nsresult FileMediaResource::ReadFromCache(char* aBuffer, int64_t aOffset, uint32_t aCount) { MutexAutoLock lock(mLock); EnsureSizeInitialized(); if (!aCount) { return NS_OK; } int64_t offset = 0; nsresult res = mSeekable->Tell(&offset); NS_ENSURE_SUCCESS(res,res); res = mSeekable->Seek(nsISeekableStream::NS_SEEK_SET, aOffset); NS_ENSURE_SUCCESS(res,res); uint32_t bytesRead = 0; do { uint32_t x = 0; uint32_t bytesToRead = aCount - bytesRead; res = mInput->Read(aBuffer, bytesToRead, &x); bytesRead += x; if (!x) { res = NS_ERROR_FAILURE; } } while (bytesRead != aCount && res == NS_OK); // Reset read head to original position so we don't disturb any other // reading thread. nsresult seekres = mSeekable->Seek(nsISeekableStream::NS_SEEK_SET, offset); // If a read failed in the loop above, we want to return its failure code. NS_ENSURE_SUCCESS(res,res); // Else we succeed if the reset-seek succeeds. return seekres; } nsresult FileMediaResource::UnsafeRead(char* aBuffer, uint32_t aCount, uint32_t* aBytes) { EnsureSizeInitialized(); return mInput->Read(aBuffer, aCount, aBytes); } nsresult FileMediaResource::ReadAt(int64_t aOffset, char* aBuffer, uint32_t aCount, uint32_t* aBytes) { NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); nsresult rv; { MutexAutoLock lock(mLock); rv = UnsafeSeek(nsISeekableStream::NS_SEEK_SET, aOffset); if (NS_FAILED(rv)) return rv; rv = UnsafeRead(aBuffer, aCount, aBytes); } if (NS_SUCCEEDED(rv)) { DispatchBytesConsumed(*aBytes, aOffset); } return rv; } already_AddRefed FileMediaResource::UnsafeMediaReadAt(int64_t aOffset, uint32_t aCount) { RefPtr bytes = new MediaByteBuffer(); bool ok = bytes->SetLength(aCount, fallible); NS_ENSURE_TRUE(ok, nullptr); nsresult rv = UnsafeSeek(nsISeekableStream::NS_SEEK_SET, aOffset); NS_ENSURE_SUCCESS(rv, nullptr); char* curr = reinterpret_cast(bytes->Elements()); const char* start = curr; while (aCount > 0) { uint32_t bytesRead; rv = UnsafeRead(curr, aCount, &bytesRead); NS_ENSURE_SUCCESS(rv, nullptr); if (!bytesRead) { break; } aCount -= bytesRead; curr += bytesRead; } bytes->SetLength(curr - start); return bytes.forget(); } nsresult FileMediaResource::UnsafeSeek(int32_t aWhence, int64_t aOffset) { NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); if (!mSeekable) return NS_ERROR_FAILURE; EnsureSizeInitialized(); return mSeekable->Seek(aWhence, aOffset); } int64_t FileMediaResource::Tell() { MutexAutoLock lock(mLock); EnsureSizeInitialized(); int64_t offset = 0; // Return mSize as offset (end of stream) in case of error if (!mSeekable || NS_FAILED(mSeekable->Tell(&offset))) return mSize; return offset; } } // mozilla namespace