Bug 1671598 [wpt PR 26128] - [AspectRatio] Fix divide by zero with a small float...
[gecko.git] / dom / indexedDB / IDBFileHandle.cpp
blob44a387e4491454cce8fa3951d528135ceb42b295
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "IDBFileHandle.h"
9 #include "ActorsChild.h"
10 #include "BackgroundChildImpl.h"
11 #include "IDBEvents.h"
12 #include "IDBMutableFile.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/dom/File.h"
15 #include "mozilla/dom/IDBFileHandleBinding.h"
16 #include "mozilla/dom/IPCBlobUtils.h"
17 #include "mozilla/dom/PBackgroundFileHandle.h"
18 #include "mozilla/EventDispatcher.h"
19 #include "mozilla/ipc/BackgroundChild.h"
20 #include "nsContentUtils.h"
21 #include "nsQueryObject.h"
22 #include "nsServiceManagerUtils.h"
23 #include "nsWidgetsCID.h"
25 namespace mozilla {
26 namespace dom {
28 using namespace mozilla::dom::indexedDB;
29 using namespace mozilla::ipc;
31 namespace {
33 RefPtr<IDBFileRequest> GenerateFileRequest(IDBFileHandle* aFileHandle) {
34 MOZ_ASSERT(aFileHandle);
35 aFileHandle->AssertIsOnOwningThread();
37 return IDBFileRequest::Create(aFileHandle, /* aWrapAsDOMRequest */ false);
40 } // namespace
42 IDBFileHandle::IDBFileHandle(IDBMutableFile* aMutableFile, FileMode aMode)
43 : DOMEventTargetHelper(aMutableFile),
44 mMutableFile(aMutableFile),
45 mBackgroundActor(nullptr),
46 mLocation(0),
47 mPendingRequestCount(0),
48 mReadyState(INITIAL),
49 mMode(aMode),
50 mAborted(false),
51 mCreating(false)
52 #ifdef DEBUG
54 mSentFinishOrAbort(false),
55 mFiredCompleteOrAbort(false)
56 #endif
58 MOZ_ASSERT(aMutableFile);
59 aMutableFile->AssertIsOnOwningThread();
62 IDBFileHandle::~IDBFileHandle() {
63 AssertIsOnOwningThread();
64 MOZ_ASSERT(!mPendingRequestCount);
65 MOZ_ASSERT(!mCreating);
66 MOZ_ASSERT(mSentFinishOrAbort);
67 MOZ_ASSERT_IF(mBackgroundActor, mFiredCompleteOrAbort);
69 mMutableFile->UnregisterFileHandle(this);
71 if (mBackgroundActor) {
72 mBackgroundActor->SendDeleteMeInternal();
74 MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
78 // static
79 RefPtr<IDBFileHandle> IDBFileHandle::Create(IDBMutableFile* aMutableFile,
80 FileMode aMode) {
81 MOZ_ASSERT(aMutableFile);
82 aMutableFile->AssertIsOnOwningThread();
83 MOZ_ASSERT(aMode == FileMode::Readonly || aMode == FileMode::Readwrite);
85 RefPtr<IDBFileHandle> fileHandle = new IDBFileHandle(aMutableFile, aMode);
87 // XXX Fix!
88 MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
90 nsCOMPtr<nsIRunnable> runnable = do_QueryObject(fileHandle);
91 nsContentUtils::AddPendingIDBTransaction(runnable.forget());
93 fileHandle->mCreating = true;
95 aMutableFile->RegisterFileHandle(fileHandle);
97 return fileHandle;
100 // static
101 IDBFileHandle* IDBFileHandle::GetCurrent() {
102 MOZ_ASSERT(BackgroundChild::GetForCurrentThread());
104 BackgroundChildImpl::ThreadLocal* threadLocal =
105 BackgroundChildImpl::GetThreadLocalForCurrentThread();
106 MOZ_ASSERT(threadLocal);
108 return threadLocal->mCurrentFileHandle;
111 #ifdef DEBUG
113 void IDBFileHandle::AssertIsOnOwningThread() const {
114 MOZ_ASSERT(mMutableFile);
115 mMutableFile->AssertIsOnOwningThread();
118 #endif // DEBUG
120 void IDBFileHandle::SetBackgroundActor(BackgroundFileHandleChild* aActor) {
121 AssertIsOnOwningThread();
122 MOZ_ASSERT(aActor);
123 MOZ_ASSERT(!mBackgroundActor);
125 mBackgroundActor = aActor;
128 void IDBFileHandle::StartRequest(IDBFileRequest* aFileRequest,
129 const FileRequestParams& aParams) {
130 AssertIsOnOwningThread();
131 MOZ_ASSERT(aFileRequest);
132 MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
134 BackgroundFileRequestChild* actor =
135 new BackgroundFileRequestChild(aFileRequest);
137 mBackgroundActor->SendPBackgroundFileRequestConstructor(actor, aParams);
139 // Balanced in BackgroundFileRequestChild::Recv__delete__().
140 OnNewRequest();
143 void IDBFileHandle::OnNewRequest() {
144 AssertIsOnOwningThread();
146 if (!mPendingRequestCount) {
147 MOZ_ASSERT(mReadyState == INITIAL);
148 mReadyState = LOADING;
151 ++mPendingRequestCount;
154 void IDBFileHandle::OnRequestFinished(bool aActorDestroyedNormally) {
155 AssertIsOnOwningThread();
156 MOZ_ASSERT(mPendingRequestCount);
158 --mPendingRequestCount;
160 if (!mPendingRequestCount && !mMutableFile->IsInvalidated()) {
161 mReadyState = FINISHING;
163 if (aActorDestroyedNormally) {
164 if (!mAborted) {
165 SendFinish();
166 } else {
167 SendAbort();
169 } else {
170 // Don't try to send any more messages to the parent if the request actor
171 // was killed.
172 #ifdef DEBUG
173 MOZ_ASSERT(!mSentFinishOrAbort);
174 mSentFinishOrAbort = true;
175 #endif
180 void IDBFileHandle::FireCompleteOrAbortEvents(bool aAborted) {
181 AssertIsOnOwningThread();
182 MOZ_ASSERT(!mFiredCompleteOrAbort);
184 mReadyState = DONE;
186 #ifdef DEBUG
187 mFiredCompleteOrAbort = true;
188 #endif
190 // TODO: Why is it necessary to create the Event on the heap at all?
191 const auto event = CreateGenericEvent(
192 this,
193 aAborted ? nsDependentString(kAbortEventType)
194 : nsDependentString(kCompleteEventType),
195 aAborted ? eDoesBubble : eDoesNotBubble, eNotCancelable);
196 MOZ_ASSERT(event);
198 IgnoredErrorResult rv;
199 DispatchEvent(*event, rv);
200 if (rv.Failed()) {
201 NS_WARNING("DispatchEvent failed!");
205 bool IDBFileHandle::IsOpen() const {
206 AssertIsOnOwningThread();
208 // If we haven't started anything then we're open.
209 if (mReadyState == INITIAL) {
210 return true;
213 // If we've already started then we need to check to see if we still have the
214 // mCreating flag set. If we do (i.e. we haven't returned to the event loop
215 // from the time we were created) then we are open. Otherwise check the
216 // currently running file handles to see if it's the same. We only allow other
217 // requests to be made if this file handle is currently running.
218 if (mReadyState == LOADING && (mCreating || GetCurrent() == this)) {
219 return true;
222 return false;
225 void IDBFileHandle::Abort() {
226 AssertIsOnOwningThread();
228 if (IsFinishingOrDone()) {
229 // Already started (and maybe finished) the finish or abort so there is
230 // nothing to do here.
231 return;
234 const bool isInvalidated = mMutableFile->IsInvalidated();
235 bool needToSendAbort = mReadyState == INITIAL && !isInvalidated;
237 #ifdef DEBUG
238 if (isInvalidated) {
239 mSentFinishOrAbort = true;
241 #endif
243 mAborted = true;
244 mReadyState = DONE;
246 // Fire the abort event if there are no outstanding requests. Otherwise the
247 // abort event will be fired when all outstanding requests finish.
248 if (needToSendAbort) {
249 SendAbort();
253 RefPtr<IDBFileRequest> IDBFileHandle::GetMetadata(
254 const IDBFileMetadataParameters& aParameters, ErrorResult& aRv) {
255 AssertIsOnOwningThread();
257 // Common state checking
258 if (!CheckState(aRv)) {
259 return nullptr;
262 // Argument checking for get metadata.
263 if (!aParameters.mSize && !aParameters.mLastModified) {
264 aRv.ThrowTypeError("Either size or lastModified should be true.");
265 return nullptr;
268 // Do nothing if the window is closed
269 if (!CheckWindow()) {
270 return nullptr;
273 FileRequestGetMetadataParams params;
274 params.size() = aParameters.mSize;
275 params.lastModified() = aParameters.mLastModified;
277 auto fileRequest = GenerateFileRequest(this);
279 StartRequest(fileRequest, params);
281 return fileRequest;
284 RefPtr<IDBFileRequest> IDBFileHandle::Truncate(const Optional<uint64_t>& aSize,
285 ErrorResult& aRv) {
286 AssertIsOnOwningThread();
288 // State checking for write
289 if (!CheckStateForWrite(aRv)) {
290 return nullptr;
293 // Getting location and additional state checking for truncate
294 uint64_t location;
295 if (aSize.WasPassed()) {
296 // Cannot use UINT64_MAX as the truncation size, as this is used as a
297 // special value for the location to mark append mode. This is not really of
298 // practical relevance, as a file cannot actually have a size that large.
300 // XXX: Remove this check when removing the use of UINT64_MAX as a special
301 // value for the location to mark append mode?
302 if (aSize.Value() == UINT64_MAX) {
303 aRv.ThrowTypeError("UINT64_MAX is not a valid size");
304 return nullptr;
306 location = aSize.Value();
307 } else {
308 // Fail if we are in append mode.
310 // XXX: Is it really ok that truncate with a size parameter works when in
311 // append mode, but one without a size parameter does not?
312 if (mLocation == UINT64_MAX) {
313 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
314 return nullptr;
316 location = mLocation;
319 // Do nothing if the window is closed
320 if (!CheckWindow()) {
321 return nullptr;
324 FileRequestTruncateParams params;
325 params.offset() = location;
327 auto fileRequest = GenerateFileRequest(this);
329 StartRequest(fileRequest, params);
331 if (aSize.WasPassed()) {
332 mLocation = aSize.Value();
335 return fileRequest;
338 RefPtr<IDBFileRequest> IDBFileHandle::Flush(ErrorResult& aRv) {
339 AssertIsOnOwningThread();
341 // State checking for write
342 if (!CheckStateForWrite(aRv)) {
343 return nullptr;
346 // Do nothing if the window is closed
347 if (!CheckWindow()) {
348 return nullptr;
351 FileRequestFlushParams params;
353 auto fileRequest = GenerateFileRequest(this);
355 StartRequest(fileRequest, params);
357 return fileRequest;
360 void IDBFileHandle::Abort(ErrorResult& aRv) {
361 AssertIsOnOwningThread();
363 // This method is special enough for not using generic state checking methods.
365 if (IsFinishingOrDone()) {
366 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
367 return;
370 Abort();
373 bool IDBFileHandle::CheckState(ErrorResult& aRv) {
374 if (!IsOpen()) {
375 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
376 return false;
379 return true;
382 bool IDBFileHandle::CheckStateAndArgumentsForRead(uint64_t aSize,
383 ErrorResult& aRv) {
384 // Common state checking
385 if (!CheckState(aRv)) {
386 return false;
389 // Additional state checking for read
390 if (mLocation == UINT64_MAX) {
391 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
392 return false;
395 // Argument checking for read
396 if (!aSize) {
397 aRv.ThrowTypeError("0 (Zero) is not a valid read size.");
398 return false;
401 if (aSize > UINT32_MAX) {
402 aRv.ThrowTypeError("Data size for read is too large.");
403 return false;
406 return true;
409 bool IDBFileHandle::CheckStateForWrite(ErrorResult& aRv) {
410 // Common state checking
411 if (!CheckState(aRv)) {
412 return false;
415 // Additional state checking for write
416 if (mMode != FileMode::Readwrite) {
417 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR);
418 return false;
421 return true;
424 bool IDBFileHandle::CheckStateForWriteOrAppend(bool aAppend, ErrorResult& aRv) {
425 // State checking for write
426 if (!CheckStateForWrite(aRv)) {
427 return false;
430 // Additional state checking for write
431 if (!aAppend && mLocation == UINT64_MAX) {
432 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
433 return false;
436 return true;
439 bool IDBFileHandle::CheckWindow() {
440 AssertIsOnOwningThread();
442 return GetOwner();
445 RefPtr<IDBFileRequest> IDBFileHandle::Read(uint64_t aSize, bool aHasEncoding,
446 const nsAString& aEncoding,
447 ErrorResult& aRv) {
448 AssertIsOnOwningThread();
450 // State and argument checking for read
451 if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
452 return nullptr;
455 // Do nothing if the window is closed
456 if (!CheckWindow()) {
457 return nullptr;
460 FileRequestReadParams params;
461 params.offset() = mLocation;
462 params.size() = aSize;
464 auto fileRequest = GenerateFileRequest(this);
465 if (aHasEncoding) {
466 fileRequest->SetEncoding(aEncoding);
469 StartRequest(fileRequest, params);
471 mLocation += aSize;
473 return fileRequest;
476 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(
477 const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue, bool aAppend,
478 ErrorResult& aRv) {
479 AssertIsOnOwningThread();
481 if (aValue.IsString()) {
482 return WriteOrAppend(aValue.GetAsString(), aAppend, aRv);
485 if (aValue.IsArrayBuffer()) {
486 return WriteOrAppend(aValue.GetAsArrayBuffer(), aAppend, aRv);
489 if (aValue.IsArrayBufferView()) {
490 return WriteOrAppend(aValue.GetAsArrayBufferView(), aAppend, aRv);
493 MOZ_ASSERT(aValue.IsBlob());
494 return WriteOrAppend(aValue.GetAsBlob(), aAppend, aRv);
497 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(const nsAString& aValue,
498 bool aAppend,
499 ErrorResult& aRv) {
500 AssertIsOnOwningThread();
502 // State checking for write or append
503 if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
504 return nullptr;
507 NS_ConvertUTF16toUTF8 cstr(aValue);
509 uint64_t dataLength = cstr.Length();
511 if (!dataLength) {
512 return nullptr;
515 FileRequestStringData stringData(cstr);
517 // Do nothing if the window is closed
518 if (!CheckWindow()) {
519 return nullptr;
522 return WriteInternal(stringData, dataLength, aAppend, aRv);
525 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(const ArrayBuffer& aValue,
526 bool aAppend,
527 ErrorResult& aRv) {
528 AssertIsOnOwningThread();
530 // State checking for write or append
531 if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
532 return nullptr;
535 aValue.ComputeState();
537 uint64_t dataLength = aValue.Length();
539 if (!dataLength) {
540 return nullptr;
543 const char* data = reinterpret_cast<const char*>(aValue.Data());
545 FileRequestStringData stringData;
546 if (NS_WARN_IF(
547 !stringData.string().Assign(data, aValue.Length(), fallible_t()))) {
548 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
549 return nullptr;
552 // Do nothing if the window is closed
553 if (!CheckWindow()) {
554 return nullptr;
557 return WriteInternal(stringData, dataLength, aAppend, aRv);
560 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(
561 const ArrayBufferView& aValue, bool aAppend, ErrorResult& aRv) {
562 AssertIsOnOwningThread();
564 // State checking for write or append
565 if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
566 return nullptr;
569 aValue.ComputeState();
571 uint64_t dataLength = aValue.Length();
573 if (!dataLength) {
574 return nullptr;
577 const char* data = reinterpret_cast<const char*>(aValue.Data());
579 FileRequestStringData stringData;
580 if (NS_WARN_IF(
581 !stringData.string().Assign(data, aValue.Length(), fallible_t()))) {
582 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
583 return nullptr;
586 // Do nothing if the window is closed
587 if (!CheckWindow()) {
588 return nullptr;
591 return WriteInternal(stringData, dataLength, aAppend, aRv);
594 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(Blob& aValue, bool aAppend,
595 ErrorResult& aRv) {
596 AssertIsOnOwningThread();
598 // State checking for write or append
599 if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
600 return nullptr;
603 ErrorResult error;
604 uint64_t dataLength = aValue.GetSize(error);
605 if (NS_WARN_IF(error.Failed())) {
606 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
607 return nullptr;
610 if (!dataLength) {
611 return nullptr;
614 PBackgroundChild* backgroundActor = BackgroundChild::GetForCurrentThread();
615 MOZ_ASSERT(backgroundActor);
617 IPCBlob ipcBlob;
618 nsresult rv =
619 IPCBlobUtils::Serialize(aValue.Impl(), backgroundActor, ipcBlob);
620 if (NS_WARN_IF(NS_FAILED(rv))) {
621 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
622 return nullptr;
625 FileRequestBlobData blobData;
626 blobData.blob() = ipcBlob;
628 // Do nothing if the window is closed
629 if (!CheckWindow()) {
630 return nullptr;
633 return WriteInternal(blobData, dataLength, aAppend, aRv);
636 RefPtr<IDBFileRequest> IDBFileHandle::WriteInternal(
637 const FileRequestData& aData, uint64_t aDataLength, bool aAppend,
638 ErrorResult& aRv) {
639 AssertIsOnOwningThread();
641 DebugOnly<ErrorResult> error;
642 MOZ_ASSERT(CheckStateForWrite(error));
643 MOZ_ASSERT_IF(!aAppend, mLocation != UINT64_MAX);
644 MOZ_ASSERT(aDataLength);
645 MOZ_ASSERT(CheckWindow());
647 FileRequestWriteParams params;
648 params.offset() = aAppend ? UINT64_MAX : mLocation;
649 params.data() = aData;
650 params.dataLength() = aDataLength;
652 auto fileRequest = GenerateFileRequest(this);
653 MOZ_ASSERT(fileRequest);
655 StartRequest(fileRequest, params);
657 if (aAppend) {
658 mLocation = UINT64_MAX;
659 } else {
660 mLocation += aDataLength;
663 return fileRequest;
666 void IDBFileHandle::SendFinish() {
667 AssertIsOnOwningThread();
668 MOZ_ASSERT(!mAborted);
669 MOZ_ASSERT(IsFinishingOrDone());
670 MOZ_ASSERT(!mSentFinishOrAbort);
671 MOZ_ASSERT(!mPendingRequestCount);
673 MOZ_ASSERT(mBackgroundActor);
674 mBackgroundActor->SendFinish();
676 #ifdef DEBUG
677 mSentFinishOrAbort = true;
678 #endif
681 void IDBFileHandle::SendAbort() {
682 AssertIsOnOwningThread();
683 MOZ_ASSERT(mAborted);
684 MOZ_ASSERT(IsFinishingOrDone());
685 MOZ_ASSERT(!mSentFinishOrAbort);
687 MOZ_ASSERT(mBackgroundActor);
688 mBackgroundActor->SendAbort();
690 #ifdef DEBUG
691 mSentFinishOrAbort = true;
692 #endif
695 NS_IMPL_ADDREF_INHERITED(IDBFileHandle, DOMEventTargetHelper)
696 NS_IMPL_RELEASE_INHERITED(IDBFileHandle, DOMEventTargetHelper)
698 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBFileHandle)
699 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
700 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
701 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
703 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFileHandle)
705 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBFileHandle,
706 DOMEventTargetHelper)
707 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMutableFile)
708 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
710 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBFileHandle,
711 DOMEventTargetHelper)
712 // Don't unlink mMutableFile!
713 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
714 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
716 NS_IMETHODIMP
717 IDBFileHandle::Run() {
718 AssertIsOnOwningThread();
720 // We're back at the event loop, no longer newborn.
721 mCreating = false;
723 // Maybe finish if there were no requests generated.
724 if (mReadyState == INITIAL) {
725 mReadyState = DONE;
727 SendFinish();
730 return NS_OK;
733 void IDBFileHandle::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
734 AssertIsOnOwningThread();
736 aVisitor.mCanHandle = true;
737 aVisitor.SetParentTarget(mMutableFile, false);
740 // virtual
741 JSObject* IDBFileHandle::WrapObject(JSContext* aCx,
742 JS::Handle<JSObject*> aGivenProto) {
743 AssertIsOnOwningThread();
745 return IDBFileHandle_Binding::Wrap(aCx, this, aGivenProto);
748 } // namespace dom
749 } // namespace mozilla