910e62b5创建于 1月15日历史提交
/*
 * 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.
 *
 * 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.
 */

#include "third_party/blink/renderer/modules/indexeddb/idb_index.h"

#include <limits>
#include <memory>
#include <utility>

#include "third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom-shared.h"
#include "third_party/blink/renderer/bindings/core/v8/to_v8_traits.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_idb_get_all_options.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_database.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_get_all_options_helper.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_key.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_object_store.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_request.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_transaction.h"
#include "third_party/blink/renderer/modules/indexeddb/indexed_db_blink_mojom_traits.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"

namespace blink {

IDBIndex::IDBIndex(scoped_refptr<IDBIndexMetadata> metadata,
                   IDBObjectStore* object_store,
                   IDBTransaction* transaction)
    : metadata_(std::move(metadata)),
      object_store_(object_store),
      transaction_(transaction) {
  DCHECK(object_store_);
  DCHECK(transaction_);
  DCHECK(metadata_.get());
  DCHECK_NE(Id(), IDBIndexMetadata::kInvalidId);
}

IDBIndex::~IDBIndex() = default;

void IDBIndex::Trace(Visitor* visitor) const {
  visitor->Trace(object_store_);
  visitor->Trace(transaction_);
  ScriptWrappable::Trace(visitor);
}

void IDBIndex::setName(const String& name, ExceptionState& exception_state) {
  TRACE_EVENT0("IndexedDB", "IDBIndex::setName");
  if (!transaction_->IsVersionChange()) {
    exception_state.ThrowDOMException(
        DOMExceptionCode::kInvalidStateError,
        IDBDatabase::kNotVersionChangeTransactionErrorMessage);
    return;
  }
  if (IsDeleted()) {
    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                      IDBDatabase::kIndexDeletedErrorMessage);
    return;
  }
  if (!transaction_->IsActive()) {
    exception_state.ThrowDOMException(
        DOMExceptionCode::kTransactionInactiveError,
        transaction_->InactiveErrorMessage());
    return;
  }

  if (this->name() == name)
    return;
  if (object_store_->ContainsIndex(name)) {
    exception_state.ThrowDOMException(DOMExceptionCode::kConstraintError,
                                      IDBDatabase::kIndexNameTakenErrorMessage);
    return;
  }
  if (!db().IsConnectionOpen()) {
    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                      IDBDatabase::kDatabaseClosedErrorMessage);
    return;
  }

  object_store_->RenameIndex(Id(), name);
}

ScriptValue IDBIndex::keyPath(ScriptState* script_state) const {
  return ScriptValue(script_state->GetIsolate(),
                     Metadata().key_path.ToV8(script_state));
}

void IDBIndex::RevertMetadata(scoped_refptr<IDBIndexMetadata> old_metadata) {
  metadata_ = std::move(old_metadata);

  // An index's metadata will only get reverted if the index was in the
  // database when the versionchange transaction started.
  deleted_ = false;
}

IDBRequest* IDBIndex::openCursor(ScriptState* script_state,
                                 const ScriptValue& range,
                                 const V8IDBCursorDirection& v8_direction,
                                 ExceptionState& exception_state) {
  TRACE_EVENT1("IndexedDB", "IDBIndex::openCursorRequestSetup", "index_name",
               metadata_->name.Utf8());
  IDBRequest::AsyncTraceState metrics(
      IDBRequest::TypeForMetrics::kIndexOpenCursor);
  if (IsDeleted()) {
    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                      IDBDatabase::kIndexDeletedErrorMessage);
    return nullptr;
  }
  if (!transaction_->IsActive()) {
    exception_state.ThrowDOMException(
        DOMExceptionCode::kTransactionInactiveError,
        transaction_->InactiveErrorMessage());
    return nullptr;
  }
  mojom::blink::IDBCursorDirection direction =
      IDBCursor::V8EnumToDirection(v8_direction.AsEnum());
  IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
      ExecutionContext::From(script_state), range, exception_state);
  if (exception_state.HadException())
    return nullptr;

  if (!db().IsConnectionOpen()) {
    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                      IDBDatabase::kDatabaseClosedErrorMessage);
    return nullptr;
  }

  return openCursor(script_state, key_range, direction, std::move(metrics));
}

IDBRequest* IDBIndex::openCursor(ScriptState* script_state,
                                 IDBKeyRange* key_range,
                                 mojom::blink::IDBCursorDirection direction,
                                 IDBRequest::AsyncTraceState metrics) {
  IDBRequest* request = IDBRequest::Create(
      script_state, this, transaction_.Get(), std::move(metrics));
  request->SetCursorDetails(indexed_db::kCursorKeyAndValue, direction);
  db().OpenCursor(object_store_->Id(), Id(), key_range, direction, false,
                  mojom::blink::IDBTaskType::Normal, request);
  return request;
}

IDBRequest* IDBIndex::count(ScriptState* script_state,
                            const ScriptValue& range,
                            ExceptionState& exception_state) {
  TRACE_EVENT1("IndexedDB", "IDBIndex::countRequestSetup", "index_name",
               metadata_->name.Utf8());
  IDBRequest::AsyncTraceState metrics(IDBRequest::TypeForMetrics::kIndexCount);
  if (IsDeleted()) {
    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                      IDBDatabase::kIndexDeletedErrorMessage);
    return nullptr;
  }
  if (!transaction_->IsActive()) {
    exception_state.ThrowDOMException(
        DOMExceptionCode::kTransactionInactiveError,
        transaction_->InactiveErrorMessage());
    return nullptr;
  }

  IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
      ExecutionContext::From(script_state), range, exception_state);
  if (exception_state.HadException())
    return nullptr;

  if (!db().IsConnectionOpen()) {
    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                      IDBDatabase::kDatabaseClosedErrorMessage);
    return nullptr;
  }

  IDBRequest* request = IDBRequest::Create(
      script_state, this, transaction_.Get(), std::move(metrics));
  db().Count(transaction_->Id(), object_store_->Id(), Id(), key_range,
             BindOnce(&IDBRequest::OnCount, WrapWeakPersistent(request)));
  return request;
}

IDBRequest* IDBIndex::openKeyCursor(ScriptState* script_state,
                                    const ScriptValue& range,
                                    const V8IDBCursorDirection& v8_direction,
                                    ExceptionState& exception_state) {
  TRACE_EVENT1("IndexedDB", "IDBIndex::openKeyCursorRequestSetup", "index_name",
               metadata_->name.Utf8());
  IDBRequest::AsyncTraceState metrics(
      IDBRequest::TypeForMetrics::kIndexOpenKeyCursor);
  if (IsDeleted()) {
    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                      IDBDatabase::kIndexDeletedErrorMessage);
    return nullptr;
  }
  if (!transaction_->IsActive()) {
    exception_state.ThrowDOMException(
        DOMExceptionCode::kTransactionInactiveError,
        transaction_->InactiveErrorMessage());
    return nullptr;
  }
  mojom::blink::IDBCursorDirection direction =
      IDBCursor::V8EnumToDirection(v8_direction.AsEnum());
  IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
      ExecutionContext::From(script_state), range, exception_state);
  if (exception_state.HadException())
    return nullptr;
  if (!db().IsConnectionOpen()) {
    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                      IDBDatabase::kDatabaseClosedErrorMessage);
    return nullptr;
  }

  IDBRequest* request = IDBRequest::Create(
      script_state, this, transaction_.Get(), std::move(metrics));
  request->SetCursorDetails(indexed_db::kCursorKeyOnly, direction);
  db().OpenCursor(object_store_->Id(), Id(), key_range, direction, true,
                  mojom::blink::IDBTaskType::Normal, request);
  return request;
}

IDBRequest* IDBIndex::get(ScriptState* script_state,
                          const ScriptValue& key,
                          ExceptionState& exception_state) {
  TRACE_EVENT1("IndexedDB", "IDBIndex::getRequestSetup", "index_name",
               metadata_->name.Utf8());
  IDBRequest::AsyncTraceState metrics(IDBRequest::TypeForMetrics::kIndexGet);
  return GetInternal(script_state, key, exception_state, false,
                     std::move(metrics));
}

IDBRequest* IDBIndex::getAll(ScriptState* script_state,
                             const ScriptValue& range_or_options,
                             ExceptionState& exception_state) {
  return getAll(script_state, range_or_options,
                std::numeric_limits<uint32_t>::max(), exception_state);
}

IDBRequest* IDBIndex::getAll(ScriptState* script_state,
                             const ScriptValue& range_or_options,
                             uint32_t max_count,
                             ExceptionState& exception_state) {
  TRACE_EVENT1("IndexedDB", "IDBIndex::getAllRequestSetup", "index_name",
               metadata_->name.Utf8());

  IDBGetAllOptions* options =
      IDBGetAllOptionsHelper::CreateFromArgumentsOrDictionary(
          script_state, range_or_options, max_count, exception_state);
  if (exception_state.HadException()) {
    return nullptr;
  }
  return CreateGetAllRequest(
      IDBRequest::TypeForMetrics::kIndexGetAll, script_state, *options,
      mojom::blink::IDBGetAllResultType::Values, exception_state);
}

IDBRequest* IDBIndex::getAllKeys(ScriptState* script_state,
                                 const ScriptValue& range_or_options,
                                 ExceptionState& exception_state) {
  return getAllKeys(script_state, range_or_options,
                    std::numeric_limits<uint32_t>::max(), exception_state);
}

IDBRequest* IDBIndex::getAllKeys(ScriptState* script_state,
                                 const ScriptValue& range_or_options,
                                 uint32_t max_count,
                                 ExceptionState& exception_state) {
  TRACE_EVENT1("IndexedDB", "IDBIndex::getAllKeysRequestSetup", "index_name",
               metadata_->name.Utf8());

  IDBGetAllOptions* options =
      IDBGetAllOptionsHelper::CreateFromArgumentsOrDictionary(
          script_state, range_or_options, max_count, exception_state);
  if (exception_state.HadException()) {
    return nullptr;
  }
  return CreateGetAllRequest(
      IDBRequest::TypeForMetrics::kIndexGetAllKeys, script_state, *options,
      mojom::blink::IDBGetAllResultType::Keys, exception_state);
}

IDBRequest* IDBIndex::getAllRecords(ScriptState* script_state,
                                    const IDBGetAllOptions* options,
                                    ExceptionState& exception_state) {
  TRACE_EVENT1("IndexedDB", "IDBIndex::getAllRecords", "index_name",
               metadata_->name.Utf8());

  // Explicitly count `kIndexedDBGetAllRecords` usage because the IDL definition
  // for `getAllRecords()` already has a [Measure] recording `kIndexedDBRead`.
  UseCounter::Count(ExecutionContext::From(script_state),
                    WebFeature::kIndexedDBGetAllRecords);

  return CreateGetAllRequest(
      IDBRequest::TypeForMetrics::kIndexGetAllRecords, script_state, *options,
      mojom::blink::IDBGetAllResultType::Records, exception_state);
}

IDBRequest* IDBIndex::getKey(ScriptState* script_state,
                             const ScriptValue& key,
                             ExceptionState& exception_state) {
  TRACE_EVENT1("IndexedDB", "IDBIndex::getKeyRequestSetup", "index_name",
               metadata_->name.Utf8());
  IDBRequest::AsyncTraceState metrics(IDBRequest::TypeForMetrics::kIndexGetKey);
  return GetInternal(script_state, key, exception_state, true,
                     std::move(metrics));
}

IDBRequest* IDBIndex::GetInternal(ScriptState* script_state,
                                  const ScriptValue& key,
                                  ExceptionState& exception_state,
                                  bool key_only,
                                  IDBRequest::AsyncTraceState metrics) {
  if (IsDeleted()) {
    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                      IDBDatabase::kIndexDeletedErrorMessage);
    return nullptr;
  }
  if (!transaction_->IsActive()) {
    exception_state.ThrowDOMException(
        DOMExceptionCode::kTransactionInactiveError,
        transaction_->InactiveErrorMessage());
    return nullptr;
  }

  IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
      ExecutionContext::From(script_state), key, exception_state);
  if (exception_state.HadException())
    return nullptr;
  if (!key_range) {
    exception_state.ThrowDOMException(
        DOMExceptionCode::kDataError,
        IDBDatabase::kNoKeyOrKeyRangeErrorMessage);
    return nullptr;
  }
  if (!db().IsConnectionOpen()) {
    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                      IDBDatabase::kDatabaseClosedErrorMessage);
    return nullptr;
  }
  IDBRequest* request = IDBRequest::Create(
      script_state, this, transaction_.Get(), std::move(metrics));
  db().Get(transaction_->Id(), object_store_->Id(), Id(), key_range, key_only,
           BindOnce(&IDBRequest::OnGet, WrapPersistent(request)));
  return request;
}

IDBRequest* IDBIndex::CreateGetAllRequest(
    IDBRequest::TypeForMetrics type_for_metrics,
    ScriptState* script_state,
    const IDBGetAllOptions& options,
    mojom::blink::IDBGetAllResultType result_type,
    ExceptionState& exception_state) {
  IDBRequest::AsyncTraceState metrics(type_for_metrics);

  if (IsDeleted()) {
    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                      IDBDatabase::kIndexDeletedErrorMessage);
    return nullptr;
  }
  if (!transaction_->IsActive()) {
    exception_state.ThrowDOMException(
        DOMExceptionCode::kTransactionInactiveError,
        transaction_->InactiveErrorMessage());
    return nullptr;
  }

  IDBKeyRange* key_range = IDBKeyRange::FromScriptValue(
      ExecutionContext::From(script_state), options.query(), exception_state);
  if (exception_state.HadException())
    return nullptr;
  if (!db().IsConnectionOpen()) {
    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                      IDBDatabase::kDatabaseClosedErrorMessage);
    return nullptr;
  }

  const uint32_t count = IDBGetAllOptionsHelper::GetCount(options);
  const mojom::blink::IDBCursorDirection direction =
      IDBCursor::V8EnumToDirection(options.direction().AsEnum());

  IDBRequest* request = IDBRequest::Create(
      script_state, this, transaction_.Get(), std::move(metrics));
  db().GetAll(transaction_->Id(), object_store_->Id(), Id(), key_range,
              result_type, count, direction, request);
  return request;
}

IDBDatabase& IDBIndex::db() {
  return transaction_->db();
}

}  // namespace blink