Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(147)

Side by Side Diff: content/browser/in_process_webkit/indexed_db_context.cc

Issue 7692016: Delete indexedDBs from the cookie tree ui. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/in_process_webkit/indexed_db_context.h" 5 #include "content/browser/in_process_webkit/indexed_db_context.h"
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/file_util.h" 8 #include "base/file_util.h"
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/message_loop_proxy.h" 10 #include "base/message_loop_proxy.h"
11 #include "base/string_util.h" 11 #include "base/string_util.h"
12 #include "base/task.h" 12 #include "base/task.h"
13 #include "base/utf_string_conversions.h" 13 #include "base/utf_string_conversions.h"
14 #include "content/browser/browser_thread.h" 14 #include "content/browser/browser_thread.h"
15 #include "content/browser/in_process_webkit/indexed_db_quota_client.h" 15 #include "content/browser/in_process_webkit/indexed_db_quota_client.h"
16 #include "content/browser/in_process_webkit/webkit_context.h" 16 #include "content/browser/in_process_webkit/webkit_context.h"
17 #include "content/common/content_switches.h" 17 #include "content/common/content_switches.h"
18 #include "googleurl/src/gurl.h"
19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCString.h" 18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebCString.h"
20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebIDBDatabase.h" 19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebIDBDatabase.h"
21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebIDBFactory.h" 20 #include "third_party/WebKit/Source/WebKit/chromium/public/WebIDBFactory.h"
22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h" 21 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
23 #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h" 22 #include "third_party/WebKit/Source/WebKit/chromium/public/WebString.h"
24 #include "webkit/database/database_util.h" 23 #include "webkit/database/database_util.h"
25 #include "webkit/glue/webkit_glue.h" 24 #include "webkit/glue/webkit_glue.h"
26 #include "webkit/quota/quota_manager.h" 25 #include "webkit/quota/quota_manager.h"
27 #include "webkit/quota/special_storage_policy.h" 26 #include "webkit/quota/special_storage_policy.h"
28 27
29 using webkit_database::DatabaseUtil; 28 using webkit_database::DatabaseUtil;
30 using WebKit::WebIDBDatabase; 29 using WebKit::WebIDBDatabase;
31 using WebKit::WebIDBFactory; 30 using WebKit::WebIDBFactory;
32 using WebKit::WebSecurityOrigin; 31 using WebKit::WebSecurityOrigin;
33 32
34 namespace { 33 namespace {
35 34
35 void GetAllOriginsAndPaths(
michaeln 2011/08/23 20:39:48 Consolidated this directory listing stuff in one p
36 const FilePath& indexeddb_path,
37 std::vector<GURL>* origins,
38 std::vector<FilePath>* file_paths) {
39 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
40 if (indexeddb_path.empty())
41 return;
42 file_util::FileEnumerator file_enumerator(indexeddb_path,
43 false, file_util::FileEnumerator::DIRECTORIES);
44 for (FilePath file_path = file_enumerator.Next(); !file_path.empty();
45 file_path = file_enumerator.Next()) {
46 if (file_path.Extension() == IndexedDBContext::kIndexedDBExtension) {
47 WebKit::WebString origin_id_webstring =
48 webkit_glue::FilePathToWebString(file_path.BaseName());
49 origins->push_back(
50 DatabaseUtil::GetOriginFromIdentifier(origin_id_webstring));
51 if (file_paths)
52 file_paths->push_back(file_path);
53 }
54 }
55 }
56
36 void ClearLocalState( 57 void ClearLocalState(
37 const FilePath& indexeddb_path, 58 const FilePath& indexeddb_path,
38 scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy) { 59 scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy) {
39 file_util::FileEnumerator file_enumerator( 60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
40 indexeddb_path, false, file_util::FileEnumerator::FILES); 61 std::vector<GURL> origins;
41 // TODO(pastarmovj): We might need to consider exchanging this loop for 62 std::vector<FilePath> file_paths;
42 // something more efficient in the future. 63 GetAllOriginsAndPaths(indexeddb_path, &origins, &file_paths);
43 for (FilePath file_path = file_enumerator.Next(); !file_path.empty(); 64 DCHECK_EQ(origins.size(), file_paths.size());
44 file_path = file_enumerator.Next()) { 65 std::vector<FilePath>::const_iterator file_path_iter = file_paths.begin();
45 if (file_path.Extension() != IndexedDBContext::kIndexedDBExtension) 66 for (std::vector<GURL>::const_iterator iter = origins.begin();
67 iter != origins.end(); ++iter, ++file_path_iter) {
68 if (special_storage_policy->IsStorageProtected(*iter))
46 continue; 69 continue;
47 WebSecurityOrigin origin = 70 file_util::Delete(*file_path_iter, true);
48 WebSecurityOrigin::createFromDatabaseIdentifier(
49 webkit_glue::FilePathToWebString(file_path.BaseName()));
50 if (special_storage_policy->IsStorageProtected(GURL(origin.toString())))
51 continue;
52 file_util::Delete(file_path, false);
michaeln 2011/08/23 21:54:39 Looks like this was never updated in the switch fr
53 } 71 }
54 } 72 }
55 73
56 } // namespace 74 } // namespace
57 75
58 const FilePath::CharType IndexedDBContext::kIndexedDBDirectory[] = 76 const FilePath::CharType IndexedDBContext::kIndexedDBDirectory[] =
59 FILE_PATH_LITERAL("IndexedDB"); 77 FILE_PATH_LITERAL("IndexedDB");
60 78
61 const FilePath::CharType IndexedDBContext::kIndexedDBExtension[] = 79 const FilePath::CharType IndexedDBContext::kIndexedDBExtension[] =
62 FILE_PATH_LITERAL(".leveldb"); 80 FILE_PATH_LITERAL(".leveldb");
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
97 }; 115 };
98 116
99 IndexedDBContext::IndexedDBContext( 117 IndexedDBContext::IndexedDBContext(
100 WebKitContext* webkit_context, 118 WebKitContext* webkit_context,
101 quota::SpecialStoragePolicy* special_storage_policy, 119 quota::SpecialStoragePolicy* special_storage_policy,
102 quota::QuotaManagerProxy* quota_manager_proxy, 120 quota::QuotaManagerProxy* quota_manager_proxy,
103 base::MessageLoopProxy* webkit_thread_loop) 121 base::MessageLoopProxy* webkit_thread_loop)
104 : clear_local_state_on_exit_(false), 122 : clear_local_state_on_exit_(false),
105 special_storage_policy_(special_storage_policy), 123 special_storage_policy_(special_storage_policy),
106 quota_manager_proxy_(quota_manager_proxy) { 124 quota_manager_proxy_(quota_manager_proxy) {
107 data_path_ = webkit_context->data_path().Append(kIndexedDBDirectory); 125 if (!webkit_context->is_incognito())
michaeln 2011/08/23 20:39:48 Made sure to not look in the filesystem for incogn
126 data_path_ = webkit_context->data_path().Append(kIndexedDBDirectory);
108 if (quota_manager_proxy && 127 if (quota_manager_proxy &&
109 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess)) { 128 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess)) {
110 quota_manager_proxy->RegisterClient( 129 quota_manager_proxy->RegisterClient(
111 new IndexedDBQuotaClient(webkit_thread_loop, this)); 130 new IndexedDBQuotaClient(webkit_thread_loop, this));
112 } 131 }
113 } 132 }
114 133
115 IndexedDBContext::~IndexedDBContext() { 134 IndexedDBContext::~IndexedDBContext() {
116 WebKit::WebIDBFactory* factory = idb_factory_.release(); 135 WebKit::WebIDBFactory* factory = idb_factory_.release();
117 if (factory) 136 if (factory)
118 BrowserThread::DeleteSoon(BrowserThread::WEBKIT, FROM_HERE, factory); 137 BrowserThread::DeleteSoon(BrowserThread::WEBKIT, FROM_HERE, factory);
119 138
120 if (clear_local_state_on_exit_) { 139 if (clear_local_state_on_exit_ && !data_path_.empty()) {
121 // No WEBKIT thread here means we are running in a unit test where no clean 140 // No WEBKIT thread here means we are running in a unit test where no clean
122 // up is needed. 141 // up is needed.
123 BrowserThread::PostTask(BrowserThread::WEBKIT, FROM_HERE, 142 BrowserThread::PostTask(BrowserThread::WEBKIT, FROM_HERE,
124 NewRunnableFunction(&ClearLocalState, data_path_, 143 NewRunnableFunction(&ClearLocalState, data_path_,
125 special_storage_policy_)); 144 special_storage_policy_));
126 } 145 }
127 } 146 }
128 147
129 WebIDBFactory* IndexedDBContext::GetIDBFactory() { 148 WebIDBFactory* IndexedDBContext::GetIDBFactory() {
149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
130 if (!idb_factory_.get()) 150 if (!idb_factory_.get())
131 idb_factory_.reset(WebIDBFactory::create()); 151 idb_factory_.reset(WebIDBFactory::create());
132 DCHECK(idb_factory_.get()); 152 DCHECK(idb_factory_.get());
133 return idb_factory_.get(); 153 return idb_factory_.get();
134 } 154 }
135 155
136 FilePath IndexedDBContext::GetIndexedDBFilePath(
137 const string16& origin_id) const {
138 FilePath::StringType id =
139 webkit_glue::WebStringToFilePathString(origin_id).append(
140 FILE_PATH_LITERAL(".indexeddb"));
141 return data_path_.Append(id.append(kIndexedDBExtension));
142 }
143
144 // Note: This is not called in response to a UI action in Content Settings. Only 156 // Note: This is not called in response to a UI action in Content Settings. Only
145 // extension data deleter and quota manager currently call this. 157 // extension data deleter and quota manager currently call this.
146 void IndexedDBContext::DeleteIndexedDBForOrigin(const GURL& origin_url) { 158 void IndexedDBContext::DeleteIndexedDBForOrigin(const GURL& origin_url) {
147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT)); 159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
160 if (data_path_.empty() || !IsInOriginSet(origin_url))
161 return;
148 string16 origin_id = DatabaseUtil::GetOriginIdentifier(origin_url); 162 string16 origin_id = DatabaseUtil::GetOriginIdentifier(origin_url);
149 FilePath idb_directory = GetIndexedDBFilePath(origin_id); 163 FilePath idb_directory = GetIndexedDBFilePath(origin_id);
150 if (idb_directory.BaseName().value().substr(0, strlen("chrome-extension")) == 164 if (idb_directory.BaseName().value().substr(0, strlen("chrome-extension")) ==
151 FILE_PATH_LITERAL("chrome-extension") || 165 FILE_PATH_LITERAL("chrome-extension") ||
152 connection_count_.find(origin_url) == connection_count_.end()) { 166 connection_count_.find(origin_url) == connection_count_.end()) {
153 EnsureDiskUsageCacheInitialized(origin_url); 167 EnsureDiskUsageCacheInitialized(origin_url);
154 file_util::Delete(idb_directory, true /*recursive*/); 168 bool deleted = file_util::Delete(idb_directory, true /*recursive*/);
155 QueryDiskAndUpdateQuotaUsage(origin_url); 169 QueryDiskAndUpdateQuotaUsage(origin_url);
170 if (deleted) {
171 RemoveFromOriginSet(origin_url);
172 origin_size_map_.erase(origin_url);
173 }
156 } 174 }
157 } 175 }
158 176
159 bool IndexedDBContext::IsUnlimitedStorageGranted( 177 bool IndexedDBContext::IsUnlimitedStorageGranted(
160 const GURL& origin) const { 178 const GURL& origin) const {
161 return special_storage_policy_->IsStorageUnlimited(origin); 179 return special_storage_policy_->IsStorageUnlimited(origin);
162 } 180 }
163 181
164 // TODO(dgrogan): Merge this code with the similar loop in 182 void IndexedDBContext::GetAllOrigins(std::vector<GURL>* origins) {
michaeln 2011/08/23 20:39:48 This can get called frequently, utilize a cache of
165 // BrowsingDataIndexedDBHelperImpl::FetchIndexedDBInfoInWebKitThread.
166 void IndexedDBContext::GetAllOriginIdentifiers(
167 std::vector<string16>* origin_ids) {
168 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT)); 183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::WEBKIT));
169 file_util::FileEnumerator file_enumerator(data_path_, 184 std::set<GURL>* origins_set = GetOriginSet();
170 false, file_util::FileEnumerator::DIRECTORIES); 185 for (std::set<GURL>::const_iterator iter = origins_set->begin();
171 for (FilePath file_path = file_enumerator.Next(); !file_path.empty(); 186 iter != origins_set->end(); ++iter) {
172 file_path = file_enumerator.Next()) { 187 origins->push_back(*iter);
173 if (file_path.Extension() == IndexedDBContext::kIndexedDBExtension) {
174 WebKit::WebString origin_id_webstring =
175 webkit_glue::FilePathToWebString(file_path.BaseName());
176 origin_ids->push_back(origin_id_webstring);
177 }
178 } 188 }
179 } 189 }
180 190
181 int64 IndexedDBContext::GetOriginDiskUsage(const GURL& origin_url) { 191 int64 IndexedDBContext::GetOriginDiskUsage(const GURL& origin_url) {
182 return ResetDiskUsageCache(origin_url); 192 if (data_path_.empty() || !IsInOriginSet(origin_url))
193 return 0;
194 EnsureDiskUsageCacheInitialized(origin_url);
195 return origin_size_map_[origin_url];
michaeln 2011/08/23 20:39:48 Depend more heavily on the cached size values here
196 }
197
198 base::Time IndexedDBContext::GetOriginLastModified(const GURL& origin_url) {
199 if (data_path_.empty() || !IsInOriginSet(origin_url))
200 return base::Time();
201 string16 origin_id = DatabaseUtil::GetOriginIdentifier(origin_url);
202 FilePath idb_directory = GetIndexedDBFilePath(origin_id);
203 base::PlatformFileInfo file_info;
204 if (!file_util::GetFileInfo(idb_directory, &file_info))
205 return base::Time();
206 return file_info.last_modified;
183 } 207 }
184 208
185 void IndexedDBContext::ConnectionOpened(const GURL& origin_url) { 209 void IndexedDBContext::ConnectionOpened(const GURL& origin_url) {
186 if (quota_manager_proxy()) { 210 if (quota_manager_proxy()) {
187 quota_manager_proxy()->NotifyStorageAccessed( 211 quota_manager_proxy()->NotifyStorageAccessed(
188 quota::QuotaClient::kIndexedDatabase, origin_url, 212 quota::QuotaClient::kIndexedDatabase, origin_url,
189 quota::kStorageTypeTemporary); 213 quota::kStorageTypeTemporary);
190 } 214 }
215 AddToOriginSet(origin_url);
191 connection_count_[origin_url]++; 216 connection_count_[origin_url]++;
192 QueryAvailableQuota(origin_url); 217 QueryAvailableQuota(origin_url);
193 EnsureDiskUsageCacheInitialized(origin_url); 218 EnsureDiskUsageCacheInitialized(origin_url);
194 } 219 }
195 220
196 void IndexedDBContext::ConnectionClosed(const GURL& origin_url) { 221 void IndexedDBContext::ConnectionClosed(const GURL& origin_url) {
197 DCHECK(connection_count_[origin_url] > 0); 222 DCHECK(connection_count_[origin_url] > 0);
198 if (quota_manager_proxy()) { 223 if (quota_manager_proxy()) {
199 quota_manager_proxy()->NotifyStorageAccessed( 224 quota_manager_proxy()->NotifyStorageAccessed(
200 quota::QuotaClient::kIndexedDatabase, origin_url, 225 quota::QuotaClient::kIndexedDatabase, origin_url,
201 quota::kStorageTypeTemporary); 226 quota::kStorageTypeTemporary);
202 } 227 }
203 connection_count_[origin_url]--; 228 connection_count_[origin_url]--;
204 if (connection_count_[origin_url] == 0) { 229 if (connection_count_[origin_url] == 0) {
205 QueryDiskAndUpdateQuotaUsage(origin_url); 230 QueryDiskAndUpdateQuotaUsage(origin_url);
206 connection_count_.erase(origin_url); 231 connection_count_.erase(origin_url);
207 } 232 }
208 } 233 }
209 234
210 void IndexedDBContext::TransactionComplete(const GURL& origin_url) { 235 void IndexedDBContext::TransactionComplete(const GURL& origin_url) {
211 DCHECK(connection_count_[origin_url] > 0); 236 DCHECK(connection_count_[origin_url] > 0);
212 QueryDiskAndUpdateQuotaUsage(origin_url); 237 QueryDiskAndUpdateQuotaUsage(origin_url);
213 QueryAvailableQuota(origin_url); 238 QueryAvailableQuota(origin_url);
214 } 239 }
215 240
241 FilePath IndexedDBContext::GetIndexedDBFilePath(
242 const string16& origin_id) const {
243 if (data_path_.empty())
244 return FilePath();
245 FilePath::StringType id =
246 webkit_glue::WebStringToFilePathString(origin_id).append(
247 FILE_PATH_LITERAL(".indexeddb"));
248 return data_path_.Append(id.append(kIndexedDBExtension));
249 }
250
216 bool IndexedDBContext::WouldBeOverQuota(const GURL& origin_url, 251 bool IndexedDBContext::WouldBeOverQuota(const GURL& origin_url,
217 int64 additional_bytes) { 252 int64 additional_bytes) {
218 if (space_available_map_.find(origin_url) == space_available_map_.end()) { 253 if (space_available_map_.find(origin_url) == space_available_map_.end()) {
219 // We haven't heard back from the QuotaManager yet, just let it through. 254 // We haven't heard back from the QuotaManager yet, just let it through.
220 return false; 255 return false;
221 } 256 }
222 bool over_quota = additional_bytes > space_available_map_[origin_url]; 257 bool over_quota = additional_bytes > space_available_map_[origin_url];
223 return over_quota; 258 return over_quota;
224 } 259 }
225 260
226 bool IndexedDBContext::IsOverQuota(const GURL& origin_url) { 261 bool IndexedDBContext::IsOverQuota(const GURL& origin_url) {
227 const int kOneAdditionalByte = 1; 262 const int kOneAdditionalByte = 1;
228 return WouldBeOverQuota(origin_url, kOneAdditionalByte); 263 return WouldBeOverQuota(origin_url, kOneAdditionalByte);
229 } 264 }
230 265
231 quota::QuotaManagerProxy* IndexedDBContext::quota_manager_proxy() { 266 quota::QuotaManagerProxy* IndexedDBContext::quota_manager_proxy() {
232 return quota_manager_proxy_; 267 return quota_manager_proxy_;
233 } 268 }
234 269
235 int64 IndexedDBContext::ReadUsageFromDisk(const GURL& origin_url) const { 270 int64 IndexedDBContext::ReadUsageFromDisk(const GURL& origin_url) const {
236 string16 origin_id = DatabaseUtil::GetOriginIdentifier(origin_url); 271 string16 origin_id = DatabaseUtil::GetOriginIdentifier(origin_url);
237 FilePath file_path = GetIndexedDBFilePath(origin_id); 272 FilePath file_path = GetIndexedDBFilePath(origin_id);
238 return file_util::ComputeDirectorySize(file_path); 273 return file_path.empty() ? 0 : file_util::ComputeDirectorySize(file_path);
239 } 274 }
240 275
241 void IndexedDBContext::EnsureDiskUsageCacheInitialized(const GURL& origin_url) { 276 void IndexedDBContext::EnsureDiskUsageCacheInitialized(const GURL& origin_url) {
242 if (origin_size_map_.find(origin_url) == origin_size_map_.end()) 277 if (origin_size_map_.find(origin_url) == origin_size_map_.end())
243 ResetDiskUsageCache(origin_url); 278 origin_size_map_[origin_url] = ReadUsageFromDisk(origin_url);
244 } 279 }
245 280
246 void IndexedDBContext::QueryDiskAndUpdateQuotaUsage(const GURL& origin_url) { 281 void IndexedDBContext::QueryDiskAndUpdateQuotaUsage(const GURL& origin_url) {
247 int64 former_disk_usage = origin_size_map_[origin_url]; 282 int64 former_disk_usage = origin_size_map_[origin_url];
248 int64 current_disk_usage = ReadUsageFromDisk(origin_url); 283 int64 current_disk_usage = ReadUsageFromDisk(origin_url);
249 int64 difference = current_disk_usage - former_disk_usage; 284 int64 difference = current_disk_usage - former_disk_usage;
250 if (difference) { 285 if (difference) {
251 origin_size_map_[origin_url] = current_disk_usage; 286 origin_size_map_[origin_url] = current_disk_usage;
252 // quota_manager_proxy() is NULL in unit tests. 287 // quota_manager_proxy() is NULL in unit tests.
253 if (quota_manager_proxy()) 288 if (quota_manager_proxy())
(...skipping 24 matching lines...) Expand all
278 if (!quota_manager_proxy()->quota_manager()) 313 if (!quota_manager_proxy()->quota_manager())
279 return; 314 return;
280 IndexedDBGetUsageAndQuotaCallback* callback = 315 IndexedDBGetUsageAndQuotaCallback* callback =
281 new IndexedDBGetUsageAndQuotaCallback(this, origin_url); 316 new IndexedDBGetUsageAndQuotaCallback(this, origin_url);
282 quota_manager_proxy()->quota_manager()->GetUsageAndQuota( 317 quota_manager_proxy()->quota_manager()->GetUsageAndQuota(
283 origin_url, 318 origin_url,
284 quota::kStorageTypeTemporary, 319 quota::kStorageTypeTemporary,
285 callback); 320 callback);
286 } 321 }
287 322
288 int64 IndexedDBContext::ResetDiskUsageCache(const GURL& origin_url) { 323 std::set<GURL>* IndexedDBContext::GetOriginSet() {
289 origin_size_map_[origin_url] = ReadUsageFromDisk(origin_url); 324 if (!origin_set_.get()) {
290 return origin_size_map_[origin_url]; 325 origin_set_.reset(new std::set<GURL>);
326 std::vector<GURL> origins;
327 GetAllOriginsAndPaths(data_path_, &origins, NULL);
328 for (std::vector<GURL>::const_iterator iter = origins.begin();
329 iter != origins.end(); ++iter) {
330 origin_set_->insert(*iter);
331 }
332 }
333 return origin_set_.get();
291 } 334 }
335
336 void IndexedDBContext::ResetCaches() {
337 origin_set_.reset();
338 origin_size_map_.clear();
339 space_available_map_.clear();
340 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698