blob: 640051ef99ab6e3c3369326842c627957fcd2e1b [file] [log] [blame]
/*
* 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_