text: // Copyright (c) 2013 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_H_
  #define CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_H_
  
  #include <stddef.h>
  #include <stdint.h>
  
  #include <list>
  #include <map>
  #include <memory>
  #include <queue>
  #include <string>
  #include <tuple>
  #include <utility>
  #include <vector>
  
  #include "base/gtest_prod_util.h"
  #include "base/macros.h"
  #include "base/memory/ref_counted.h"
  #include "content/browser/indexed_db/indexed_db.h"
  #include "content/browser/indexed_db/indexed_db_backing_store.h"
  #include "content/browser/indexed_db/indexed_db_callbacks.h"
  #include "content/browser/indexed_db/indexed_db_observer.h"
  #include "content/browser/indexed_db/indexed_db_pending_connection.h"
  #include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
  #include "content/browser/indexed_db/list_set.h"
  #include "content/common/indexed_db/indexed_db_metadata.h"
  #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBTypes.h"
  
  namespace url {
  class Origin;
  }
  
  namespace content {
  
  class IndexedDBConnection;
  class IndexedDBDatabaseCallbacks;
  class IndexedDBFactory;
  class IndexedDBKey;
  class IndexedDBKeyPath;
  class IndexedDBKeyRange;
  class IndexedDBTransaction;
  struct IndexedDBValue;
  
  class CONTENT_EXPORT IndexedDBDatabase
      : NON_EXPORTED_BASE(public base::RefCounted<IndexedDBDatabase>) {
   public:
    // Identifier is pair of (origin, database name).
    using Identifier = std::pair<url::Origin, base::string16>;
  
    static const int64_t kInvalidId = 0;
    static const int64_t kMinimumIndexId = 30;
  
    static std::tuple<scoped_refptr<IndexedDBDatabase>, leveldb::Status> Create(
        const base::string16& name,
        scoped_refptr<IndexedDBBackingStore> backing_store,
        scoped_refptr<IndexedDBFactory> factory,
        const Identifier& unique_identifier);
  
    const Identifier& identifier() const { return identifier_; }
    IndexedDBBackingStore* backing_store() { return backing_store_.get(); }
  
    int64_t id() const { return metadata_.id; }
    const base::string16& name() const { return metadata_.name; }
    const url::Origin& origin() const { return identifier_.first; }
  
    void AddObjectStore(const IndexedDBObjectStoreMetadata& metadata,
                        int64_t new_max_object_store_id);
    void RemoveObjectStore(int64_t object_store_id);
    void SetObjectStoreName(int64_t object_store_id, const base::string16& name);
    void AddIndex(int64_t object_store_id,
                  const IndexedDBIndexMetadata& metadata,
                  int64_t new_max_index_id);
    void RemoveIndex(int64_t object_store_id, int64_t index_id);
    void SetIndexName(int64_t object_store_id,
                      int64_t index_id,
                      const base::string16& name);
  
    void OpenConnection(std::unique_ptr<IndexedDBPendingConnection> connection);
    void DeleteDatabase(scoped_refptr<IndexedDBCallbacks> callbacks,
                        bool force_close);
    const IndexedDBDatabaseMetadata& metadata() const { return metadata_; }
  
    void CreateObjectStore(IndexedDBTransaction* transaction,
                           int64_t object_store_id,
                           const base::string16& name,
                           const IndexedDBKeyPath& key_path,
                           bool auto_increment);
    void DeleteObjectStore(IndexedDBTransaction* transaction,
                           int64_t object_store_id);
    void RenameObjectStore(IndexedDBTransaction* transaction,
                           int64_t object_store_id,
                           const base::string16& new_name);
  
    // Returns a pointer to a newly created transaction. The object is owned
    // by |transaction_coordinator_|.
    IndexedDBTransaction* CreateTransaction(
        int64_t transaction_id,
        IndexedDBConnection* connection,
        const std::vector<int64_t>& object_store_ids,
        blink::WebIDBTransactionMode mode);
    void Close(IndexedDBConnection* connection, bool forced);
    void ForceClose();
  
    // Ack that one of the connections notified with a "versionchange" event did
    // not promptly close. Therefore a "blocked" event should be fired at the
    // pending connection.
    void VersionChangeIgnored();
  
    void Commit(IndexedDBTransaction* transaction);
  
    void CreateIndex(IndexedDBTransaction* transaction,
                     int64_t object_store_id,
                     int64_t index_id,
                     const base::string16& name,
                     const IndexedDBKeyPath& key_path,
                     bool unique,
                     bool multi_entry);
    void DeleteIndex(IndexedDBTransaction* transaction,
                     int64_t object_store_id,
                     int64_t index_id);
    void RenameIndex(IndexedDBTransaction* transaction,
                     int64_t object_store_id,
                     int64_t index_id,
                     const base::string16& new_name);
  
    IndexedDBTransactionCoordinator& transaction_coordinator() {
      return transaction_coordinator_;
    }
    const IndexedDBTransactionCoordinator& transaction_coordinator() const {
      return transaction_coordinator_;
    }
  
    void TransactionCreated(IndexedDBTransaction* transaction);
    void TransactionFinished(IndexedDBTransaction* transaction, bool committed);
  
    void AbortAllTransactionsForConnections();
  
    void AddPendingObserver(IndexedDBTransaction* transaction,
                            int32_t observer_id,
                            const IndexedDBObserver::Options& options);
  
    // |value| can be null for delete and clear operations.
    void FilterObservation(IndexedDBTransaction*,
                           int64_t object_store_id,
                           blink::WebIDBOperationType type,
                           const IndexedDBKeyRange& key_range,
                           const IndexedDBValue* value);
    void SendObservations(
        std::map<int32_t, ::indexed_db::mojom::ObserverChangesPtr> change_map);
  
    void Get(IndexedDBTransaction* transaction,
             int64_t object_store_id,
             int64_t index_id,
             std::unique_ptr<IndexedDBKeyRange> key_range,
             bool key_only,
             scoped_refptr<IndexedDBCallbacks> callbacks);
    void GetAll(IndexedDBTransaction* transaction,
                int64_t object_store_id,
                int64_t index_id,
                std::unique_ptr<IndexedDBKeyRange> key_range,
                bool key_only,
                int64_t max_count,
                scoped_refptr<IndexedDBCallbacks> callbacks);
    void Put(IndexedDBTransaction* transaction,
             int64_t object_store_id,
             IndexedDBValue* value,
             std::vector<std::unique_ptr<storage::BlobDataHandle>>* handles,
             std::unique_ptr<IndexedDBKey> key,
             blink::WebIDBPutMode mode,
             scoped_refptr<IndexedDBCallbacks> callbacks,
             const std::vector<IndexedDBIndexKeys>& index_keys);
    void SetIndexKeys(IndexedDBTransaction* transaction,
                      int64_t object_store_id,
                      std::unique_ptr<IndexedDBKey> primary_key,
                      const std::vector<IndexedDBIndexKeys>& index_keys);
    void SetIndexesReady(IndexedDBTransaction* transaction,
                         int64_t object_store_id,
                         const std::vector<int64_t>& index_ids);
    void OpenCursor(IndexedDBTransaction* transaction,
                    int64_t object_store_id,
                    int64_t index_id,
                    std::unique_ptr<IndexedDBKeyRange> key_range,
                    blink::WebIDBCursorDirection,
                    bool key_only,
                    blink::WebIDBTaskType task_type,
                    scoped_refptr<IndexedDBCallbacks> callbacks);
    void Count(IndexedDBTransaction* transaction,
               int64_t object_store_id,
               int64_t index_id,
               std::unique_ptr<IndexedDBKeyRange> key_range,
               scoped_refptr<IndexedDBCallbacks> callbacks);
    void DeleteRange(IndexedDBTransaction* transaction,
                     int64_t object_store_id,
                     std::unique_ptr<IndexedDBKeyRange> key_range,
                     scoped_refptr<IndexedDBCallbacks> callbacks);
    void Clear(IndexedDBTransaction* transaction,
               int64_t object_store_id,
               scoped_refptr<IndexedDBCallbacks> callbacks);
  
    // Number of connections that have progressed passed initial open call.
    size_t ConnectionCount() const { return connections_.size(); }
  
    // Number of active open/delete calls (running or blocked on other
    // connections).
    size_t ActiveOpenDeleteCount() const { return active_request_ ? 1 : 0; }
  
    // Number of open/delete calls that are waiting their turn.
    size_t PendingOpenDeleteCount() const { return pending_requests_.size(); }
  
    // Asynchronous tasks scheduled within transactions:
    void CreateObjectStoreAbortOperation(int64_t object_store_id);
    leveldb::Status DeleteObjectStoreOperation(int64_t object_store_id,
                                               IndexedDBTransaction* transaction);
    void DeleteObjectStoreAbortOperation(
        const IndexedDBObjectStoreMetadata& object_store_metadata);
    void RenameObjectStoreAbortOperation(int64_t object_store_id,
                                         const base::string16& old_name);
    leveldb::Status VersionChangeOperation(
        int64_t version,
        scoped_refptr<IndexedDBCallbacks> callbacks,
        IndexedDBTransaction* transaction);
    void VersionChangeAbortOperation(int64_t previous_version);
    leveldb::Status DeleteIndexOperation(int64_t object_store_id,
                                         int64_t index_id,
                                         IndexedDBTransaction* transaction);
    void CreateIndexAbortOperation(int64_t object_store_id, int64_t index_id);
    void DeleteIndexAbortOperation(int64_t object_store_id,
                                   const IndexedDBIndexMetadata& index_metadata);
    void RenameIndexAbortOperation(int64_t object_store_id,
                                   int64_t index_id,
                                   const base::string16& old_name);
    leveldb::Status GetOperation(int64_t object_store_id,
                                 int64_t index_id,
                                 std::unique_ptr<IndexedDBKeyRange> key_range,
                                 indexed_db::CursorType cursor_type,
                                 scoped_refptr<IndexedDBCallbacks> callbacks,
                                 IndexedDBTransaction* transaction);
    leveldb::Status GetAllOperation(int64_t object_store_id,
                                    int64_t index_id,
                                    std::unique_ptr<IndexedDBKeyRange> key_range,
                                    indexed_db::CursorType cursor_type,
                                    int64_t max_count,
                                    scoped_refptr<IndexedDBCallbacks> callbacks,
                                    IndexedDBTransaction* transaction);
    struct PutOperationParams;
    leveldb::Status PutOperation(std::unique_ptr<PutOperationParams> params,
                                 IndexedDBTransaction* transaction);
    leveldb::Status SetIndexesReadyOperation(size_t index_count,
                                             IndexedDBTransaction* transaction);
    struct OpenCursorOperationParams;
    leveldb::Status OpenCursorOperation(
        std::unique_ptr<OpenCursorOperationParams> params,
        IndexedDBTransaction* transaction);
    leveldb::Status CountOperation(int64_t object_store_id,
                                   int64_t index_id,
                                   std::unique_ptr<IndexedDBKeyRange> key_range,
                                   scoped_refptr<IndexedDBCallbacks> callbacks,
                                   IndexedDBTransaction* transaction);
    leveldb::Status DeleteRangeOperation(
        int64_t object_store_id,
        std::unique_ptr<IndexedDBKeyRange> key_range,
        scoped_refptr<IndexedDBCallbacks> callbacks,
        IndexedDBTransaction* transaction);
    leveldb::Status ClearOperation(int64_t object_store_id,
                                   scoped_refptr<IndexedDBCallbacks> callbacks,
                                   IndexedDBTransaction* transaction);
  
    // Called when a backing store operation has failed. The database will be
    // closed (IndexedDBFactory::ForceClose) during this call. This should NOT
    // be used in an method scheduled as a transaction operation.
    void ReportError(leveldb::Status status);
    void ReportErrorWithDetails(leveldb::Status status, const char* message);
  
    IndexedDBFactory* factory() const { return factory_.get(); }
  
   protected:
    friend class IndexedDBTransaction;
  
    IndexedDBDatabase(const base::string16& name,
                      scoped_refptr<IndexedDBBackingStore> backing_store,
                      scoped_refptr<IndexedDBFactory> factory,
                      const Identifier& unique_identifier);
    virtual ~IndexedDBDatabase();
  
    // May be overridden in tests.
    virtual size_t GetMaxMessageSizeInBytes() const;
  
   private:
    friend class base::RefCounted<IndexedDBDatabase>;
    friend class IndexedDBClassFactory;
  
    FRIEND_TEST_ALL_PREFIXES(IndexedDBDatabaseTest, OpenDeleteClear);
  
    void CallUpgradeTransactionStartedForTesting(int64_t old_version);
  
    class ConnectionRequest;
    class OpenRequest;
    class DeleteRequest;
  
    leveldb::Status OpenInternal();
  
    // Called internally when an open or delete request comes in. Processes
    // the queue immediately if there are no other requests.
    void AppendRequest(std::unique_ptr<ConnectionRequest> request);
  
    // Called by requests when complete. The request will be freed, so the
    // request must do no other work after calling this. If there are pending
    // requests, the queue will be synchronously processed.
    void RequestComplete(ConnectionRequest* request);
  
    // Pop the first request from the queue and start it.
    void ProcessRequestQueue();
  
    std::unique_ptr<IndexedDBConnection> CreateConnection(
        scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
        int child_process_id);
  
    bool ValidateObjectStoreId(int64_t object_store_id) const;
    bool ValidateObjectStoreIdAndIndexId(int64_t object_store_id,
                                         int64_t index_id) const;
    bool ValidateObjectStoreIdAndOptionalIndexId(int64_t object_store_id,
                                                 int64_t index_id) const;
    bool ValidateObjectStoreIdAndNewIndexId(int64_t object_store_id,
                                            int64_t index_id) const;
  
    scoped_refptr<IndexedDBBackingStore> backing_store_;
    IndexedDBDatabaseMetadata metadata_;
  
    const Identifier identifier_;
    scoped_refptr<IndexedDBFactory> factory_;
  
    IndexedDBTransactionCoordinator transaction_coordinator_;
    int64_t transaction_count_ = 0;
  
    list_set<IndexedDBConnection*> connections_;
  
    // This holds the first open or delete request that is currently being
    // processed. The request has already broadcast OnVersionChange if
    // necessary.
    std::unique_ptr<ConnectionRequest> active_request_;
  
    // This holds open or delete requests that are waiting for the active
    // request to be completed. The requests have not yet broadcast
    // OnVersionChange (if necessary).
    std::queue<std::unique_ptr<ConnectionRequest>> pending_requests_;
  
    // The |processing_pending_requests_| flag is set while ProcessRequestQueue()
    // is executing. It prevents rentrant calls if the active request completes
    // synchronously.
    bool processing_pending_requests_ = false;
  
    DISALLOW_COPY_AND_ASSIGN(IndexedDBDatabase);
  };
  
  }  // namespace content
  
  #endif  // CONTENT_BROWSER_INDEXED_DB_INDEXED_DB_DATABASE_H_
  