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

Side by Side Diff: chrome/browser/net/sqlite_persistent_cookie_store.cc

Issue 10407124: Don't force non-session only cookies to be session only cookies, instead delete on shutdown (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: updates Created 8 years, 6 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) 2012 The Chromium Authors. All rights reserved. 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 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 "chrome/browser/net/sqlite_persistent_cookie_store.h" 5 #include "chrome/browser/net/sqlite_persistent_cookie_store.h"
6 6
7 #include <list> 7 #include <list>
8 #include <map> 8 #include <map>
9 #include <set> 9 #include <set>
10 #include <utility> 10 #include <utility>
11 11
12 #include "base/basictypes.h" 12 #include "base/basictypes.h"
13 #include "base/bind.h" 13 #include "base/bind.h"
14 #include "base/callback.h" 14 #include "base/callback.h"
15 #include "base/file_path.h" 15 #include "base/file_path.h"
16 #include "base/file_util.h" 16 #include "base/file_util.h"
17 #include "base/logging.h" 17 #include "base/logging.h"
18 #include "base/memory/ref_counted.h" 18 #include "base/memory/ref_counted.h"
19 #include "base/memory/scoped_ptr.h" 19 #include "base/memory/scoped_ptr.h"
20 #include "base/metrics/histogram.h" 20 #include "base/metrics/histogram.h"
21 #include "base/string_util.h" 21 #include "base/string_util.h"
22 #include "base/synchronization/lock.h" 22 #include "base/synchronization/lock.h"
23 #include "base/threading/thread.h" 23 #include "base/threading/thread.h"
24 #include "base/threading/thread_restrictions.h" 24 #include "base/threading/thread_restrictions.h"
25 #include "base/time.h" 25 #include "base/time.h"
26 #include "chrome/browser/diagnostics/sqlite_diagnostics.h" 26 #include "chrome/browser/diagnostics/sqlite_diagnostics.h"
27 #include "chrome/browser/net/clear_on_exit_policy.h"
27 #include "content/public/browser/browser_thread.h" 28 #include "content/public/browser/browser_thread.h"
28 #include "googleurl/src/gurl.h" 29 #include "googleurl/src/gurl.h"
29 #include "net/base/registry_controlled_domain.h" 30 #include "net/base/registry_controlled_domain.h"
30 #include "sql/meta_table.h" 31 #include "sql/meta_table.h"
31 #include "sql/statement.h" 32 #include "sql/statement.h"
32 #include "sql/transaction.h" 33 #include "sql/transaction.h"
33 34
34 using base::Time; 35 using base::Time;
35 using content::BrowserThread; 36 using content::BrowserThread;
36 37
(...skipping 15 matching lines...) Expand all
52 // CompleteLoadForKeyOnIOThread to the IO thread to notify the caller of 53 // CompleteLoadForKeyOnIOThread to the IO thread to notify the caller of
53 // SQLitePersistentCookieStore::LoadCookiesForKey that that load is complete. 54 // SQLitePersistentCookieStore::LoadCookiesForKey that that load is complete.
54 // 55 //
55 // Subsequent to loading, mutations may be queued by any thread using 56 // Subsequent to loading, mutations may be queued by any thread using
56 // AddCookie, UpdateCookieAccessTime, and DeleteCookie. These are flushed to 57 // AddCookie, UpdateCookieAccessTime, and DeleteCookie. These are flushed to
57 // disk on the DB thread every 30 seconds, 512 operations, or call to Flush(), 58 // disk on the DB thread every 30 seconds, 512 operations, or call to Flush(),
58 // whichever occurs first. 59 // whichever occurs first.
59 class SQLitePersistentCookieStore::Backend 60 class SQLitePersistentCookieStore::Backend
60 : public base::RefCountedThreadSafe<SQLitePersistentCookieStore::Backend> { 61 : public base::RefCountedThreadSafe<SQLitePersistentCookieStore::Backend> {
61 public: 62 public:
62 Backend(const FilePath& path, bool restore_old_session_cookies) 63 Backend(const FilePath& path,
64 bool restore_old_session_cookies,
65 ClearOnExitPolicy* clear_on_exit_policy)
63 : path_(path), 66 : path_(path),
64 db_(NULL), 67 db_(NULL),
65 num_pending_(0), 68 num_pending_(0),
66 clear_local_state_on_exit_(false), 69 clear_local_state_on_exit_(false),
67 initialized_(false), 70 initialized_(false),
68 restore_old_session_cookies_(restore_old_session_cookies), 71 restore_old_session_cookies_(restore_old_session_cookies),
72 clear_on_exit_policy_(clear_on_exit_policy),
69 num_cookies_read_(0), 73 num_cookies_read_(0),
70 num_priority_waiting_(0), 74 num_priority_waiting_(0),
71 total_priority_requests_(0) { 75 total_priority_requests_(0) {
72 } 76 }
73 77
74 // Creates or loads the SQLite database. 78 // Creates or loads the SQLite database.
75 void Load(const LoadedCallback& loaded_callback); 79 void Load(const LoadedCallback& loaded_callback);
76 80
77 // Loads cookies for the domain key (eTLD+1). 81 // Loads cookies for the domain key (eTLD+1).
78 void LoadCookiesForKey(const std::string& domain, 82 void LoadCookiesForKey(const std::string& domain,
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
174 bool LoadCookiesForDomains(const std::set<std::string>& key); 178 bool LoadCookiesForDomains(const std::set<std::string>& key);
175 179
176 // Batch a cookie operation (add or delete) 180 // Batch a cookie operation (add or delete)
177 void BatchOperation(PendingOperation::OperationType op, 181 void BatchOperation(PendingOperation::OperationType op,
178 const net::CookieMonster::CanonicalCookie& cc); 182 const net::CookieMonster::CanonicalCookie& cc);
179 // Commit our pending operations to the database. 183 // Commit our pending operations to the database.
180 void Commit(); 184 void Commit();
181 // Close() executed on the background thread. 185 // Close() executed on the background thread.
182 void InternalBackgroundClose(); 186 void InternalBackgroundClose();
183 187
184 void DeleteSessionCookies(); 188 void DeleteSessionCookiesOnStartup();
189
190 void DeleteSessionCookiesOnShutdown();
185 191
186 FilePath path_; 192 FilePath path_;
187 scoped_ptr<sql::Connection> db_; 193 scoped_ptr<sql::Connection> db_;
188 sql::MetaTable meta_table_; 194 sql::MetaTable meta_table_;
189 195
190 typedef std::list<PendingOperation*> PendingOperationsList; 196 typedef std::list<PendingOperation*> PendingOperationsList;
191 PendingOperationsList pending_; 197 PendingOperationsList pending_;
192 PendingOperationsList::size_type num_pending_; 198 PendingOperationsList::size_type num_pending_;
193 // True if the persistent store should be deleted upon destruction. 199 // True if the persistent store should be deleted upon destruction.
194 bool clear_local_state_on_exit_; 200 bool clear_local_state_on_exit_;
195 // Guard |cookies_|, |pending_|, |num_pending_|, |clear_local_state_on_exit_| 201 // Guard |cookies_|, |pending_|, |num_pending_|, |clear_local_state_on_exit_|
196 base::Lock lock_; 202 base::Lock lock_;
197 203
198 // Temporary buffer for cookies loaded from DB. Accumulates cookies to reduce 204 // Temporary buffer for cookies loaded from DB. Accumulates cookies to reduce
199 // the number of messages sent to the IO thread. Sent back in response to 205 // the number of messages sent to the IO thread. Sent back in response to
200 // individual load requests for domain keys or when all loading completes. 206 // individual load requests for domain keys or when all loading completes.
201 std::vector<net::CookieMonster::CanonicalCookie*> cookies_; 207 std::vector<net::CookieMonster::CanonicalCookie*> cookies_;
202 208
203 // Map of domain keys(eTLD+1) to domains/hosts that are to be loaded from DB. 209 // Map of domain keys(eTLD+1) to domains/hosts that are to be loaded from DB.
204 std::map<std::string, std::set<std::string> > keys_to_load_; 210 std::map<std::string, std::set<std::string> > keys_to_load_;
205 211
212 // Map of (domain keys(eTLD+1), is secure cookie) to number of cookies in the
213 // database.
214 typedef std::pair<std::string, bool> CookieOrigin;
215 typedef std::map<CookieOrigin, int> CookiesPerOriginMap;
216 CookiesPerOriginMap cookies_per_origin_;
217
206 // Indicates if DB has been initialized. 218 // Indicates if DB has been initialized.
207 bool initialized_; 219 bool initialized_;
208 220
209 // If false, we should filter out session cookies when reading the DB. 221 // If false, we should filter out session cookies when reading the DB.
210 bool restore_old_session_cookies_; 222 bool restore_old_session_cookies_;
211 223
224 // Policy defining what data is deleted on shutdown.
225 scoped_refptr<ClearOnExitPolicy> clear_on_exit_policy_;
226
212 // The cumulative time spent loading the cookies on the DB thread. Incremented 227 // The cumulative time spent loading the cookies on the DB thread. Incremented
213 // and reported from the DB thread. 228 // and reported from the DB thread.
214 base::TimeDelta cookie_load_duration_; 229 base::TimeDelta cookie_load_duration_;
215 230
216 // The total number of cookies read. Incremented and reported on the DB 231 // The total number of cookies read. Incremented and reported on the DB
217 // thread. 232 // thread.
218 int num_cookies_read_; 233 int num_cookies_read_;
219 234
220 // Guards the following metrics-related properties (only accessed when 235 // Guards the following metrics-related properties (only accessed when
221 // starting/completing priority loads or completing the total load). 236 // starting/completing priority loads or completing the total load).
(...skipping 336 matching lines...) Expand 10 before | Expand all | Expand 10 after
558 if (load_success && keys_to_load_.size() > 0) { 573 if (load_success && keys_to_load_.size() > 0) {
559 BrowserThread::PostTask( 574 BrowserThread::PostTask(
560 BrowserThread::DB, FROM_HERE, 575 BrowserThread::DB, FROM_HERE,
561 base::Bind(&Backend::ChainLoadCookies, this, loaded_callback)); 576 base::Bind(&Backend::ChainLoadCookies, this, loaded_callback));
562 } else { 577 } else {
563 BrowserThread::PostTask( 578 BrowserThread::PostTask(
564 BrowserThread::IO, FROM_HERE, 579 BrowserThread::IO, FROM_HERE,
565 base::Bind(&SQLitePersistentCookieStore::Backend::CompleteLoadOnIOThread, 580 base::Bind(&SQLitePersistentCookieStore::Backend::CompleteLoadOnIOThread,
566 this, loaded_callback, load_success)); 581 this, loaded_callback, load_success));
567 if (load_success && !restore_old_session_cookies_) 582 if (load_success && !restore_old_session_cookies_)
568 DeleteSessionCookies(); 583 DeleteSessionCookiesOnStartup();
569 } 584 }
570 } 585 }
571 586
572 bool SQLitePersistentCookieStore::Backend::LoadCookiesForDomains( 587 bool SQLitePersistentCookieStore::Backend::LoadCookiesForDomains(
573 const std::set<std::string>& domains) { 588 const std::set<std::string>& domains) {
574 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 589 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
575 590
576 sql::Statement smt; 591 sql::Statement smt;
577 if (restore_old_session_cookies_) { 592 if (restore_old_session_cookies_) {
578 smt.Assign(db_->GetCachedStatement( 593 smt.Assign(db_->GetCachedStatement(
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
610 std::string(), // TODO(abarth): Persist mac_algorithm 625 std::string(), // TODO(abarth): Persist mac_algorithm
611 Time::FromInternalValue(smt.ColumnInt64(0)), // creation_utc 626 Time::FromInternalValue(smt.ColumnInt64(0)), // creation_utc
612 Time::FromInternalValue(smt.ColumnInt64(5)), // expires_utc 627 Time::FromInternalValue(smt.ColumnInt64(5)), // expires_utc
613 Time::FromInternalValue(smt.ColumnInt64(8)), // last_access_utc 628 Time::FromInternalValue(smt.ColumnInt64(8)), // last_access_utc
614 smt.ColumnInt(6) != 0, // secure 629 smt.ColumnInt(6) != 0, // secure
615 smt.ColumnInt(7) != 0, // httponly 630 smt.ColumnInt(7) != 0, // httponly
616 smt.ColumnInt(9) != 0, // has_expires 631 smt.ColumnInt(9) != 0, // has_expires
617 smt.ColumnInt(10) != 0)); // is_persistent 632 smt.ColumnInt(10) != 0)); // is_persistent
618 DLOG_IF(WARNING, 633 DLOG_IF(WARNING,
619 cc->CreationDate() > Time::Now()) << L"CreationDate too recent"; 634 cc->CreationDate() > Time::Now()) << L"CreationDate too recent";
635 cookies_per_origin_[CookieOrigin(cc->Domain(), cc->IsSecure())]++;
620 cookies.push_back(cc.release()); 636 cookies.push_back(cc.release());
621 ++num_cookies_read_; 637 ++num_cookies_read_;
622 } 638 }
623 smt.Reset(true); 639 smt.Reset(true);
624 } 640 }
625 { 641 {
626 base::AutoLock locked(lock_); 642 base::AutoLock locked(lock_);
627 cookies_.insert(cookies_.end(), cookies.begin(), cookies.end()); 643 cookies_.insert(cookies_.end(), cookies.begin(), cookies.end());
628 } 644 }
629 return true; 645 return true;
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after
819 sql::Transaction transaction(db_.get()); 835 sql::Transaction transaction(db_.get());
820 if (!transaction.Begin()) 836 if (!transaction.Begin())
821 return; 837 return;
822 838
823 for (PendingOperationsList::iterator it = ops.begin(); 839 for (PendingOperationsList::iterator it = ops.begin();
824 it != ops.end(); ++it) { 840 it != ops.end(); ++it) {
825 // Free the cookies as we commit them to the database. 841 // Free the cookies as we commit them to the database.
826 scoped_ptr<PendingOperation> po(*it); 842 scoped_ptr<PendingOperation> po(*it);
827 switch (po->op()) { 843 switch (po->op()) {
828 case PendingOperation::COOKIE_ADD: 844 case PendingOperation::COOKIE_ADD:
845 cookies_per_origin_[
846 CookieOrigin(po->cc().Domain(), po->cc().IsSecure())]++;
829 add_smt.Reset(true); 847 add_smt.Reset(true);
830 add_smt.BindInt64(0, po->cc().CreationDate().ToInternalValue()); 848 add_smt.BindInt64(0, po->cc().CreationDate().ToInternalValue());
831 add_smt.BindString(1, po->cc().Domain()); 849 add_smt.BindString(1, po->cc().Domain());
832 add_smt.BindString(2, po->cc().Name()); 850 add_smt.BindString(2, po->cc().Name());
833 add_smt.BindString(3, po->cc().Value()); 851 add_smt.BindString(3, po->cc().Value());
834 add_smt.BindString(4, po->cc().Path()); 852 add_smt.BindString(4, po->cc().Path());
835 add_smt.BindInt64(5, po->cc().ExpiryDate().ToInternalValue()); 853 add_smt.BindInt64(5, po->cc().ExpiryDate().ToInternalValue());
836 add_smt.BindInt(6, po->cc().IsSecure()); 854 add_smt.BindInt(6, po->cc().IsSecure());
837 add_smt.BindInt(7, po->cc().IsHttpOnly()); 855 add_smt.BindInt(7, po->cc().IsHttpOnly());
838 add_smt.BindInt64(8, po->cc().LastAccessDate().ToInternalValue()); 856 add_smt.BindInt64(8, po->cc().LastAccessDate().ToInternalValue());
839 add_smt.BindInt(9, po->cc().DoesExpire()); 857 add_smt.BindInt(9, po->cc().DoesExpire());
840 add_smt.BindInt(10, po->cc().IsPersistent()); 858 add_smt.BindInt(10, po->cc().IsPersistent());
841 if (!add_smt.Run()) 859 if (!add_smt.Run())
842 NOTREACHED() << "Could not add a cookie to the DB."; 860 NOTREACHED() << "Could not add a cookie to the DB.";
843 break; 861 break;
844 862
845 case PendingOperation::COOKIE_UPDATEACCESS: 863 case PendingOperation::COOKIE_UPDATEACCESS:
846 update_access_smt.Reset(true); 864 update_access_smt.Reset(true);
847 update_access_smt.BindInt64(0, 865 update_access_smt.BindInt64(0,
848 po->cc().LastAccessDate().ToInternalValue()); 866 po->cc().LastAccessDate().ToInternalValue());
849 update_access_smt.BindInt64(1, 867 update_access_smt.BindInt64(1,
850 po->cc().CreationDate().ToInternalValue()); 868 po->cc().CreationDate().ToInternalValue());
851 if (!update_access_smt.Run()) 869 if (!update_access_smt.Run())
852 NOTREACHED() << "Could not update cookie last access time in the DB."; 870 NOTREACHED() << "Could not update cookie last access time in the DB.";
853 break; 871 break;
854 872
855 case PendingOperation::COOKIE_DELETE: 873 case PendingOperation::COOKIE_DELETE:
874 cookies_per_origin_[
875 CookieOrigin(po->cc().Domain(), po->cc().IsSecure())]--;
856 del_smt.Reset(true); 876 del_smt.Reset(true);
857 del_smt.BindInt64(0, po->cc().CreationDate().ToInternalValue()); 877 del_smt.BindInt64(0, po->cc().CreationDate().ToInternalValue());
858 if (!del_smt.Run()) 878 if (!del_smt.Run())
859 NOTREACHED() << "Could not delete a cookie from the DB."; 879 NOTREACHED() << "Could not delete a cookie from the DB.";
860 break; 880 break;
861 881
862 default: 882 default:
863 NOTREACHED(); 883 NOTREACHED();
864 break; 884 break;
865 } 885 }
(...skipping 28 matching lines...) Expand all
894 BrowserThread::DB, FROM_HERE, 914 BrowserThread::DB, FROM_HERE,
895 base::Bind(&Backend::InternalBackgroundClose, this)); 915 base::Bind(&Backend::InternalBackgroundClose, this));
896 } 916 }
897 } 917 }
898 918
899 void SQLitePersistentCookieStore::Backend::InternalBackgroundClose() { 919 void SQLitePersistentCookieStore::Backend::InternalBackgroundClose() {
900 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 920 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
901 // Commit any pending operations 921 // Commit any pending operations
902 Commit(); 922 Commit();
903 923
924 if (!clear_local_state_on_exit_ && clear_on_exit_policy_.get() &&
925 clear_on_exit_policy_->HasClearOnExitOrigins()) {
926 DeleteSessionCookiesOnShutdown();
927 }
928
904 db_.reset(); 929 db_.reset();
905 930
906 if (clear_local_state_on_exit_) 931 if (clear_local_state_on_exit_)
907 file_util::Delete(path_, false); 932 file_util::Delete(path_, false);
908 } 933 }
909 934
935 void SQLitePersistentCookieStore::Backend::DeleteSessionCookiesOnShutdown() {
936 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
937
938 if (!db_.get())
939 return;
940
941 sql::Statement del_smt(db_->GetCachedStatement(
942 SQL_FROM_HERE, "DELETE FROM cookies WHERE host_key=? AND secure=?"));
943 if (!del_smt.is_valid()) {
944 LOG(WARNING) << "Unable to delete cookies on shutdown.";
945 return;
946 }
947
948 sql::Transaction transaction(db_.get());
949 if (!transaction.Begin()) {
950 LOG(WARNING) << "Unable to delete cookies on shutdown.";
951 return;
952 }
953
954 for (CookiesPerOriginMap::iterator it = cookies_per_origin_.begin();
955 it != cookies_per_origin_.end(); ++it) {
956 if (it->second <= 0) {
957 DCHECK_EQ(0, it->second);
958 continue;
959 }
960 if (!clear_on_exit_policy_->ShouldClearOriginOnExit(it->first.first,
961 it->first.second)) {
962 continue;
963 }
964
965 del_smt.Reset(true);
966 del_smt.BindString(0, it->first.first);
967 del_smt.BindInt(1, it->first.second);
968 if (!del_smt.Run())
969 NOTREACHED() << "Could not delete a cookie from the DB.";
970 }
971
972 if (!transaction.Commit())
973 LOG(WARNING) << "Unable to delete cookies on shutdown.";
974 }
975
910 void SQLitePersistentCookieStore::Backend::SetClearLocalStateOnExit( 976 void SQLitePersistentCookieStore::Backend::SetClearLocalStateOnExit(
911 bool clear_local_state) { 977 bool clear_local_state) {
912 base::AutoLock locked(lock_); 978 base::AutoLock locked(lock_);
913 clear_local_state_on_exit_ = clear_local_state; 979 clear_local_state_on_exit_ = clear_local_state;
914 } 980 }
915 981
916 void SQLitePersistentCookieStore::Backend::DeleteSessionCookies() { 982 void SQLitePersistentCookieStore::Backend::DeleteSessionCookiesOnStartup() {
917 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); 983 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
918 if (!db_->Execute("DELETE FROM cookies WHERE persistent == 0")) 984 if (!db_->Execute("DELETE FROM cookies WHERE persistent == 0"))
919 LOG(WARNING) << "Unable to delete session cookies."; 985 LOG(WARNING) << "Unable to delete session cookies.";
920 } 986 }
921 987
922 SQLitePersistentCookieStore::SQLitePersistentCookieStore( 988 SQLitePersistentCookieStore::SQLitePersistentCookieStore(
923 const FilePath& path, 989 const FilePath& path,
924 bool restore_old_session_cookies) 990 bool restore_old_session_cookies,
925 : backend_(new Backend(path, restore_old_session_cookies)) { 991 ClearOnExitPolicy* clear_on_exit_policy)
992 : backend_(
993 new Backend(path, restore_old_session_cookies, clear_on_exit_policy)) {
926 } 994 }
927 995
928 void SQLitePersistentCookieStore::Load(const LoadedCallback& loaded_callback) { 996 void SQLitePersistentCookieStore::Load(const LoadedCallback& loaded_callback) {
929 backend_->Load(loaded_callback); 997 backend_->Load(loaded_callback);
930 } 998 }
931 999
932 void SQLitePersistentCookieStore::LoadCookiesForKey( 1000 void SQLitePersistentCookieStore::LoadCookiesForKey(
933 const std::string& key, 1001 const std::string& key,
934 const LoadedCallback& loaded_callback) { 1002 const LoadedCallback& loaded_callback) {
935 backend_->LoadCookiesForKey(key, loaded_callback); 1003 backend_->LoadCookiesForKey(key, loaded_callback);
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
967 } 1035 }
968 1036
969 SQLitePersistentCookieStore::~SQLitePersistentCookieStore() { 1037 SQLitePersistentCookieStore::~SQLitePersistentCookieStore() {
970 if (backend_.get()) { 1038 if (backend_.get()) {
971 backend_->Close(); 1039 backend_->Close();
972 // Release our reference, it will probably still have a reference if the 1040 // Release our reference, it will probably still have a reference if the
973 // background thread has not run Close() yet. 1041 // background thread has not run Close() yet.
974 backend_ = NULL; 1042 backend_ = NULL;
975 } 1043 }
976 } 1044 }
OLDNEW
« no previous file with comments | « chrome/browser/net/sqlite_persistent_cookie_store.h ('k') | chrome/browser/net/sqlite_persistent_cookie_store_perftest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698