Chromium 中Window.localStorage对象c++实现分析

一、前端定义Window.localStorage

只读的localStorage 属性允许你访问一个Document 源(origin)的对象 Storage;存储的数据将保存在浏览器会话中。localStorage 类似 sessionStorage,但其区别在于:存储在 localStorage 的数据可以长期保留;而当页面会话结束——也就是说,当页面被关闭时,存储在 sessionStorage 的数据会被清除。

应注意,无论数据存储在 localStorage 还是 sessionStorage ,它们都特定于页面的协议。

另外,localStorage 中的键值对总是以字符串的形式存储。 (需要注意,和 js 对象相比,键值对总是以字符串的形式存储意味着数值类型会自动转化为字符串类型).

语法

jsCopy to Clipboard

myStorage = localStorage;

一个可被用于访问当前源(origin)的本地存储空间的 Storage 对象。

异常

SecurityError

请求违反了一个策略声明,或者源(origin)不是 一个有效的 scheme/host/port tuple (例如如果 origin 使用 file: 或者 data: 形式将可能发生)。比如,用户可以有禁用允许对指定的 origin 存留数据的浏览器配置。

示例

下面的代码片段访问了当前域名下的本地 Storage 对象,并通过 Storage.setItem() 增加了一个数据项目。

jsCopy to Clipboard

localStorage.setItem("myCat", "Tom");

该语法用于读取 localStorage 项,如下:

jsCopy to Clipboard

let cat = localStorage.getItem("myCat");

该语法用于移除 localStorage 项,如下:

jsCopy to Clipboard

localStorage.removeItem("myCat");

该语法用于移除所有的 localStorage 项,如下:

jsCopy to Clipboard

// 移除所有
localStorage.clear();

Window.localStorage - Web API | MDN (mozilla.org)

二、看下c++代码实现

 1、localStorage在window对象属性定义

third_party\blink\renderer\modules\storage\window_storage.idl

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

[
    ImplementedAs=DOMWindowStorage
] partial interface Window {
    // https://html.spec.whatwg.org/C/#the-sessionstorage-attribute
    [LogActivity=GetterOnly, RaisesException=Getter] readonly attribute Storage sessionStorage;
    // https://html.spec.whatwg.org/C/#the-localstorage-attribute
    [LogActivity=GetterOnly, RaisesException=Getter] readonly attribute Storage localStorage;
};

对应实现

third_party\blink\renderer\modules\storage\dom_window_storage.h

third_party\blink\renderer\modules\storage\dom_window_storage.cc

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_DOM_WINDOW_STORAGE_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_DOM_WINDOW_STORAGE_H_

#include "mojo/public/cpp/bindings/pending_remote.h"
#include "third_party/blink/public/mojom/dom_storage/storage_area.mojom-blink-forward.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/supplementable.h"

namespace blink {

class ExceptionState;
class LocalDOMWindow;
class StorageArea;

class DOMWindowStorage final : public GarbageCollected<DOMWindowStorage>,
                               public Supplement<LocalDOMWindow> {
 public:
  static const char kSupplementName[];

  static DOMWindowStorage& From(LocalDOMWindow&);
  static StorageArea* sessionStorage(LocalDOMWindow&, ExceptionState&);
  static StorageArea* localStorage(LocalDOMWindow&, ExceptionState&);

  explicit DOMWindowStorage(LocalDOMWindow&);

  StorageArea* sessionStorage(ExceptionState&) const;
  StorageArea* localStorage(ExceptionState&) const;
  StorageArea* OptionalSessionStorage() const { return session_storage_.Get(); }
  StorageArea* OptionalLocalStorage() const { return local_storage_.Get(); }

  // These Init* methods allow initializing the StorageArea as an optimization
  // to avoid it being requested from the browser process, which can be slow.
  // These storage areas are ignored if a cached storage area already exists for
  // this storage key/namespace.
  void InitLocalStorage(
      mojo::PendingRemote<mojom::blink::StorageArea> local_storage_area) const;
  void InitSessionStorage(mojo::PendingRemote<mojom::blink::StorageArea>
                              session_storage_area) const;

  void Trace(Visitor*) const override;

 private:
  StorageArea* GetOrCreateSessionStorage(
      ExceptionState& exception_state,
      mojo::PendingRemote<mojom::blink::StorageArea> storage_area_for_init)
      const;
  StorageArea* GetOrCreateLocalStorage(
      ExceptionState& exception_state,
      mojo::PendingRemote<mojom::blink::StorageArea> storage_area_for_init)
      const;

  mutable Member<StorageArea> session_storage_;
  mutable Member<StorageArea> local_storage_;
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_DOM_WINDOW_STORAGE_H_

2、其中localStorage 是Storage对象,那么看下Storage对象定义

third_party\blink\renderer\modules\storage\storage.idl

/*
 * Copyright (C) 2008 Apple 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 INC. ``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 INC. OR
 * 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.
 */

// https://html.spec.whatwg.org/C/#the-storage-interface

[
    Exposed=Window,
    ImplementedAs=StorageArea
] interface Storage {
    [RaisesException=Getter] readonly attribute unsigned long length;
    [RaisesException, MeasureAs=DOMStorageRead] DOMString? key(unsigned long index);
    [LogActivity, RaisesException, MeasureAs=DOMStorageRead] getter DOMString? getItem(DOMString key);
    [LogActivity, RaisesException, MeasureAs=DOMStorageWrite] setter void setItem(DOMString key, DOMString value);
    [LogActivity, RaisesException, MeasureAs=DOMStorageWrite] deleter void removeItem(DOMString key);
    [LogActivity, RaisesException, MeasureAs=DOMStorageWrite] void clear();
};

提供了clear setitem等方法。

对应实现:

third_party\blink\renderer\modules\storage\storage_area.h

third_party\blink\renderer\modules\storage\storage_area.cc

third_party\blink\renderer\modules\storage\storage_area_map.h

third_party\blink\renderer\modules\storage\storage_area_map.cc

third_party\blink\renderer\modules\storage\cached_storage_area.h

third_party\blink\renderer\modules\storage\cached_storage_area.cc

/*
 * Copyright (C) 2008 Apple 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 INC. ``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 INC. OR
 * 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_STORAGE_STORAGE_AREA_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_STORAGE_AREA_H_

#include "base/memory/scoped_refptr.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
#include "third_party/blink/renderer/modules/storage/cached_storage_area.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/wtf/forward.h"

namespace blink {

class ExceptionState;
class LocalDOMWindow;

class StorageArea final : public ScriptWrappable,
                          public ExecutionContextClient,
                          public CachedStorageArea::Source {
  DEFINE_WRAPPERTYPEINFO();

 public:
  enum class StorageType { kLocalStorage, kSessionStorage };
  static const char kAccessDataMessage[];
  static const char kAccessDeniedMessage[];
  static const char kAccessSandboxedMessage[];

  static StorageArea* Create(LocalDOMWindow*,
                             scoped_refptr<CachedStorageArea>,
                             StorageType);

  // This storage area doesn't enqueue any events. This avoids duplicate event
  // dispatch when an inspector agent is present.
  static StorageArea* CreateForInspectorAgent(LocalDOMWindow*,
                                              scoped_refptr<CachedStorageArea>,
                                              StorageType);

  StorageArea(LocalDOMWindow*,
              scoped_refptr<CachedStorageArea>,
              StorageType,
              bool should_enqueue_events);

  unsigned length(ExceptionState&) const;
  String key(unsigned index, ExceptionState&) const;
  String getItem(const String& key, ExceptionState&) const;
  NamedPropertySetterResult setItem(const String& key,
                                    const String& value,
                                    ExceptionState&);
  NamedPropertyDeleterResult removeItem(const String& key, ExceptionState&);
  void clear(ExceptionState&);
  bool Contains(const String& key, ExceptionState& ec) const;

  void NamedPropertyEnumerator(Vector<String>&, ExceptionState&);
  bool NamedPropertyQuery(const AtomicString&, ExceptionState&);

  bool CanAccessStorage() const;

  void Trace(Visitor*) const override;

  // CachedStorageArea::Source:
  KURL GetPageUrl() const override;
  bool EnqueueStorageEvent(const String& key,
                           const String& old_value,
                           const String& new_value,
                           const String& url) override;

  blink::WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
      const char* name,
      WebScopedVirtualTimePauser::VirtualTaskDuration duration) override;

  LocalDOMWindow* GetDOMWindow() override;

 private:
  void RecordModificationInMetrics();

  void OnDocumentActivatedForPrerendering();

  scoped_refptr<CachedStorageArea> cached_area_;
  StorageType storage_type_;
  const bool should_enqueue_events_;

  mutable bool did_check_can_access_storage_ = false;
  mutable bool can_access_storage_cached_result_ = false;
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_STORAGE_STORAGE_AREA_H_

3、在v8_storage.cc中的实现

out\Debug\gen\third_party\blink\renderer\bindings\modules\v8\v8_storage.cc

方法注册以及实现

{
  static const IDLMemberInstaller::OperationConfig kOperationTable[] = {
{"clear", ClearOperationCallback, 0, unsigned(v8::None), unsigned(IDLMemberInstaller::FlagLocation::kPrototype), unsigned(IDLMemberInstaller::FlagWorld::kAllWorlds), unsigned(IDLMemberInstaller::FlagReceiverCheck::kCheck), unsigned(IDLMemberInstaller::FlagCrossOriginCheck::kCheck), unsigned(v8::SideEffectType::kHasSideEffect)}, 
{"getItem", GetItemOperationCallback, 1, unsigned(v8::None), unsigned(IDLMemberInstaller::FlagLocation::kPrototype), unsigned(IDLMemberInstaller::FlagWorld::kAllWorlds), unsigned(IDLMemberInstaller::FlagReceiverCheck::kCheck), unsigned(IDLMemberInstaller::FlagCrossOriginCheck::kCheck), unsigned(v8::SideEffectType::kHasSideEffect)}, 
{"key", KeyOperationCallback, 1, unsigned(v8::None), unsigned(IDLMemberInstaller::FlagLocation::kPrototype), unsigned(IDLMemberInstaller::FlagWorld::kAllWorlds), unsigned(IDLMemberInstaller::FlagReceiverCheck::kCheck), unsigned(IDLMemberInstaller::FlagCrossOriginCheck::kCheck), unsigned(v8::SideEffectType::kHasSideEffect)}, 
{"removeItem", RemoveItemOperationCallback, 1, unsigned(v8::None), unsigned(IDLMemberInstaller::FlagLocation::kPrototype), unsigned(IDLMemberInstaller::FlagWorld::kAllWorlds), unsigned(IDLMemberInstaller::FlagReceiverCheck::kCheck), unsigned(IDLMemberInstaller::FlagCrossOriginCheck::kCheck), unsigned(v8::SideEffectType::kHasSideEffect)}, 
{"setItem", SetItemOperationCallback, 2, unsigned(v8::None), unsigned(IDLMemberInstaller::FlagLocation::kPrototype), unsigned(IDLMemberInstaller::FlagWorld::kAllWorlds), unsigned(IDLMemberInstaller::FlagReceiverCheck::kCheck), unsigned(IDLMemberInstaller::FlagCrossOriginCheck::kCheck), unsigned(v8::SideEffectType::kHasSideEffect)}, 
};
IDLMemberInstaller::InstallOperations(isolate, world, instance_template, prototype_template, interface_template, signature, kOperationTable);
}

 1)、Storage.setItem

void SetItemOperationCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_StorageArea_setItem");
BLINK_BINDINGS_TRACE_EVENT("Storage.setItem");


v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> current_context = isolate->GetCurrentContext();
ExecutionContext* current_execution_context = ExecutionContext::From(current_context);
// [Measure], [MeasureAs]
UseCounter::Count(current_execution_context, WebFeature::kDOMStorageWrite);
v8::Local<v8::Object> v8_receiver = info.This();
v8::Local<v8::Context> receiver_context = v8_receiver->GetCreationContextChecked();
ScriptState* receiver_script_state = ScriptState::From(receiver_context);
ScriptState* script_state = receiver_script_state;
V8PerContextData* per_context_data = script_state->PerContextData();
// [LogActivity], [LogAllWorlds]
if (UNLIKELY(per_context_data && per_context_data->ActivityLogger())) { per_context_data->ActivityLogger()->LogMethod(script_state, "Storage.setItem", info); }



const ExceptionContextType exception_context_type = ExceptionContextType::kOperationInvoke;
const char* const class_like_name = "Storage";
const char* const property_name = "setItem";
ExceptionState exception_state(isolate, exception_context_type, class_like_name, property_name);
if (UNLIKELY(info.Length() < 2)) {
  exception_state.ThrowTypeError(ExceptionMessages::NotEnoughArguments(2, info.Length()));
return;
}

2)、Storage.clear

void ClearOperationCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_StorageArea_clear");
BLINK_BINDINGS_TRACE_EVENT("Storage.clear");


v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> current_context = isolate->GetCurrentContext();
ExecutionContext* current_execution_context = ExecutionContext::From(current_context);
// [Measure], [MeasureAs]
UseCounter::Count(current_execution_context, WebFeature::kDOMStorageWrite);
v8::Local<v8::Object> v8_receiver = info.This();
v8::Local<v8::Context> receiver_context = v8_receiver->GetCreationContextChecked();
ScriptState* receiver_script_state = ScriptState::From(receiver_context);
ScriptState* script_state = receiver_script_state;
V8PerContextData* per_context_data = script_state->PerContextData();
// [LogActivity], [LogAllWorlds]
if (UNLIKELY(per_context_data && per_context_data->ActivityLogger())) { per_context_data->ActivityLogger()->LogMethod(script_state, "Storage.clear", info); }






StorageArea* blink_receiver = V8Storage::ToWrappableUnsafe(isolate, v8_receiver);
const ExceptionContextType exception_context_type = ExceptionContextType::kOperationInvoke;
const char* const class_like_name = "Storage";
const char* const property_name = "clear";
ExceptionState exception_state(isolate, exception_context_type, class_like_name, property_name);
blink_receiver->clear(exception_state);
if (UNLIKELY(exception_state.HadException())) {
  return;
}

}

3)、Storage.getItem

void GetItemOperationCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_StorageArea_getItem");
BLINK_BINDINGS_TRACE_EVENT("Storage.getItem");


v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> current_context = isolate->GetCurrentContext();
ExecutionContext* current_execution_context = ExecutionContext::From(current_context);
// [Measure], [MeasureAs]
UseCounter::Count(current_execution_context, WebFeature::kDOMStorageRead);
v8::Local<v8::Object> v8_receiver = info.This();
v8::Local<v8::Context> receiver_context = v8_receiver->GetCreationContextChecked();
ScriptState* receiver_script_state = ScriptState::From(receiver_context);
ScriptState* script_state = receiver_script_state;
V8PerContextData* per_context_data = script_state->PerContextData();
// [LogActivity], [LogAllWorlds]
if (UNLIKELY(per_context_data && per_context_data->ActivityLogger())) { per_context_data->ActivityLogger()->LogMethod(script_state, "Storage.getItem", info); }



const ExceptionContextType exception_context_type = ExceptionContextType::kOperationInvoke;
const char* const class_like_name = "Storage";
const char* const property_name = "getItem";
ExceptionState exception_state(isolate, exception_context_type, class_like_name, property_name);
if (UNLIKELY(info.Length() < 1)) {
  exception_state.ThrowTypeError(ExceptionMessages::NotEnoughArguments(1, info.Length()));
return;
}



StorageArea* blink_receiver = V8Storage::ToWrappableUnsafe(isolate, v8_receiver);
auto&& arg1_key = NativeValueTraits<IDLString>::ArgumentValue(isolate, 0, info[0], exception_state);
if (UNLIKELY(exception_state.HadException())) {
  return;
}
auto&& return_value = blink_receiver->getItem(arg1_key, exception_state);
if (UNLIKELY(exception_state.HadException())) {
  return;
}
bindings::V8SetReturnValue(info, return_value, isolate, bindings::V8ReturnValue::kNullable);
}

4)、Storage.removeItem

void RemoveItemOperationCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_StorageArea_removeItem");
BLINK_BINDINGS_TRACE_EVENT("Storage.removeItem");


v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> current_context = isolate->GetCurrentContext();
ExecutionContext* current_execution_context = ExecutionContext::From(current_context);
// [Measure], [MeasureAs]
UseCounter::Count(current_execution_context, WebFeature::kDOMStorageWrite);
v8::Local<v8::Object> v8_receiver = info.This();
v8::Local<v8::Context> receiver_context = v8_receiver->GetCreationContextChecked();
ScriptState* receiver_script_state = ScriptState::From(receiver_context);
ScriptState* script_state = receiver_script_state;
V8PerContextData* per_context_data = script_state->PerContextData();
// [LogActivity], [LogAllWorlds]
if (UNLIKELY(per_context_data && per_context_data->ActivityLogger())) { per_context_data->ActivityLogger()->LogMethod(script_state, "Storage.removeItem", info); }



const ExceptionContextType exception_context_type = ExceptionContextType::kOperationInvoke;
const char* const class_like_name = "Storage";
const char* const property_name = "removeItem";
ExceptionState exception_state(isolate, exception_context_type, class_like_name, property_name);
if (UNLIKELY(info.Length() < 1)) {
  exception_state.ThrowTypeError(ExceptionMessages::NotEnoughArguments(1, info.Length()));
return;
}



StorageArea* blink_receiver = V8Storage::ToWrappableUnsafe(isolate, v8_receiver);
auto&& arg1_key = NativeValueTraits<IDLString>::ArgumentValue(isolate, 0, info[0], exception_state);
if (UNLIKELY(exception_state.HadException())) {
  return;
}
blink_receiver->removeItem(arg1_key, exception_state);
if (UNLIKELY(exception_state.HadException())) {
  return;
}

}

5)、Storage.key

void KeyOperationCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_StorageArea_key");
BLINK_BINDINGS_TRACE_EVENT("Storage.key");


v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> current_context = isolate->GetCurrentContext();
ExecutionContext* current_execution_context = ExecutionContext::From(current_context);
// [Measure], [MeasureAs]
UseCounter::Count(current_execution_context, WebFeature::kDOMStorageRead);



const ExceptionContextType exception_context_type = ExceptionContextType::kOperationInvoke;
const char* const class_like_name = "Storage";
const char* const property_name = "key";
ExceptionState exception_state(isolate, exception_context_type, class_like_name, property_name);
if (UNLIKELY(info.Length() < 1)) {
  exception_state.ThrowTypeError(ExceptionMessages::NotEnoughArguments(1, info.Length()));
return;
}



v8::Local<v8::Object> v8_receiver = info.This();
StorageArea* blink_receiver = V8Storage::ToWrappableUnsafe(isolate, v8_receiver);
auto&& arg1_index = NativeValueTraits<IDLUnsignedLong>::ArgumentValue(isolate, 0, info[0], exception_state);
if (UNLIKELY(exception_state.HadException())) {
  return;
}
auto&& return_value = blink_receiver->key(arg1_index, exception_state);
if (UNLIKELY(exception_state.HadException())) {
  return;
}
bindings::V8SetReturnValue(info, return_value, isolate, bindings::V8ReturnValue::kNullable);
}

4、存储在Local Storage一个leveldb文件中。 C:\Users\Administrator\AppData\Local\Chromium\User Data\Default\Local Storage

实现:

components\services\storage\dom_storage\local_storage_impl.h

components\services\storage\dom_storage\local_storage_impl.cc

components\services\storage\dom_storage\dom_storage_database.h

components\services\storage\dom_storage\dom_storage_database.cc

注意:操作 Local Storage数据库进程是在--type=utility --utility-sub-type=storage.mojom.StorageService子进程里面。

操作 Local Storage数据库进程chrome.exe ID=28624【storage.mojom.StorageService 】          "F:\code\google\src\out\Debug\chrome.exe" --type=utility --utility-sub-type=storage.mojom.StorageService --lang=zh-CN --service-sandbox-type=service --no-pre-read-main-dll --mojo-platform-channel-handle=2400 --field-trial-handle=2008,i,5623276556528525387,15730261715311392447,262144 --variations-seed-version /prefetch:8

// The path where Local Storage data is persisted on disk, relative to a storage

// partition's root directory.

const base::FilePath::CharType kLocalStoragePath[] =

    FILE_PATH_LITERAL("Local Storage");

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_SERVICES_STORAGE_DOM_STORAGE_LOCAL_STORAGE_IMPL_H_
#define COMPONENTS_SERVICES_STORAGE_DOM_STORAGE_LOCAL_STORAGE_IMPL_H_

#include <stdint.h>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>

#include "base/files/file_path.h"
#include "base/functional/callback_forward.h"
#include "base/memory/ref_counted.h"
#include "base/task/sequenced_task_runner.h"
#include "base/threading/sequence_bound.h"
#include "base/trace_event/memory_allocator_dump.h"
#include "base/trace_event/memory_dump_provider.h"
#include "components/services/storage/dom_storage/async_dom_storage_database.h"
#include "components/services/storage/dom_storage/dom_storage_database.h"
#include "components/services/storage/public/mojom/local_storage_control.mojom.h"
#include "components/services/storage/public/mojom/storage_policy_update.mojom.h"
#include "components/services/storage/public/mojom/storage_usage_info.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "third_party/blink/public/mojom/dom_storage/storage_area.mojom.h"

namespace blink {
class StorageKey;
}  // namespace blink

namespace storage {

// The Local Storage implementation. An instance of this class exists for each
// storage partition using Local Storage, managing storage for all StorageKeys
// within the partition.
class LocalStorageImpl : public base::trace_event::MemoryDumpProvider,
                         public mojom::LocalStorageControl {
 public:
  // Constructs a Local Storage implementation which will create its root
  // "Local Storage" directory in |storage_root| if non-empty. |task_runner|
  // run tasks on the same sequence as the one which constructs this object.
  // |legacy_task_runner| must support blocking operations and its tasks must
  // be able to block shutdown. If valid, |receiver| will be bound to this
  // object to allow for remote control via the LocalStorageControl interface.
  LocalStorageImpl(const base::FilePath& storage_root,
                   scoped_refptr<base::SequencedTaskRunner> task_runner,
                   mojo::PendingReceiver<mojom::LocalStorageControl> receiver);
  ~LocalStorageImpl() override;

  void FlushStorageKeyForTesting(const blink::StorageKey& storage_key);

  // Used by content settings to alter the behavior around
  // what data to keep and what data to discard at shutdown.
  // The policy is not so straight forward to describe, see
  // the implementation for details.
  void SetForceKeepSessionState() { force_keep_session_state_ = true; }

  // Called when the owning BrowserContext is ending.
  // Schedules the commit of any unsaved changes and will delete or keep data on
  // disk per the content settings and special storage policies.  `callback` is
  // invoked when shutdown is complete, which may happen even before ShutDown
  // returns.
  void ShutDown(base::OnceClosure callback);

  // Clears unused storage areas, when thresholds are reached.
  void PurgeUnusedAreasIfNeeded();

  // mojom::LocalStorageControl implementation:
  void BindStorageArea(
      const blink::StorageKey& storage_key,
      mojo::PendingReceiver<blink::mojom::StorageArea> receiver) override;
  void GetUsage(GetUsageCallback callback) override;
  void DeleteStorage(const blink::StorageKey& storage_key,
                     DeleteStorageCallback callback) override;
  void CleanUpStorage(CleanUpStorageCallback callback) override;
  void Flush(FlushCallback callback) override;
  void PurgeMemory() override;
  void ApplyPolicyUpdates(
      std::vector<mojom::StoragePolicyUpdatePtr> policy_updates) override;
  void ForceKeepSessionState() override;

  // base::trace_event::MemoryDumpProvider implementation.
  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
                    base::trace_event::ProcessMemoryDump* pmd) override;

  // Access the underlying DomStorageDatabase. May be null if the database is
  // not yet open.
  const base::SequenceBound<DomStorageDatabase>& GetDatabaseForTesting() const {
    return database_->database();
  }

  // Wait for the database to be opened, or for opening to fail. If the database
  // is already opened, |callback| is invoked immediately.
  void SetDatabaseOpenCallbackForTesting(base::OnceClosure callback);

 private:
  friend class DOMStorageBrowserTest;

  class StorageAreaHolder;

  // Runs |callback| immediately if already connected to a database, otherwise
  // delays running |callback| untill after a connection has been established.
  // Initiates connecting to the database if no connection is in progres yet.
  void RunWhenConnected(base::OnceClosure callback);

  // StorageAreas held by this LocalStorageImpl retain an unmanaged reference to
  // `database_`. This deletes them and is used any time `database_` is reset.
  void PurgeAllStorageAreas();

  // Part of our asynchronous directory opening called from RunWhenConnected().
  void InitiateConnection(bool in_memory_only = false);
  void OnDatabaseOpened(leveldb::Status status);
  void OnGotDatabaseVersion(leveldb::Status status,
                            const std::vector<uint8_t>& value);
  void OnConnectionFinished();
  void DeleteAndRecreateDatabase();
  void OnDBDestroyed(bool recreate_in_memory, leveldb::Status status);

  StorageAreaHolder* GetOrCreateStorageArea(
      const blink::StorageKey& storage_key);

  // The (possibly delayed) implementation of GetUsage(). Can be called directly
  // from that function, or through |on_database_open_callbacks_|.
  void RetrieveStorageUsage(GetUsageCallback callback);
  void OnGotMetaData(GetUsageCallback callback,
                     std::vector<DomStorageDatabase::KeyValuePair> data);

  void OnGotStorageUsageForShutdown(
      std::vector<mojom::StorageUsageInfoPtr> usage);
  void OnStorageKeysDeleted(leveldb::Status status);
  void OnShutdownComplete();

  void GetStatistics(size_t* total_cache_size, size_t* unused_area_count);
  void OnCommitResult(leveldb::Status status);

  const base::FilePath directory_;

  enum ConnectionState {
    NO_CONNECTION,
    CONNECTION_IN_PROGRESS,
    CONNECTION_FINISHED,
    CONNECTION_SHUTDOWN
  } connection_state_ = NO_CONNECTION;
  bool database_initialized_ = false;

  bool force_keep_session_state_ = false;

  const scoped_refptr<base::SequencedTaskRunner> leveldb_task_runner_;

  base::trace_event::MemoryAllocatorDumpGuid memory_dump_id_;

  std::unique_ptr<AsyncDomStorageDatabase> database_;
  bool tried_to_recreate_during_open_ = false;
  bool in_memory_ = false;

  std::vector<base::OnceClosure> on_database_opened_callbacks_;

  // Maps between a StorageKey and its prefixed LevelDB view.
  std::map<blink::StorageKey, std::unique_ptr<StorageAreaHolder>> areas_;

  bool is_low_end_device_;
  // Counts consecutive commit errors. If this number reaches a threshold, the
  // whole database is thrown away.
  int commit_error_count_ = 0;
  bool tried_to_recover_from_commit_errors_ = false;

  // The set of Origins which should be cleared on shutdown.
  // this is used by ApplyPolicyUpdates to store which origin
  // to clear based on the provided StoragePolicyUpdate.
  std::set<url::Origin> origins_to_purge_on_shutdown_;

  mojo::Receiver<mojom::LocalStorageControl> control_receiver_{this};

  base::OnceClosure shutdown_complete_callback_;

  base::WeakPtrFactory<LocalStorageImpl> weak_ptr_factory_{this};
};

}  // namespace storage

#endif  // COMPONENTS_SERVICES_STORAGE_DOM_STORAGE_LOCAL_STORAGE_IMPL_H_

LocalStorageImpl继承自public mojom::LocalStorageControl

5、每个标签通创建会绑定使用local_storage_control.mojom接口 与Local Storage数据库进程chrome.exe【storage.mojom.StorageService 】绑定,监听数据变化

定义如下:

components\services\storage\public\mojom\local_storage_control.mojom

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

module storage.mojom;

import "components/services/storage/public/mojom/storage_policy_update.mojom";
import "components/services/storage/public/mojom/storage_usage_info.mojom";
import "third_party/blink/public/mojom/dom_storage/storage_area.mojom";
import "third_party/blink/public/mojom/storage_key/storage_key.mojom";

// Controls the state of Local Storage within a partition. This is a privileged
// interface and must not be brokered to untrusted clients.
//
// Currently this is consumed and implemented in the browser process, but the
// implementation will eventually live in the storage service which may run
// out-of-process.
interface LocalStorageControl {
  // Binds a blink.mojom.StorageArea `receiver` for a specific `storage_key`.
  BindStorageArea(blink.mojom.StorageKey storage_key,
                  pending_receiver<blink.mojom.StorageArea> receiver);

  // Retrieves some basic usage information about the Local Storage state.
  GetUsage() => (array<StorageUsageInfo> info);

  // Deletes all Local State state for the given `storage_key`. Responds when
  // the deletion is complete.
  DeleteStorage(blink.mojom.StorageKey storage_key) => ();

  // Ensures that no traces of deleted Local Storage data are left in the
  // backing storage for this `storage_key`. Responds when the cleanup is
  // complete.
  CleanUpStorage() => ();

  // Tells the service to immediately commit any pending operations to disk.
  Flush() => ();

  // Purges any in-memory caches to free up as much memory as possible. The
  // next access to the StorageArea will reload data from the backing database.
  PurgeMemory();

  // Applies changes to data retention policy which are relevant at shutdown.
  // See StoragePolicyUpdate.
  ApplyPolicyUpdates(array<StoragePolicyUpdate> policy_updates);

  // Some StorageKeys may be configured to be purged at the end of a session
  // rather than persisted as in the default Local Storage behavior. If this is
  // called, that behavior is overridden and Local Storage data is persisted
  // for all StorageKeys.
  ForceKeepSessionState();
};

6、每个render进程【打开网页的进程】与Local Storage数据库进程chrome.exe【storage.mojom.StorageService 】通信,通知该进程更新删除数据库。

接口定义:third_party\blink\public\mojom\dom_storage\storage_area.mojom

render进程通过接口storage_area.mojom 里面put等与Local Storage数据库进程chrome.exe【storage.mojom.StorageService 】通信。

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

module blink.mojom;

// Gives information about changes to a StorageArea.
//
// NOTE: When used for Local Storage, all of the events defined here may be
// received by an observer. Session Storage observers will ONLY ever observe
// `AllDeleted()` messages, and only in response to browser-initiated storage
// removal. This is because every Session Storage area is effectively owned by
// a single client and there is no need to incur IPC overhead by repeating the
// client's changes back to itself.
interface StorageAreaObserver {
  // Notifies the observer that a key's value was changed. If `old_value` is
  // null, the key was newly added and had no previous value stored.
  KeyChanged(array<uint8> key, array<uint8> new_value, array<uint8>? old_value,
             string source);

  // Notifies the observer that a requested key change failed. This is generally
  // only of interest to the client corresponding to `source` which attempted to
  // change the key (e.g. to roll back a cache update).
  KeyChangeFailed(array<uint8> key, string source);

  // Notifies the observer that a key was deleted. `old_value` may be null if
  // the deleted key didn't have an entry prior to this call.
  KeyDeleted(array<uint8> key, array<uint8>? old_value, string source);

  // Notifies the observer that all keys were deleted. `was_nonempty` indicates
  // whether the StorageArea had at least one key-value stored at when the
  // corresponding `DeleteAll()` call was received.
  AllDeleted(bool was_nonempty, string source);

  // Tells the client if it should send the old values for the key on `Put()`
  // and `Delete()` calls for sending notifications. Clients should assume they
  // need to send these values unless this method is called with `false` at some
  // point.
  ShouldSendOldValueOnMutations(bool value);
};

struct KeyValue {
  array<uint8> key;
  array<uint8> value;
};

// The mojo interface representing the connection to a single Local Storage or
// Session Storage storage area. Every area represents an isolated key-value
// store.
interface StorageArea {
  // The quota for each storage area.
  // This value is enforced in renderer processes and the browser process.
  const uint32 kPerStorageAreaQuota = 10485760; // 10 MiB

  // In the browser process we allow some overage to
  // accommodate concurrent writes from different renderers
  // that were allowed because the limit imposed in the renderer
  // wasn't exceeded.
  const uint32 kPerStorageAreaOverQuotaAllowance = 102400; // 100 KiB

  // Adds an observer to monitor changes to the StorageArea. Note that no
  // guarantees can be made about exactly when this observer will start
  // observing events. For example, a `Put()` immediately before or after this
  // `AddObserver()` call may or may not result in a corresponding
  // `KeyChanged()` event on the observer.
  //
  // In order to properly synchronize observed events against known storage
  // state, callers must use `GetAll()` or `DeleteAll()` and pass an observer
  // to those methods.
  AddObserver(pending_remote<StorageAreaObserver> observer);

  // Set the database entry for `key` to `value`.
  // Takes an optional `client_old_value` (see ShouldSendOldValueOnMutations()):
  // 1. If the client is notified to not send old value on mutations
  //    `client_old_value` is unused and can be nullopt.
  // 2. If the client is notified to send old values or not notified at all,
  //    `client_old_value` must be filled in with old value of the `key`, or
  //    nullopt if `key` was not present in database. This value is used to send
  //    notifications to StorageArea(s).
  // Returns `true` on success or `false` on failure.
  //
  // Note that a successful reply should not be treated as an indication that
  // the value stored at `key` is `value`: it is possible for the reply to be
  // received after some other client has already modified the key again.
  // Clients interested in maintaining a consistent local cache of the stored
  // contents should rely only on sequential StorageAreaObserver events from
  // an observer bound via `GetAll()` or `DeleteAll()`.
  Put(array<uint8> key, array<uint8> value, array<uint8>? client_old_value,
      string source)
      => (bool success);

  // Remove the database entry (if any) for `key`.
  // Takes an optional `client_old_value` (see ShouldSendOldValueOnMutations()):
  // 1. If the client is notified to not send old value on mutations,
  //    `client_old_value` is unused and can be nullopt.
  // 2. If the client is notified to send old values or not notified at all,
  //    `client_old_value` must be filled in with old value of the `key`, or
  //    nullopt if `key` was not present in database. This value is used to send
  //    notifications to StorageAreaObserver(s).
  //
  // TODO(https://crbug.com/1000959): Remove the `success` reply argument. This
  // call always succeeds.
  //
  // Note that a successful reply should not be treated as an indication that
  // the value for `key` is still empty: it is possible for the reply to be
  // received after some other client has already added the key again. Clients
  // interested in maintaining a consistent local cache of the stored contents
  // should rely only on sequential StorageAreaObserver events from an observer
  // bound via `GetAll()` or `DeleteAll()`.
  Delete(array<uint8> key, array<uint8>? client_old_value, string source)
      => (bool success);

  // Removes all entries. If `new_observer` is non-null, it will be added to
  // the StorageArea's set of observers immediately BEFORE broadcasting the
  // corresponding `AllDeleted()` event, such that `new_observer`'s receiver
  // will always receive that `AllDeleted()` as its first observed event.
  //
  // TODO(https://crbug.com/1000959): Remove the `success` reply argument. This
  // call always succeeds.
  DeleteAll(string source, pending_remote<StorageAreaObserver>? new_observer)
      => (bool success);

  // [DEPRECATED] Returns the value of the `key` only if values are
  // stored in the internal in-memory cache. Fails if the `key` does not exist
  // or if values are not required to be stored in the cache.
  // TODO(ssid): Remove this function, crbug.com/764127.
  Get(array<uint8> key) => (bool success, array<uint8> value);

  // Returns all key/value pairs and optionally adds a new StorageAreaObserver
  // which will observe all events on the StorageArea which occur after the
  // returned snapshot.
  [Sync]
  GetAll(pending_remote<StorageAreaObserver>? new_observer)
      => (array<KeyValue> data);
};

在render进程页面点击改值时候对应c++代码

third_party\blink\renderer\modules\storage\cached_storage_area.cc

CachedStorageArea::SetItem()函数调用remote_area_->Put

bool CachedStorageArea::SetItem(const String& key,
                                const String& value,
                                Source* source) {
  DCHECK(areas_->Contains(source));

  // A quick check to reject obviously overbudget items to avoid priming the
  // cache.
  if ((key.length() + value.length()) * 2 >
      mojom::blink::StorageArea::kPerStorageAreaQuota) {
    return false;
  }

  EnsureLoaded();
  String old_value;
  if (!map_->SetItem(key, value, &old_value))
    return false;

  const FormatOption value_format = GetValueFormat();
  absl::optional<Vector<uint8_t>> optional_old_value;
  if (!old_value.IsNull() && should_send_old_value_on_mutations_)
    optional_old_value = StringToUint8Vector(old_value, value_format);
  KURL page_url = source->GetPageUrl();
  String source_id = areas_->at(source);
  String source_string = PackSource(page_url, source_id);

  if (!is_session_storage_for_prerendering_) {
    remote_area_->Put(StringToUint8Vector(key, GetKeyFormat()),
                      StringToUint8Vector(value, value_format),
                      optional_old_value, source_string,
                      MakeSuccessCallback(source));
  }
  if (!IsSessionStorage())
    EnqueuePendingMutation(key, value, old_value, source_string);
  else if (old_value != value)
    EnqueueStorageEvent(key, old_value, value, page_url, source_id);
  return true;
}

通知Local Storage数据库进程chrome.exe【storage.mojom.StorageService 】进行AsyncDomStorageDatabase::Put函数更新数据等。

components\services\storage\dom_storage\async_dom_storage_database.cc

void AsyncDomStorageDatabase::Put(const std::vector<uint8_t>& key,

                                  const std::vector<uint8_t>& value,

                                  StatusCallback callback) {

  RunDatabaseTask(

      base::BindOnce(

          [](const std::vector<uint8_t>& key, const std::vector<uint8_t>& value,

             const DomStorageDatabase& db) { return db.Put(key, value); },

          key, value),

      std::move(callback));

}

至此通信完毕,数据已经更新。

看下测试用例和堆栈:

1、前端代码

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
<script>
function clickCounter() {
    if(typeof(Storage) !== "undefined") {
        if (localStorage.click_count) {
            localStorage.click_count = Number(localStorage.click_count)+1;
        } else {
            localStorage.click_count = 1;
        }
        document.getElementById("result").innerHTML = "你在按钮上已经点击了 " + localStorage.click_count + " 次。";
    } else {
        document.getElementById("result").innerHTML = "Sorry, your browser does not support web storage...";
    }
}
</script>
</head>
<body>
<p><button onclick="clickCounter()" type="button">点我!</button></p>
<div id="result"></div>
<p>点击按钮查看数字会自动增加。</p>
<p>关闭浏览器,重新打开这个页面点击按钮,可以看到之前的数据是有保留的。</p>
</body>
</html>

2、加载测试用例堆栈:

   1)、打开页面test03.html 主进程ID=12340通过local_storage_control_->BindStorageArea 接口与子进程ID= 28624  --type=utility --utility-sub-type=storage.mojom.StorageService 绑定。

2)、子进程ID= 28624  --type=utility --utility-sub-type=storage.mojom.StorageService 收到local_storage_control_->BindStorageArea调用堆栈:

components\services\storage\dom_storage\local_storage_impl.cc

LocalStorageImpl::BindStorageArea函数

void LocalStorageImpl::BindStorageArea(
    const blink::StorageKey& storage_key,
    mojo::PendingReceiver<blink::mojom::StorageArea> receiver) {
  if (connection_state_ != CONNECTION_FINISHED) {
    RunWhenConnected(base::BindOnce(&LocalStorageImpl::BindStorageArea,
                                    weak_ptr_factory_.GetWeakPtr(), storage_key,
                                    std::move(receiver)));
    return;
  }

  GetOrCreateStorageArea(storage_key)->Bind(std::move(receiver));
}

 3)、点击测试页按钮设置localStorage.click_count 值看下堆栈

     3.1、测试页进程ID=26764 ,存储进程进程ID= 28624(  --type=utility --utility-sub-type=storage.mojom.StorageService

     3.2、测试页进程ID=26764 前端发送SetItem函数,

   \src\out\Debug\gen\third_party\blink\renderer\bindings\modules\v8\v8_storage.cc

  

void V8Storage::NamedPropertySetterCallback(v8::Local<v8::Name> v8_property_name, v8::Local<v8::Value> v8_property_value, const v8::PropertyCallbackInfo<v8::Value>& info) {
  RUNTIME_CALL_TIMER_SCOPE_DISABLED_BY_DEFAULT(info.GetIsolate(), "Blink_StorageArea_NamedPropertySetter");

// 3.9.2. [[Set]]
// https://webidl.spec.whatwg.org/#legacy-platform-object-set
// step 1. If O and Receiver are the same object, then:
if (info.Holder() == info.This()) {
  // step 1.2.1. Invoke the named property setter with P and V.

v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Object> v8_receiver = info.Holder();
StorageArea* blink_receiver = V8Storage::ToWrappableUnsafe(isolate, v8_receiver);
const AtomicString& blink_property_name = ToCoreAtomicString(isolate, v8_property_name);
const ExceptionContextType exception_context_type = ExceptionContextType::kNamedPropertySetter;
const char* const class_like_name = "Storage";
ExceptionState exception_state(isolate, exception_context_type, class_like_name, blink_property_name, ExceptionState::kForInterceptor);
auto&& blink_property_value = NativeValueTraits<IDLString>::ArgumentValue(isolate, 1, v8_property_value, exception_state);
if (UNLIKELY(exception_state.HadException())) {
  return;
}
auto&& return_value = blink_receiver->setItem(blink_property_name, blink_property_value, exception_state);
if (UNLIKELY(exception_state.HadException())) {
  return;
}
bindings::V8SetReturnValue(info, return_value);
bindings::V8SetReturnValue(info, nullptr);
return;
}

    3.3、测试页进程ID=26764 在SetItem处理函数给【存储进程进程ID= 28624(  --type=utility --utility-sub-type=storage.mojom.StorageService发送 remote_area_->Put函数让其进行更新数据库。

\src\third_party\blink\renderer\modules\storage\cached_storage_area.cc

bool CachedStorageArea::SetItem(const String& key,
                                const String& value,
                                Source* source) {
  DCHECK(areas_->Contains(source));

  // A quick check to reject obviously overbudget items to avoid priming the
  // cache.
  if ((key.length() + value.length()) * 2 >
      mojom::blink::StorageArea::kPerStorageAreaQuota) {
    return false;
  }

  EnsureLoaded();
  String old_value;
  if (!map_->SetItem(key, value, &old_value))
    return false;

  const FormatOption value_format = GetValueFormat();
  absl::optional<Vector<uint8_t>> optional_old_value;
  if (!old_value.IsNull() && should_send_old_value_on_mutations_)
    optional_old_value = StringToUint8Vector(old_value, value_format);
  KURL page_url = source->GetPageUrl();
  String source_id = areas_->at(source);
  String source_string = PackSource(page_url, source_id);

  if (!is_session_storage_for_prerendering_) {
    //发送mojom消息给存储进程更新数据
    remote_area_->Put(StringToUint8Vector(key, GetKeyFormat()),
                      StringToUint8Vector(value, value_format),
                      optional_old_value, source_string,
                      MakeSuccessCallback(source));
  }
  if (!IsSessionStorage())
    EnqueuePendingMutation(key, value, old_value, source_string);
  else if (old_value != value)
    EnqueueStorageEvent(key, old_value, value, page_url, source_id);
  return true;
}

 测试页进程ID=26764函数堆栈如图:

4)、测试页进程ID=26764发送 remote_area_->Put函数 存储进程【存储进程ID= 28624(  --type=utility --utility-sub-type=storage.mojom.StorageService)】收到Put函数消息处理堆栈:

void StorageAreaImpl::Put()调用StorageAreaImpl::CreateCommitBatchIfNeeded()()函数。

F:\code\google\src\components\services\storage\dom_storage\storage_area_impl.cc

void StorageAreaImpl::Put()

void StorageAreaImpl::Put(
    const std::vector<uint8_t>& key,
    const std::vector<uint8_t>& value,
    const absl::optional<std::vector<uint8_t>>& client_old_value,
    const std::string& source,
    PutCallback callback) {
  if (!IsMapLoaded() || IsMapUpgradeNeeded()) {
    LoadMap(base::BindOnce(&StorageAreaImpl::Put,
                           weak_ptr_factory_.GetWeakPtr(), key, value,
                           client_old_value, source, std::move(callback)));
    return;
  }

  size_t old_item_size = 0;
  size_t old_item_memory = 0;
  size_t new_item_memory = 0;
  absl::optional<std::vector<uint8_t>> old_value;
  if (map_state_ == MapState::LOADED_KEYS_ONLY) {
    KeysOnlyMap::const_iterator found = keys_only_map_.find(key);
    if (found != keys_only_map_.end()) {
      if (client_old_value &&
          client_old_value.value().size() == found->second) {
        if (client_old_value == value) {
          // NOTE: Even though the key is not changing, we have to acknowledge
          // the change request, as clients may rely on this acknowledgement for
          // caching behavior.
          for (const auto& observer : observers_)
            observer->KeyChanged(key, value, value, source);
          std::move(callback).Run(true);  // Key already has this value.
          return;
        }
        old_value = client_old_value.value();
      } else {
#if DCHECK_IS_ON()
        // If |client_old_value| was not provided or if it's size does not
        // match, then we still let the change go through. But the notification
        // sent to clients will not contain old value. This is okay since
        // currently the only observer to these notification is the client
        // itself.
        DVLOG(1) << "Storage area with prefix "
                 << std::string(prefix_.begin(), prefix_.end())
                 << ": past value has length of " << found->second << ", but:";
        if (client_old_value) {
          DVLOG(1) << "Given past value has incorrect length of "
                   << client_old_value.value().size();
        } else {
          DVLOG(1) << "No given past value was provided.";
        }
#endif
      }
      old_item_size = key.size() + found->second;
      old_item_memory = key.size() + sizeof(size_t);
    }
    new_item_memory = key.size() + sizeof(size_t);
  } else {
    DCHECK_EQ(map_state_, MapState::LOADED_KEYS_AND_VALUES);
    auto found = keys_values_map_.find(key);
    if (found != keys_values_map_.end()) {
      if (found->second == value) {
        // NOTE: Even though the key is not changing, we have to acknowledge
        // the change request, as clients may rely on this acknowledgement for
        // caching behavior.
        for (const auto& observer : observers_)
          observer->KeyChanged(key, value, value, source);
        std::move(callback).Run(true);  // Key already has this value.
        return;
      }
      old_value = std::move(found->second);
      old_item_size = key.size() + old_value.value().size();
      old_item_memory = old_item_size;
    }
    new_item_memory = key.size() + value.size();
  }

  size_t new_item_size = key.size() + value.size();
  size_t new_storage_used = storage_used_ - old_item_size + new_item_size;

  // Only check quota if the size is increasing, this allows
  // shrinking changes to pre-existing maps that are over budget.
  if (new_item_size > old_item_size && new_storage_used > max_size_) {
    if (map_state_ == MapState::LOADED_KEYS_ONLY) {
      receivers_.ReportBadMessage(
          "The quota in browser cannot exceed when there is only one "
          "renderer.");
    } else {
      for (const auto& observer : observers_)
        observer->KeyChangeFailed(key, source);
      std::move(callback).Run(false);
    }
    return;
  }

  if (database_) {
    CreateCommitBatchIfNeeded();
    // No need to store values in |commit_batch_| if values are already
    // available in |keys_values_map_|, since CommitChanges() will take values
    // from there.
    if (map_state_ == MapState::LOADED_KEYS_ONLY)
      commit_batch_->changed_values[key] = value;
    else
      commit_batch_->changed_keys.insert(key);
  }

  if (map_state_ == MapState::LOADED_KEYS_ONLY)
    keys_only_map_[key] = value.size();
  else
    keys_values_map_[key] = value;

  storage_used_ = new_storage_used;
  memory_used_ += new_item_memory - old_item_memory;
  for (const auto& observer : observers_)
    observer->KeyChanged(key, value, old_value, source);
  std::move(callback).Run(true);
}

void StorageAreaImpl::CreateCommitBatchIfNeeded()函数实现在

components\services\storage\dom_storage\async_dom_storage_database.cc文件中。

void StorageAreaImpl::CreateCommitBatchIfNeeded() {
  if (commit_batch_)
    return;
  DCHECK(database_);

  commit_batch_ = std::make_unique<CommitBatch>();
  StartCommitTimer();
}

 看下存储进程ID= 28624此刻堆栈:

至此调用过程分析完毕。

三、总结: 网页window.localStorage对象调用c++storage.idl【v8_storage.cc】接口, 然后render进程通过调用 storage_area.mojom接口与存储进程【type=storage.mojom.StorageService】进行更新leveldb数据库的。

最后看下效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值