1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_REQUEST_H_
30 #define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_REQUEST_H_
31 
32 #include <memory>
33 
34 #include "base/macros.h"
35 #include "base/memory/scoped_refptr.h"
36 #include "third_party/blink/public/common/indexeddb/web_idb_types.h"
37 #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink-forward.h"
38 #include "third_party/blink/public/platform/web_blob_info.h"
39 #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
40 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
41 #include "third_party/blink/renderer/bindings/modules/v8/idb_object_store_or_idb_index.h"
42 #include "third_party/blink/renderer/bindings/modules/v8/idb_object_store_or_idb_index_or_idb_cursor.h"
43 #include "third_party/blink/renderer/core/dom/dom_string_list.h"
44 #include "third_party/blink/renderer/core/dom/events/event_listener.h"
45 #include "third_party/blink/renderer/core/dom/events/event_queue.h"
46 #include "third_party/blink/renderer/core/dom/events/event_target.h"
47 #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
48 #include "third_party/blink/renderer/modules/event_modules.h"
49 #include "third_party/blink/renderer/modules/indexeddb/idb_any.h"
50 #include "third_party/blink/renderer/modules/indexeddb/idb_transaction.h"
51 #include "third_party/blink/renderer/modules/indexeddb/indexed_db.h"
52 #include "third_party/blink/renderer/modules/indexeddb/web_idb_cursor.h"
53 #include "third_party/blink/renderer/modules/modules_export.h"
54 #include "third_party/blink/renderer/platform/bindings/script_state.h"
55 #include "third_party/blink/renderer/platform/blob/blob_data.h"
56 #include "third_party/blink/renderer/platform/heap/handle.h"
57 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
58 
59 namespace blink {
60 
61 class DOMException;
62 class ExceptionState;
63 class IDBCursor;
64 struct IDBDatabaseMetadata;
65 class IDBValue;
66 
67 class MODULES_EXPORT IDBRequest : public EventTargetWithInlineData,
68                                   public ActiveScriptWrappable<IDBRequest>,
69                                   public ExecutionContextLifecycleObserver {
70   DEFINE_WRAPPERTYPEINFO();
71 
72  public:
73   using Source = IDBObjectStoreOrIDBIndexOrIDBCursor;
74   // Container for async tracing state.
75   //
76   // The documentation for TRACE_EVENT_NESTABLE_ASYNC_{BEGIN,END} suggests
77   // identifying trace events by using pointers or a counter that is always
78   // incremented on the same thread. This is not viable for IndexedDB, because
79   // the same object can result in multiple trace events (requests associated
80   // with cursors), and IndexedDB can be used from multiple threads in the same
81   // renderer (workers). Furthermore, we want to record the beginning event of
82   // an async trace right when we start serving an IDB API call, before the
83   // IDBRequest object is created, so we can't rely on information in an
84   // IDBRequest.
85   //
86   // This class solves the ID uniqueness problem by relying on an atomic counter
87   // to generating unique IDs in a threadsafe manner. The atomic machinery is
88   // used when tracing is enabled. The recording problem is solved by having
89   // instances of this class store the information needed to record async trace
90   // end events (via TRACE_EVENT_NESTABLE_ASYNC_END).
91   //
92   // From a mechanical perspective, creating an AsyncTraceState instance records
93   // the beginning event of an async trace. The instance is then moved into an
94   // IDBRequest, which records the async trace's end event at the right time.
95   class MODULES_EXPORT AsyncTraceState {
96    public:
97     // Creates an empty instance, which does not produce any tracing events.
98     //
99     // This is used for internal requests that should not show up in an
100     // application's trace. Examples of internal requests are the requests
101     // issued by DevTools, and the requests used to populate indexes.
102     explicit AsyncTraceState() = default;
103 
104     // Creates an instance that produces begin/end events with the given name.
105     //
106     // The string pointed to by tracing_name argument must live for the entire
107     // application. The easiest way to meet this requirement is to have it be a
108     // string literal.
109     explicit AsyncTraceState(const char* trace_event_name);
110     ~AsyncTraceState();
111 
112     // Used to transfer the trace end event state to an IDBRequest.
AsyncTraceState(AsyncTraceState && other)113     AsyncTraceState(AsyncTraceState&& other) {
114       DCHECK(IsEmpty());
115       this->trace_event_name_ = other.trace_event_name_;
116       this->id_ = other.id_;
117       other.trace_event_name_ = nullptr;
118     }
119     AsyncTraceState& operator=(AsyncTraceState&& rhs) {
120       DCHECK(IsEmpty());
121       this->trace_event_name_ = rhs.trace_event_name_;
122       this->id_ = rhs.id_;
123       rhs.trace_event_name_ = nullptr;
124       return *this;
125     }
126 
127     // True if this instance does not store information for a tracing end event.
128     //
129     // An instance is cleared when RecordAndReset() is called on it, or when its
130     // state is moved into a different instance. Empty instances are also
131     // produced by the AsyncStateTrace() constructor.
IsEmpty()132     bool IsEmpty() const { return !trace_event_name_; }
133 
134     // Records the trace end event whose information is stored in this instance.
135     //
136     // The method results in the completion of the async trace tracked by this
137     // instance, so the instance is cleared.
138     void RecordAndReset();
139 
140    protected:  // For testing
trace_event_name()141     const char* trace_event_name() const { return trace_event_name_; }
id()142     size_t id() const { return id_; }
143 
144     // Populates the instance with state for a new async trace.
145     //
146     // The method uses the given even name and generates a new unique ID. The
147     // newly generated unique ID is returned.
148     size_t PopulateForNewEvent(const char* trace_event_name);
149 
150    private:
151     friend class IDBRequest;
152 
153     // The name of the async trace events tracked by this instance.
154     //
155     // Null is used to signal that the instance is empty, so the event name
156     // cannot be null.
157     const char* trace_event_name_ = nullptr;
158     // Uniquely generated ID that ties an async trace's begin and end events.
159     size_t id_ = 0;
160 
161     DISALLOW_COPY_AND_ASSIGN(AsyncTraceState);
162   };
163 
164   static IDBRequest* Create(ScriptState*,
165                             IDBIndex* source,
166                             IDBTransaction*,
167                             AsyncTraceState);
168   static IDBRequest* Create(ScriptState*,
169                             IDBObjectStore* source,
170                             IDBTransaction*,
171                             AsyncTraceState);
172   static IDBRequest* Create(ScriptState*,
173                             IDBCursor*,
174                             IDBTransaction* source,
175                             AsyncTraceState);
176   static IDBRequest* Create(ScriptState*,
177                             const Source&,
178                             IDBTransaction*,
179                             AsyncTraceState);
180 
181   IDBRequest(ScriptState*, const Source&, IDBTransaction*, AsyncTraceState);
182   ~IDBRequest() override;
183 
184   void Trace(Visitor*) const override;
185 
GetIsolate()186   v8::Isolate* GetIsolate() const { return isolate_; }
187   ScriptValue result(ScriptState*, ExceptionState&);
188   DOMException* error(ExceptionState&) const;
189   void source(ScriptState*, Source&) const;
transaction()190   IDBTransaction* transaction() const { return transaction_.Get(); }
191 
isResultDirty()192   bool isResultDirty() const { return result_dirty_; }
ResultAsAny()193   IDBAny* ResultAsAny() const { return result_; }
194 
195   // Requests made during index population are implementation details and so
196   // events should not be visible to script.
PreventPropagation()197   void PreventPropagation() { prevent_propagation_ = true; }
198 
199   // Defined in the IDL
200   enum ReadyState { PENDING = 1, DONE = 2, kEarlyDeath = 3 };
201 
202   const String& readyState() const;
203 
204   // Returns a new WebIDBCallbacks for this request.
205   //
206   // Each call must be paired with a WebCallbacksDestroyed() call. Most requests
207   // have a single WebIDBCallbacks instance created for them.
208   //
209   // Requests used to open and iterate cursors are special, because they are
210   // reused between openCursor() and continue() / advance() calls. These
211   // requests have a new WebIDBCallbacks instance created for each of the
212   // above-mentioned calls that they are involved in.
213   std::unique_ptr<WebIDBCallbacks> CreateWebCallbacks();
WebCallbacksDestroyed()214   void WebCallbacksDestroyed() {
215     DCHECK(web_callbacks_);
216     web_callbacks_ = nullptr;
217   }
218 #if DCHECK_IS_ON()
WebCallbacks()219   WebIDBCallbacks* WebCallbacks() const { return web_callbacks_; }
220 #endif  // DCHECK_IS_ON()
221 
222   DEFINE_ATTRIBUTE_EVENT_LISTENER(success, kSuccess)
223   DEFINE_ATTRIBUTE_EVENT_LISTENER(error, kError)
224 
225   void SetCursorDetails(indexed_db::CursorType, mojom::IDBCursorDirection);
226   void SetPendingCursor(IDBCursor*);
227   void Abort();
228 
229   // Blink's delivery of results from IndexedDB's backing store to script is
230   // more complicated than prescribed in the IndexedDB specification.
231   //
232   // IDBValue, which holds responses from the backing store, is either the
233   // serialized V8 value, or a reference to a Blob that holds the serialized
234   // value. IDBValueWrapping.h has the motivation and details. This introduces
235   // the following complexities.
236   //
237   // 1) De-serialization is expensive, so it is done lazily in
238   // IDBRequest::result(), which is called synchronously from script. On the
239   // other hand, Blob data can only be fetched asynchronously. So, IDBValues
240   // that reference serialized data stored in Blobs must be processed before
241   // IDBRequest event handlers are invoked, because the event handler script may
242   // call IDBRequest::result().
243   //
244   // 2) The IDBRequest events must be dispatched (enqueued in DOMWindow's event
245   // queue) in the order in which the requests were issued. If an IDBValue
246   // references a Blob, the Blob processing must block event dispatch for all
247   // following IDBRequests in the same transaction.
248   //
249   // The Blob de-referencing and IDBRequest blocking is performed in the
250   // HandleResponse() overloads below. Each HandleResponse() overload is paired
251   // with a matching EnqueueResponse() overload, which is called when an
252   // IDBRequest's result event can be delivered to the application. All the
253   // HandleResponse() variants include a fast path that calls directly into
254   // EnqueueResponse() if no queueing is required.
255   //
256   // Some types of requests, such as indexedDB.openDatabase(), cannot be issued
257   // after a request that needs Blob processing, so their results are handled by
258   // having WebIDBCallbacksImpl call directly into EnqueueResponse(),
259   // EnqueueBlocked(), or EnqueueUpgradeNeeded().
260 
261   void HandleResponse(DOMException*);
262   void HandleResponse(std::unique_ptr<IDBKey>);
263   void HandleResponse(std::unique_ptr<WebIDBCursor>,
264                       std::unique_ptr<IDBKey>,
265                       std::unique_ptr<IDBKey> primary_key,
266                       std::unique_ptr<IDBValue>);
267   void HandleResponse(std::unique_ptr<IDBKey>,
268                       std::unique_ptr<IDBKey> primary_key,
269                       std::unique_ptr<IDBValue>);
270   void HandleResponse(std::unique_ptr<IDBValue>);
271   void HandleResponse(Vector<std::unique_ptr<IDBValue>>);
272   void HandleResponse(int64_t);
273   void HandleResponse();
274   void HandleResponse(
275       bool key_only,
276       mojo::PendingReceiver<mojom::blink::IDBDatabaseGetAllResultSink>
277           receiver);
278 
279   // Only used in webkitGetDatabaseNames(), which is deprecated and hopefully
280   // going away soon.
281   void EnqueueResponse(const Vector<String>&);
282 
283   // Only IDBOpenDBRequest instances should receive these:
EnqueueBlocked(int64_t old_version)284   virtual void EnqueueBlocked(int64_t old_version) { NOTREACHED(); }
EnqueueUpgradeNeeded(int64_t old_version,std::unique_ptr<WebIDBDatabase>,const IDBDatabaseMetadata &,mojom::IDBDataLoss,String data_loss_message)285   virtual void EnqueueUpgradeNeeded(int64_t old_version,
286                                     std::unique_ptr<WebIDBDatabase>,
287                                     const IDBDatabaseMetadata&,
288                                     mojom::IDBDataLoss,
289                                     String data_loss_message) {
290     NOTREACHED();
291   }
EnqueueResponse(std::unique_ptr<WebIDBDatabase>,const IDBDatabaseMetadata &)292   virtual void EnqueueResponse(std::unique_ptr<WebIDBDatabase>,
293                                const IDBDatabaseMetadata&) {
294     NOTREACHED();
295   }
296 
297   // ScriptWrappable
298   bool HasPendingActivity() const final;
299 
300   // ExecutionContextLifecycleObserver
301   void ContextDestroyed() override;
302 
303   // EventTarget
304   const AtomicString& InterfaceName() const override;
305   ExecutionContext* GetExecutionContext() const final;
306 
307   // Called by a version change transaction that has finished to set this
308   // request back from DONE (following "upgradeneeded") back to PENDING (for
309   // the upcoming "success" or "error").
310   void TransactionDidFinishAndDispatch();
311 
312   IDBCursor* GetResultCursor() const;
313 
314   // Used to hang onto Blobs until the browser process handles the request.
315   //
316   // Blobs are ref-counted on the browser side, and BlobDataHandles manage
317   // references from renderers. When a BlobDataHandle gets destroyed, the
318   // browser-side Blob gets derefenced, which might cause it to be destroyed as
319   // well.
320   //
321   // After script uses a Blob in a put() request, the Blink-side Blob object
322   // (which hangs onto the BlobDataHandle) may get garbage-collected. IDBRequest
323   // needs to hang onto the BlobDataHandle as well, to avoid having the
324   // browser-side Blob get destroyed before the IndexedDB request is processed.
transit_blob_handles()325   inline Vector<scoped_refptr<BlobDataHandle>>& transit_blob_handles() {
326     return transit_blob_handles_;
327   }
328 
329 #if DCHECK_IS_ON()
TransactionHasQueuedResults()330   inline bool TransactionHasQueuedResults() const {
331     return transaction_ && transaction_->HasQueuedResults();
332   }
333 #endif  // DCHECK_IS_ON()
334 
335 #if DCHECK_IS_ON()
QueueItem()336   inline IDBRequestQueueItem* QueueItem() const { return queue_item_; }
337 #endif  // DCHECK_IS_ON()
338 
AssignNewMetrics(AsyncTraceState metrics)339   void AssignNewMetrics(AsyncTraceState metrics) {
340     DCHECK(metrics_.IsEmpty());
341     metrics_ = std::move(metrics);
342   }
343 
344  protected:
345   void EnqueueEvent(Event*);
346   virtual bool ShouldEnqueueEvent() const;
347   void EnqueueResultInternal(IDBAny*);
348   void SetResult(IDBAny*);
349 
350   // Overridden by IDBOpenDBRequest.
351   virtual void EnqueueResponse(int64_t);
352 
353   // EventTarget
354   DispatchEventResult DispatchEventInternal(Event&) override;
355 
356   // Can be nullptr for requests that are not associated with a transaction,
357   // i.e. delete requests and completed or unsuccessful open requests.
358   Member<IDBTransaction> transaction_;
359 
360   ReadyState ready_state_ = PENDING;
361   bool request_aborted_ = false;  // May be aborted by transaction then receive
362                                   // async onsuccess; ignore vs. assert.
363   // Maintain the isolate so that all externally allocated memory can be
364   // registered against it.
365   v8::Isolate* isolate_;
366 
367   AsyncTraceState metrics_;
368 
369  private:
370   // Calls EnqueueResponse().
371   friend class IDBRequestQueueItem;
372 
373   void SetResultCursor(IDBCursor*,
374                        std::unique_ptr<IDBKey>,
375                        std::unique_ptr<IDBKey> primary_key,
376                        std::unique_ptr<IDBValue>);
377 
378   void EnqueueResponse(DOMException*);
379   void EnqueueResponse(std::unique_ptr<IDBKey>);
380   void EnqueueResponse(std::unique_ptr<WebIDBCursor>,
381                        std::unique_ptr<IDBKey>,
382                        std::unique_ptr<IDBKey> primary_key,
383                        std::unique_ptr<IDBValue>);
384   void EnqueueResponse(std::unique_ptr<IDBKey>,
385                        std::unique_ptr<IDBKey> primary_key,
386                        std::unique_ptr<IDBValue>);
387   void EnqueueResponse(std::unique_ptr<IDBValue>);
388   void EnqueueResponse(Vector<std::unique_ptr<IDBValue>>);
389   void EnqueueResponse();
390 
ClearPutOperationBlobs()391   void ClearPutOperationBlobs() { transit_blob_handles_.clear(); }
392 
393   Source source_;
394   Member<IDBAny> result_;
395   Member<DOMException> error_;
396 
397   bool has_pending_activity_ = true;
398   Member<EventQueue> event_queue_;
399 
400   // Only used if the result type will be a cursor.
401   indexed_db::CursorType cursor_type_ = indexed_db::kCursorKeyAndValue;
402   mojom::IDBCursorDirection cursor_direction_ = mojom::IDBCursorDirection::Next;
403   // When a cursor is continued/advanced, |result_| is cleared and
404   // |pendingCursor_| holds it.
405   Member<IDBCursor> pending_cursor_;
406   // New state is not applied to the cursor object until the event is
407   // dispatched.
408   std::unique_ptr<IDBKey> cursor_key_;
409   std::unique_ptr<IDBKey> cursor_primary_key_;
410   std::unique_ptr<IDBValue> cursor_value_;
411 
412   Vector<scoped_refptr<BlobDataHandle>> transit_blob_handles_;
413 
414   bool did_fire_upgrade_needed_event_ = false;
415   bool prevent_propagation_ = false;
416   bool result_dirty_ = true;
417 
418   // Pointer back to the WebIDBCallbacks that holds a persistent reference to
419   // this object.
420   WebIDBCallbacks* web_callbacks_ = nullptr;
421 
422   // Non-null while this request is queued behind other requests that are still
423   // getting post-processed.
424   //
425   // The IDBRequestQueueItem is owned by the result queue in IDBTransaction.
426   IDBRequestQueueItem* queue_item_ = nullptr;
427 };
428 
429 }  // namespace blink
430 
431 #endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_REQUEST_H_
432