LocalStorage在Chrome里的实现

前段时间我们在实现CanTK-Runtime时,也曾在V8基础上模拟过浏览器的LocaleStorage功能,其实现非常简单:每个domain的数据使用的单独文件存储,因为同一时间只有一个游戏运行,所以文件操作只是放到了后台线程执行。但是Chrome里的实现就非常复杂了,它主要包括四部分:

  • 0.根据IDL文件产生出来的代码,称为Binding代码(gen/blink/bindings/modules/v8/V8Storage.cpp)。这些代码是JS与C++之间的桥梁,JS调用C++时传递过来的参数是一个数组,它负责把参数拆解出来然后调用实际的函数,执行完成后把参数返回给JS。
static void setItemMethod(const v8::FunctionCallbackInfo<v8::Value>& info)
{
    ExceptionState exceptionState(ExceptionState::ExecutionContext, "setItem", "Storage", info.Holder(), info.GetIsolate());
    if (UNLIKELY(info.Length() < 2)) {
        setMinimumArityTypeError(exceptionState, 2, info.Length());
        exceptionState.throwIfNeeded();
        return;
    }
    Storage* impl = V8Storage::toImpl(info.Holder());
    V8StringResource<> key;
    V8StringResource<> data;
    {
        key = info[0];
        if (!key.prepare())
            return;
        data = info[1];
        if (!data.prepare())
            return;
    }
    impl->setItem(key, data, exceptionState);
    if (exceptionState.hadException()) {
        exceptionState.throwIfNeeded();
        return;
    }
}
  • 1.与浏览器JS对接的模块(WebKit/Source/modules/storage)。Binding代码会调用到这里来,这里主要做些参数以及安全方面的检查,然后调用WebStorageArea的接口。
void StorageArea::setItem(const String& key, const String& value, ExceptionState& exceptionState, LocalFrame* frame)
{
    if (!canAccessStorage(frame)) {
        exceptionState.throwSecurityError("access is denied for this document.");
        return;
    }
    WebStorageArea::Result result = WebStorageArea::ResultOK;
    m_storageArea->setItem(key, value, frame->document()->url(), result);
    if (result != WebStorageArea::ResultOK)
        exceptionState.throwDOMException(QuotaExceededError, "Setting the value of '" + key + "' exceeded the quota.");
}
  • 2.客户端代理(content/renderer/dom_storage/webstoragearea_impl.cc)。上面需要的WebStorageArea接口的实现是WebStorageAreaImpl在chrome里实现的(在WebKit之外)。让我有些惊讶的是,这些代码居然是放在renderer目录下的。后来想了一下,Storage与renderer没关系,但是这些代码是在render进程程执行的。每个标签都有一个Render进程,多个标签可能是同一个domain,也就是会存取同一个Storage,出于性能和共享考虑,所以把真正执行文件系统(数据库)的操作放在服务进程里了,这个模块是客户端的代理。
void WebStorageAreaImpl::setItem(
    const WebString& key, const WebString& value, const WebURL& page_url,
    WebStorageArea::Result& result) {
  if (!cached_area_->SetItem(connection_id_, key, value, page_url))
    result = ResultBlockedByQuota;
  else
    result = ResultOK;
}

bool DOMStorageCachedArea::SetItem(int connection_id,
                                   const base::string16& key,
                                   const base::string16& value,
                                   const GURL& page_url) {
  // A quick check to reject obviously overbudget items to avoid
  // the priming the cache.
  if (key.length() + value.length() > kPerStorageAreaQuota)
    return false;

  PrimeIfNeeded(connection_id);
  base::NullableString16 unused;
  if (!map_->SetItem(key, value, &unused))
    return false;

  // Ignore mutations to 'key' until OnSetItemComplete.
  ignore_key_mutations_[key]++;
  proxy_->SetItem(
      connection_id, key, value, page_url,
      base::Bind(&DOMStorageCachedArea::OnSetItemComplete,
                 weak_factory_.GetWeakPtr(), key));
  return true;
}
  • 3.服务器及数据库(content/browser/dom_storage/dom_storage_area.cc)。这个是在浏览器的主进程里执行的,数据库使用的Sqlite。
bool DOMStorageArea::SetItem(const base::string16& key,
                             const base::string16& value,
                             base::NullableString16* old_value) {
  if (is_shutdown_)
    return false;
  InitialImportIfNeeded();
  if (!map_->HasOneRef())
    map_ = map_->DeepCopy();
  bool success = map_->SetItem(key, value, old_value);
  if (success && backing_ &&
      (old_value->is_null() || old_value->string() != value)) {
    CommitBatch* commit_batch = CreateCommitBatchIfNeeded();
    commit_batch->changed_values[key] = base::NullableString16(value, false);
  }
  return success;
}
原文地址:https://www.cnblogs.com/hzcya1995/p/13333011.html