| /* |
| * Copyright (C) 2010 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_REQUEST_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_REQUEST_H_ |
| |
| #include <memory> |
| |
| #include "base/macros.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "third_party/blink/public/common/indexeddb/web_idb_types.h" |
| #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h" |
| #include "third_party/blink/public/platform/web_blob_info.h" |
| #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h" |
| #include "third_party/blink/renderer/bindings/core/v8/script_value.h" |
| #include "third_party/blink/renderer/bindings/modules/v8/idb_object_store_or_idb_index.h" |
| #include "third_party/blink/renderer/bindings/modules/v8/idb_object_store_or_idb_index_or_idb_cursor.h" |
| #include "third_party/blink/renderer/core/dom/context_lifecycle_observer.h" |
| #include "third_party/blink/renderer/core/dom/dom_string_list.h" |
| #include "third_party/blink/renderer/core/dom/events/event_listener.h" |
| #include "third_party/blink/renderer/core/dom/events/event_queue.h" |
| #include "third_party/blink/renderer/core/dom/events/event_target.h" |
| #include "third_party/blink/renderer/modules/event_modules.h" |
| #include "third_party/blink/renderer/modules/indexeddb/idb_any.h" |
| #include "third_party/blink/renderer/modules/indexeddb/idb_transaction.h" |
| #include "third_party/blink/renderer/modules/indexeddb/indexed_db.h" |
| #include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h" |
| #include "third_party/blink/renderer/modules/modules_export.h" |
| #include "third_party/blink/renderer/platform/bindings/script_state.h" |
| #include "third_party/blink/renderer/platform/blob/blob_data.h" |
| #include "third_party/blink/renderer/platform/heap/handle.h" |
| #include "third_party/blink/renderer/platform/wtf/hash_map.h" |
| #include "third_party/blink/renderer/platform/wtf/time.h" |
| |
| namespace blink { |
| |
| class DOMException; |
| class ExceptionState; |
| class IDBCursor; |
| struct IDBDatabaseMetadata; |
| class IDBValue; |
| |
| class MODULES_EXPORT IDBRequest : public EventTargetWithInlineData, |
| public ActiveScriptWrappable<IDBRequest>, |
| public ContextLifecycleObserver { |
| DEFINE_WRAPPERTYPEINFO(); |
| USING_GARBAGE_COLLECTED_MIXIN(IDBRequest); |
| |
| public: |
| using Source = IDBObjectStoreOrIDBIndexOrIDBCursor; |
| // Container for async tracing state. |
| // |
| // The documentation for TRACE_EVENT_ASYNC_{BEGIN,END} suggests identifying |
| // trace events by using pointers or a counter that is always incremented on |
| // the same thread. This is not viable for IndexedDB, because the same object |
| // can result in multiple trace events (requests associated with cursors), and |
| // IndexedDB can be used from multiple threads in the same renderer (workers). |
| // Furthermore, we want to record the beginning event of an async trace right |
| // when we start serving an IDB API call, before the IDBRequest object is |
| // created, so we can't rely on information in an IDBRequest. |
| // |
| // This class solves the ID uniqueness problem by relying on an atomic counter |
| // to generating unique IDs in a threadsafe manner. The atomic machinery is |
| // used when tracing is enabled. The recording problem is solved by having |
| // instances of this class store the information needed to record async trace |
| // end events (via TRACE_EVENT_ASYNC_END). |
| // |
| // From a mechanical perspective, creating an AsyncTraceState instance records |
| // the beginning event of an async trace. The instance is then moved into an |
| // IDBRequest, which records the async trace's end event at the right time. |
| class MODULES_EXPORT AsyncTraceState { |
| public: |
| // Creates an empty instance, which does not produce any tracing events. |
| // |
| // This is used for internal requests that should not show up in an |
| // application's trace. Examples of internal requests are the requests |
| // issued by DevTools, and the requests used to populate indexes. |
| explicit AsyncTraceState() = default; |
| |
| // Creates an instance that produces begin/end events with the given name. |
| // |
| // The string pointed to by tracing_name argument must live for the entire |
| // application. The easiest way to meet this requirement is to have it be a |
| // string literal. |
| explicit AsyncTraceState(const char* trace_event_name); |
| ~AsyncTraceState(); |
| |
| // Used to transfer the trace end event state to an IDBRequest. |
| AsyncTraceState(AsyncTraceState&& other) { |
| DCHECK(IsEmpty()); |
| this->trace_event_name_ = other.trace_event_name_; |
| this->id_ = other.id_; |
| other.trace_event_name_ = nullptr; |
| } |
| AsyncTraceState& operator=(AsyncTraceState&& rhs) { |
| DCHECK(IsEmpty()); |
| this->trace_event_name_ = rhs.trace_event_name_; |
| this->id_ = rhs.id_; |
| rhs.trace_event_name_ = nullptr; |
| return *this; |
| } |
| |
| // True if this instance does not store information for a tracing end event. |
| // |
| // An instance is cleared when RecordAndReset() is called on it, or when its |
| // state is moved into a different instance. Empty instances are also |
| // produced by the AsyncStateTrace() constructor. |
| bool IsEmpty() const { return !trace_event_name_; } |
| |
| // Records the trace end event whose information is stored in this instance. |
| // |
| // The method results in the completion of the async trace tracked by this |
| // instance, so the instance is cleared. |
| void RecordAndReset(); |
| |
| protected: // For testing |
| const char* trace_event_name() const { return trace_event_name_; } |
| size_t id() const { return id_; } |
| |
| // Populates the instance with state for a new async trace. |
| // |
| // The method uses the given even name and generates a new unique ID. The |
| // newly generated unique ID is returned. |
| size_t PopulateForNewEvent(const char* trace_event_name); |
| |
| private: |
| // The name of the async trace events tracked by this instance. |
| // |
| // Null is used to signal that the instance is empty, so the event name |
| // cannot be null. |
| const char* trace_event_name_ = nullptr; |
| // Uniquely generated ID that ties an async trace's begin and end events. |
| size_t id_ = 0; |
| |
| DISALLOW_COPY_AND_ASSIGN(AsyncTraceState); |
| }; |
| |
| static IDBRequest* Create(ScriptState*, |
| IDBIndex* source, |
| IDBTransaction*, |
| AsyncTraceState); |
| static IDBRequest* Create(ScriptState*, |
| IDBObjectStore* source, |
| IDBTransaction*, |
| AsyncTraceState); |
| static IDBRequest* Create(ScriptState*, |
| IDBCursor*, |
| IDBTransaction* source, |
| AsyncTraceState); |
| static IDBRequest* Create(ScriptState*, |
| const Source&, |
| IDBTransaction*, |
| AsyncTraceState); |
| |
| IDBRequest(ScriptState*, const Source&, IDBTransaction*, AsyncTraceState); |
| ~IDBRequest() override; |
| |
| void Trace(blink::Visitor*) override; |
| |
| v8::Isolate* GetIsolate() const { return isolate_; } |
| ScriptValue result(ScriptState*, ExceptionState&); |
| DOMException* error(ExceptionState&) const; |
| void source(ScriptState*, Source&) const; |
| IDBTransaction* transaction() const { return transaction_.Get(); } |
| |
| bool isResultDirty() const { return result_dirty_; } |
| IDBAny* ResultAsAny() const { return result_; } |
| |
| // Requests made during index population are implementation details and so |
| // events should not be visible to script. |
| void PreventPropagation() { prevent_propagation_ = true; } |
| |
| // Defined in the IDL |
| enum ReadyState { PENDING = 1, DONE = 2, kEarlyDeath = 3 }; |
| |
| const String& readyState() const; |
| |
| // Returns a new WebIDBCallbacks for this request. |
| // |
| // Each call must be paired with a WebCallbacksDestroyed() call. Most requests |
| // have a single WebIDBCallbacks instance created for them. |
| // |
| // Requests used to open and iterate cursors are special, because they are |
| // reused between openCursor() and continue() / advance() calls. These |
| // requests have a new WebIDBCallbacks instance created for each of the |
| // above-mentioned calls that they are involved in. |
| std::unique_ptr<WebIDBCallbacks> CreateWebCallbacks(); |
| void WebCallbacksDestroyed() { |
| DCHECK(web_callbacks_); |
| web_callbacks_ = nullptr; |
| } |
| #if DCHECK_IS_ON() |
| WebIDBCallbacks* WebCallbacks() const { return web_callbacks_; } |
| #endif // DCHECK_IS_ON() |
| |
| DEFINE_ATTRIBUTE_EVENT_LISTENER(success, kSuccess); |
| DEFINE_ATTRIBUTE_EVENT_LISTENER(error, kError); |
| |
| void SetCursorDetails(indexed_db::CursorType, mojom::IDBCursorDirection); |
| void SetPendingCursor(IDBCursor*); |
| void Abort(); |
| |
| // Blink's delivery of results from IndexedDB's backing store to script is |
| // more complicated than prescribed in the IndexedDB specification. |
| // |
| // IDBValue, which holds responses from the backing store, is either the |
| // serialized V8 value, or a reference to a Blob that holds the serialized |
| // value. IDBValueWrapping.h has the motivation and details. This introduces |
| // the following complexities. |
| // |
| // 1) De-serialization is expensive, so it is done lazily in |
| // IDBRequest::result(), which is called synchronously from script. On the |
| // other hand, Blob data can only be fetched asynchronously. So, IDBValues |
| // that reference serialized data stored in Blobs must be processed before |
| // IDBRequest event handlers are invoked, because the event handler script may |
| // call IDBRequest::result(). |
| // |
| // 2) The IDBRequest events must be dispatched (enqueued in DOMWindow's event |
| // queue) in the order in which the requests were issued. If an IDBValue |
| // references a Blob, the Blob processing must block event dispatch for all |
| // following IDBRequests in the same transaction. |
| // |
| // The Blob de-referencing and IDBRequest blocking is performed in the |
| // HandleResponse() overloads below. Each HandleResponse() overload is paired |
| // with a matching EnqueueResponse() overload, which is called when an |
| // IDBRequest's result event can be delivered to the application. All the |
| // HandleResponse() variants include a fast path that calls directly into |
| // EnqueueResponse() if no queueing is required. |
| // |
| // Some types of requests, such as indexedDB.openDatabase(), cannot be issued |
| // after a request that needs Blob processing, so their results are handled by |
| // having WebIDBCallbacksImpl call directly into EnqueueResponse(), |
| // EnqueueBlocked(), or EnqueueUpgradeNeeded(). |
| |
| void HandleResponse(DOMException*); |
| void HandleResponse(std::unique_ptr<IDBKey>); |
| void HandleResponse(std::unique_ptr<WebIDBCursor>, |
| std::unique_ptr<IDBKey>, |
| std::unique_ptr<IDBKey> primary_key, |
| std::unique_ptr<IDBValue>); |
| void HandleResponse(std::unique_ptr<IDBKey>, |
| std::unique_ptr<IDBKey> primary_key, |
| std::unique_ptr<IDBValue>); |
| void HandleResponse(std::unique_ptr<IDBValue>); |
| void HandleResponse(Vector<std::unique_ptr<IDBValue>>); |
| void HandleResponse(int64_t); |
| void HandleResponse(); |
| |
| // Only used in webkitGetDatabaseNames(), which is deprecated and hopefully |
| // going away soon. |
| void EnqueueResponse(const Vector<String>&); |
| |
| // Only IDBOpenDBRequest instances should receive these: |
| virtual void EnqueueBlocked(int64_t old_version) { NOTREACHED(); } |
| virtual void EnqueueUpgradeNeeded(int64_t old_version, |
| std::unique_ptr<WebIDBDatabase>, |
| const IDBDatabaseMetadata&, |
| mojom::IDBDataLoss, |
| String data_loss_message) { |
| NOTREACHED(); |
| } |
| virtual void EnqueueResponse(std::unique_ptr<WebIDBDatabase>, |
| const IDBDatabaseMetadata&) { |
| NOTREACHED(); |
| } |
| |
| // ScriptWrappable |
| bool HasPendingActivity() const final; |
| |
| // ContextLifecycleObserver |
| void ContextDestroyed(ExecutionContext*) override; |
| |
| // EventTarget |
| const AtomicString& InterfaceName() const override; |
| ExecutionContext* GetExecutionContext() const final; |
| |
| // Called by a version change transaction that has finished to set this |
| // request back from DONE (following "upgradeneeded") back to PENDING (for |
| // the upcoming "success" or "error"). |
| void TransactionDidFinishAndDispatch(); |
| |
| IDBCursor* GetResultCursor() const; |
| |
| // Used to hang onto Blobs until the browser process handles the request. |
| // |
| // Blobs are ref-counted on the browser side, and BlobDataHandles manage |
| // references from renderers. When a BlobDataHandle gets destroyed, the |
| // browser-side Blob gets derefenced, which might cause it to be destroyed as |
| // well. |
| // |
| // After script uses a Blob in a put() request, the Blink-side Blob object |
| // (which hangs onto the BlobDataHandle) may get garbage-collected. IDBRequest |
| // needs to hang onto the BlobDataHandle as well, to avoid having the |
| // browser-side Blob get destroyed before the IndexedDB request is processed. |
| inline Vector<scoped_refptr<BlobDataHandle>>& transit_blob_handles() { |
| return transit_blob_handles_; |
| } |
| |
| #if DCHECK_IS_ON() |
| inline bool TransactionHasQueuedResults() const { |
| return transaction_ && transaction_->HasQueuedResults(); |
| } |
| #endif // DCHECK_IS_ON() |
| |
| #if DCHECK_IS_ON() |
| inline IDBRequestQueueItem* QueueItem() const { return queue_item_; } |
| #endif // DCHECK_IS_ON() |
| |
| void AssignNewMetrics(AsyncTraceState metrics) { |
| DCHECK(metrics_.IsEmpty()); |
| metrics_ = std::move(metrics); |
| } |
| |
| protected: |
| void EnqueueEvent(Event*); |
| virtual bool ShouldEnqueueEvent() const; |
| void EnqueueResultInternal(IDBAny*); |
| void SetResult(IDBAny*); |
| |
| // Overridden by IDBOpenDBRequest. |
| virtual void EnqueueResponse(int64_t); |
| |
| // EventTarget |
| DispatchEventResult DispatchEventInternal(Event&) override; |
| |
| // Can be nullptr for requests that are not associated with a transaction, |
| // i.e. delete requests and completed or unsuccessful open requests. |
| Member<IDBTransaction> transaction_; |
| |
| ReadyState ready_state_ = PENDING; |
| bool request_aborted_ = false; // May be aborted by transaction then receive |
| // async onsuccess; ignore vs. assert. |
| // Maintain the isolate so that all externally allocated memory can be |
| // registered against it. |
| v8::Isolate* isolate_; |
| |
| AsyncTraceState metrics_; |
| |
| private: |
| // Calls EnqueueResponse(). |
| friend class IDBRequestQueueItem; |
| |
| void SetResultCursor(IDBCursor*, |
| std::unique_ptr<IDBKey>, |
| std::unique_ptr<IDBKey> primary_key, |
| std::unique_ptr<IDBValue>); |
| |
| void EnqueueResponse(DOMException*); |
| void EnqueueResponse(std::unique_ptr<IDBKey>); |
| void EnqueueResponse(std::unique_ptr<WebIDBCursor>, |
| std::unique_ptr<IDBKey>, |
| std::unique_ptr<IDBKey> primary_key, |
| std::unique_ptr<IDBValue>); |
| void EnqueueResponse(std::unique_ptr<IDBKey>, |
| std::unique_ptr<IDBKey> primary_key, |
| std::unique_ptr<IDBValue>); |
| void EnqueueResponse(std::unique_ptr<IDBValue>); |
| void EnqueueResponse(Vector<std::unique_ptr<IDBValue>>); |
| void EnqueueResponse(); |
| |
| void ClearPutOperationBlobs() { transit_blob_handles_.clear(); } |
| |
| Source source_; |
| Member<IDBAny> result_; |
| Member<DOMException> error_; |
| |
| bool has_pending_activity_ = true; |
| Member<EventQueue> event_queue_; |
| |
| // Only used if the result type will be a cursor. |
| indexed_db::CursorType cursor_type_ = indexed_db::kCursorKeyAndValue; |
| mojom::IDBCursorDirection cursor_direction_ = mojom::IDBCursorDirection::Next; |
| // When a cursor is continued/advanced, |result_| is cleared and |
| // |pendingCursor_| holds it. |
| Member<IDBCursor> pending_cursor_; |
| // New state is not applied to the cursor object until the event is |
| // dispatched. |
| std::unique_ptr<IDBKey> cursor_key_; |
| std::unique_ptr<IDBKey> cursor_primary_key_; |
| std::unique_ptr<IDBValue> cursor_value_; |
| |
| Vector<scoped_refptr<BlobDataHandle>> transit_blob_handles_; |
| |
| bool did_fire_upgrade_needed_event_ = false; |
| bool prevent_propagation_ = false; |
| bool result_dirty_ = true; |
| |
| // Pointer back to the WebIDBCallbacks that holds a persistent reference to |
| // this object. |
| WebIDBCallbacks* web_callbacks_ = nullptr; |
| |
| // Non-null while this request is queued behind other requests that are still |
| // getting post-processed. |
| // |
| // The IDBRequestQueueItem is owned by the result queue in IDBTransaction. |
| IDBRequestQueueItem* queue_item_ = nullptr; |
| }; |
| |
| } // namespace blink |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_REQUEST_H_ |