OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "webkit/dom_storage/session_storage_database.h" | |
6 | |
7 #include "base/file_util.h" | |
8 #include "base/string_number_conversions.h" | |
9 #include "base/utf_string_conversions.h" | |
10 #include "third_party/leveldatabase/src/include/leveldb/db.h" | |
11 #include "third_party/leveldatabase/src/include/leveldb/iterator.h" | |
12 #include "third_party/leveldatabase/src/include/leveldb/status.h" | |
13 #include "third_party/leveldatabase/src/include/leveldb/options.h" | |
14 | |
15 #include <sstream> | |
16 | |
17 // Layout of the database: | |
michaeln
2012/04/03 22:56:45
This seems like the best place to start hammering
| |
18 // | key | value | | |
19 // --------------------------------------------------- | |
20 // | area-1 (1 = namespace id) | dummy | start of area-1-* keys | |
michaeln
2012/04/03 22:56:45
what's the benefit of the dummy entries?
marja
2012/04/10 15:34:45
To be able to iterate "all origins that belong to
| |
21 // | area-1-origin1 | 1 (mapid) | | |
22 // | area-1-origin2 | 2 | | |
23 // | area-2 | dummy | | |
24 // | area-2-origin1 | 1 | shallow copy | |
25 // | area-3 | | | |
26 // | area-3-origin1 | 3 | deep copy | |
27 // | map-1 | dummy | start of map-1-* keys | |
28 // | map-1-a | b | a = b in map 1 | |
29 // | ... | | | |
30 // | next-map-id | 4 | | |
31 | |
32 namespace { | |
33 | |
34 // Helper functions for creating the keys. | |
35 std::string AreaKey(int64 namespace_id, const GURL& origin) { | |
36 std::ostringstream stream; | |
michaeln
2012/04/03 22:56:45
do we use sstream elswhere in the project? if not,
marja
2012/04/10 15:34:45
Done.
| |
37 stream << "area-" << namespace_id << "-" << origin.spec(); | |
38 return stream.str(); | |
39 } | |
40 | |
41 std::string MapStartKey(const std::string& map_id) { | |
42 std::ostringstream stream; | |
43 stream << "map-" << map_id; | |
44 return stream.str(); | |
45 } | |
46 | |
47 std::string MapKey(const std::string& map_id, const std::string& key) { | |
48 std::ostringstream stream; | |
49 stream << "value-" << map_id << "-" << key; | |
50 return stream.str(); | |
51 } | |
52 | |
53 std::string NextMapIdKey() { | |
54 return "next-map-id"; | |
55 } | |
56 | |
57 } // namespace | |
58 | |
59 namespace dom_storage { | |
60 | |
61 SessionStorageDatabase::SessionStorageDatabase(const FilePath& file_path) | |
62 : DomStorageDatabase(file_path) { | |
63 } | |
64 | |
65 SessionStorageDatabase::~SessionStorageDatabase() { } | |
66 | |
67 void SessionStorageDatabase::ReadAllValues(int64 namespace_id, | |
68 const GURL& origin, | |
69 ValuesMap* result) { | |
70 // We don't create a database if it doesn't exist. In that case, there is | |
71 // nothing to be added to the result. | |
72 if (!LazyOpen(false)) | |
73 return; | |
74 // Check if there is map for namespace_id-origin. | |
75 std::string area_key = AreaKey(namespace_id, origin); | |
76 std::string map_id; | |
77 leveldb::Status s = db_->Get(leveldb::ReadOptions(), area_key, &map_id); | |
78 if (!s.ok() || map_id.empty()) | |
79 return; | |
80 ReadAllValuesInMap(map_id, result); | |
81 } | |
82 | |
83 bool SessionStorageDatabase::CommitChanges(int64 namespace_id, | |
michaeln
2012/04/03 22:56:45
Another detail to hammer out is around the read/wr
marja
2012/04/10 15:34:45
Added WriteBatch. If we allow writing from 2 threa
| |
84 const GURL& origin, | |
85 bool clear_all_first, | |
86 const ValuesMap& changes) { | |
87 if (!LazyOpen(!changes.empty())) { | |
88 // If we're being asked to commit changes that will result in an | |
89 // empty database, we return true if the database file doesn't exist. | |
90 return clear_all_first && changes.empty() && | |
91 !file_util::PathExists(file_path_); | |
92 } | |
93 std::string area_key = AreaKey(namespace_id, origin); | |
94 std::string map_id; | |
95 leveldb::Status s = db_->Get(leveldb::ReadOptions(), area_key, &map_id); | |
96 DCHECK(s.ok() || s.IsNotFound()); | |
97 if (s.IsNotFound()) { | |
98 // Create a new map. | |
99 std::string next_map_id_key = NextMapIdKey(); | |
100 s = db_->Get(leveldb::ReadOptions(), next_map_id_key, &map_id); | |
101 DCHECK(s.ok() || s.IsNotFound()); | |
102 int64 next_map_id = 0; | |
103 if (!s.IsNotFound() || !base::StringToInt64(map_id, &next_map_id)) | |
104 map_id = "0"; | |
105 ++next_map_id; | |
106 s = db_->Put(leveldb::WriteOptions(), next_map_id_key, | |
107 base::Int64ToString(next_map_id)); | |
108 DCHECK(s.ok()); | |
109 s = db_->Put(leveldb::WriteOptions(), area_key, map_id); | |
110 } else if (clear_all_first) { | |
111 ValuesMap values; | |
112 ReadAllValuesInMap(map_id, &values); | |
michaeln
2012/04/03 22:56:45
is there a way to clear values w/o having to read
marja
2012/04/10 15:34:45
No, afaik. The interface is just Get(), Put(), Del
| |
113 for (ValuesMap::const_iterator it = values.begin(); it != values.end(); | |
114 ++it) { | |
115 leveldb::Status s = | |
116 db_->Delete(leveldb::WriteOptions(), UTF16ToUTF8(it->first)); | |
117 DCHECK(s.ok()); | |
118 } | |
119 } | |
120 | |
121 for(ValuesMap::const_iterator it = changes.begin(); | |
122 it != changes.end(); ++it) { | |
123 NullableString16 value = it->second; | |
124 std::string key = MapKey(map_id, UTF16ToUTF8(it->first)); | |
125 if (value.is_null()) { | |
126 s = db_->Delete(leveldb::WriteOptions(), key); | |
127 DCHECK(s.ok()); | |
128 } else { | |
129 s = db_->Put(leveldb::WriteOptions(), key, UTF16ToUTF8(value.string())); | |
michaeln
2012/04/03 22:56:45
i think we need to treat the 'value' as binary dat
marja
2012/04/10 15:34:45
Ok, UTF16ToUTF8 was definitely wrong. Otoh, we can
| |
130 DCHECK(s.ok()); | |
131 } | |
132 } | |
133 return true; | |
134 } | |
135 | |
136 bool SessionStorageDatabase::LazyOpen(bool create_if_needed) { | |
michaeln
2012/04/03 22:56:45
This stuff about lazily creating the directory on
| |
137 if (failed_to_open_) { | |
138 // Don't try to open a database that we know has failed | |
139 // already. | |
140 return false; | |
141 } | |
142 | |
143 if (IsOpen()) | |
144 return true; | |
145 | |
146 bool database_exists = file_util::PathExists(file_path_); | |
147 | |
148 if (!database_exists && !create_if_needed) { | |
149 // If the file doesn't exist already and we haven't been asked to create | |
150 // a file on disk, then we don't bother opening the database. This means | |
151 // we wait until we absolutely need to put something onto disk before we | |
152 // do so. | |
153 return false; | |
154 } | |
155 | |
156 leveldb::Options options; | |
157 options.create_if_missing = create_if_needed; | |
158 leveldb::Status s; | |
159 leveldb::DB* db; | |
160 #if defined(OS_WIN) | |
161 s = leveldb::DB::Open(options, WideToUTF8(file_path_.value()), &db); | |
162 #elif defined(OS_POSIX) | |
163 s = leveldb::DB::Open(options, file_path_.value(), &db); | |
164 #endif | |
165 if (!s.ok()) { | |
166 DCHECK(db == NULL); | |
167 failed_to_open_ = true; | |
168 return false; | |
169 } | |
170 | |
171 db_.reset(db); | |
172 return true; | |
173 } | |
174 | |
175 bool SessionStorageDatabase::IsOpen() const { | |
176 return db_.get(); | |
177 } | |
178 | |
179 void SessionStorageDatabase::ReadAllValuesInMap(const std::string map_id, | |
180 ValuesMap* result) { | |
181 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); | |
182 std::string map_start_key = MapStartKey(map_id); | |
183 // Skip the dummy entry, then start iterating the keys in the map. | |
184 for (it->Seek(map_start_key), it->Next(); it->Valid(); it->Next()) { | |
185 string16 key = UTF8ToUTF16(it->key().ToString()); | |
186 string16 value = UTF8ToUTF16(it->value().ToString()); | |
187 // Key is of the form "value-<mapid>-<key>". | |
188 size_t second_dash = key.find('-', 6); | |
189 if (second_dash == std::string::npos) { | |
190 // Iterated beyond the keys in this map. | |
191 break; | |
192 } | |
193 (*result)[key.substr(second_dash + 1)] = NullableString16(value, false); | |
194 } | |
195 } | |
196 | |
197 } // namespace dom_storage | |
OLD | NEW |