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

Side by Side Diff: content/browser/indexed_db/indexed_db_browsertest.cc

Issue 334303002: Using a mock LevelDBTransaction for corruption tests. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Reduced scope of s_factory, "num" -> "instNum", etc. Created 6 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
« no previous file with comments | « no previous file | content/browser/indexed_db/leveldb/leveldb_transaction.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "base/bind.h" 5 #include "base/bind.h"
6 #include "base/command_line.h" 6 #include "base/command_line.h"
7 #include "base/file_util.h" 7 #include "base/file_util.h"
8 #include "base/files/file.h" 8 #include "base/files/file.h"
9 #include "base/files/file_enumerator.h" 9 #include "base/files/file_enumerator.h"
10 #include "base/files/file_path.h" 10 #include "base/files/file_path.h"
11 #include "base/lazy_instance.h"
11 #include "base/memory/ref_counted.h" 12 #include "base/memory/ref_counted.h"
12 #include "base/message_loop/message_loop.h" 13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/string_split.h"
13 #include "base/strings/utf_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h"
14 #include "base/test/thread_test_helper.h" 16 #include "base/test/thread_test_helper.h"
15 #include "content/browser/browser_main_loop.h" 17 #include "content/browser/browser_main_loop.h"
18 #include "content/browser/indexed_db/indexed_db_class_factory.h"
16 #include "content/browser/indexed_db/indexed_db_context_impl.h" 19 #include "content/browser/indexed_db/indexed_db_context_impl.h"
17 #include "content/browser/web_contents/web_contents_impl.h" 20 #include "content/browser/web_contents/web_contents_impl.h"
18 #include "content/public/browser/browser_context.h" 21 #include "content/public/browser/browser_context.h"
19 #include "content/public/browser/browser_thread.h" 22 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/render_process_host.h" 23 #include "content/public/browser/render_process_host.h"
21 #include "content/public/browser/storage_partition.h" 24 #include "content/public/browser/storage_partition.h"
22 #include "content/public/browser/web_contents.h" 25 #include "content/public/browser/web_contents.h"
23 #include "content/public/common/content_switches.h" 26 #include "content/public/common/content_switches.h"
24 #include "content/public/common/url_constants.h" 27 #include "content/public/common/url_constants.h"
25 #include "content/public/test/browser_test_utils.h" 28 #include "content/public/test/browser_test_utils.h"
26 #include "content/public/test/content_browser_test.h" 29 #include "content/public/test/content_browser_test.h"
27 #include "content/public/test/content_browser_test_utils.h" 30 #include "content/public/test/content_browser_test_utils.h"
28 #include "content/shell/browser/shell.h" 31 #include "content/shell/browser/shell.h"
29 #include "net/base/net_errors.h" 32 #include "net/base/net_errors.h"
30 #include "net/test/embedded_test_server/embedded_test_server.h" 33 #include "net/test/embedded_test_server/embedded_test_server.h"
31 #include "net/test/embedded_test_server/http_request.h" 34 #include "net/test/embedded_test_server/http_request.h"
32 #include "net/test/embedded_test_server/http_response.h" 35 #include "net/test/embedded_test_server/http_response.h"
33 #include "webkit/browser/database/database_util.h" 36 #include "webkit/browser/database/database_util.h"
34 #include "webkit/browser/quota/quota_manager.h" 37 #include "webkit/browser/quota/quota_manager.h"
35 38
36 using base::ASCIIToUTF16; 39 using base::ASCIIToUTF16;
37 using quota::QuotaManager; 40 using quota::QuotaManager;
38 using webkit_database::DatabaseUtil; 41 using webkit_database::DatabaseUtil;
39 42
40 namespace content { 43 namespace content {
41 44
45 enum FailClass {
46 FAIL_CLASS_NOTHING,
47 FAIL_CLASS_LEVELDB_TRANSACTION,
48 };
49
50 enum FailMethod {
51 FAIL_METHOD_NOTHING,
52 FAIL_METHOD_COMMIT,
53 FAIL_METHOD_GET,
54 };
55
56 class FunctionTracer {
57 public:
58 FunctionTracer(const std::string& class_name,
59 const std::string& method_name,
60 int instance_num)
61 : class_name_(class_name),
62 method_name_(method_name),
63 instance_count_(instance_num),
64 current_call_num_(0) {}
65
66 void log_call() {
67 current_call_num_++;
68 VLOG(0) << class_name_ << '[' << instance_count_ << "]::" << method_name_
69 << "()[" << current_call_num_ << ']';
70 }
71
72 private:
73 std::string class_name_;
74 std::string method_name_;
75 int instance_count_;
76 int current_call_num_;
77 };
78
79 class LevelDBTestTansaction : public LevelDBTransaction {
80 public:
81 LevelDBTestTansaction(LevelDBDatabase* db,
82 FailMethod fail_method,
83 int fail_on_call_num)
84 : LevelDBTransaction(db),
85 fail_method_(fail_method),
86 fail_on_call_num_(fail_on_call_num),
87 current_call_num_(0) {
88 DCHECK(fail_method != FAIL_METHOD_NOTHING);
89 DCHECK_GT(fail_on_call_num, 0);
90 }
91
92 virtual leveldb::Status Get(const base::StringPiece& key,
93 std::string* value,
94 bool* found) OVERRIDE {
95 if (fail_method_ != FAIL_METHOD_GET ||
96 ++current_call_num_ != fail_on_call_num_)
97 return LevelDBTransaction::Get(key, value, found);
98
99 *found = false;
100 return leveldb::Status::Corruption("Corrupted for the test");
101 }
102
103 virtual leveldb::Status Commit() OVERRIDE {
104 if (fail_method_ != FAIL_METHOD_COMMIT ||
105 ++current_call_num_ != fail_on_call_num_)
106 return LevelDBTransaction::Commit();
107
108 return leveldb::Status::Corruption("Corrupted for the test");
109 }
110
111 private:
112 virtual ~LevelDBTestTansaction() {}
113
114 FailMethod fail_method_;
115 int fail_on_call_num_;
116 int current_call_num_;
117 };
118
119 class LevelDBTraceTansaction : public LevelDBTransaction {
120 public:
121 LevelDBTraceTansaction(LevelDBDatabase* db, int tx_num)
122 : LevelDBTransaction(db),
123 commit_tracer_(s_class_name, "Commit", tx_num),
124 get_tracer_(s_class_name, "Get", tx_num) {}
125
126 virtual leveldb::Status Get(const base::StringPiece& key,
127 std::string* value,
128 bool* found) OVERRIDE {
129 get_tracer_.log_call();
130 return LevelDBTransaction::Get(key, value, found);
131 }
132
133 virtual leveldb::Status Commit() OVERRIDE {
134 commit_tracer_.log_call();
135 return LevelDBTransaction::Commit();
136 }
137
138 private:
139 virtual ~LevelDBTraceTansaction() {}
140
141 const std::string s_class_name = "LevelDBTransaction";
142
143 FunctionTracer commit_tracer_;
144 FunctionTracer get_tracer_;
145 };
146
147 class IndexedDBBrowserTestClassFactory : public IndexedDBClassFactory {
148 public:
149 IndexedDBBrowserTestClassFactory()
150 : failure_class_(FAIL_CLASS_NOTHING),
151 failure_method_(FAIL_METHOD_NOTHING),
152 only_trace_calls_(false) {}
153 virtual LevelDBTransaction* CreateLevelDBTransaction(
154 LevelDBDatabase* db) OVERRIDE {
155 instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] =
156 instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] + 1;
157 if (only_trace_calls_) {
158 return new LevelDBTraceTansaction(
159 db, instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION]);
160 } else {
161 if (failure_class_ == FAIL_CLASS_LEVELDB_TRANSACTION &&
162 instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] ==
163 fail_on_instance_num_[FAIL_CLASS_LEVELDB_TRANSACTION])
164 return new LevelDBTestTansaction(
165 db,
166 failure_method_,
167 fail_on_call_num_[FAIL_CLASS_LEVELDB_TRANSACTION]);
168 else
169 return IndexedDBClassFactory::CreateLevelDBTransaction(db);
170 }
171 }
172
173 void FailOperation(FailClass failure_class,
174 FailMethod failure_method,
175 int fail_on_instance_num,
176 int fail_on_call_num) {
177 VLOG(0) << "FailOperation: class=" << failure_class
178 << ", method=" << failure_method
179 << ", instanceNum=" << fail_on_instance_num
180 << ", callNum=" << fail_on_call_num;
181 DCHECK(failure_class != FAIL_CLASS_NOTHING);
182 DCHECK(failure_method != FAIL_METHOD_NOTHING);
183 failure_class_ = failure_class;
184 failure_method_ = failure_method;
185 fail_on_instance_num_[failure_class_] = fail_on_instance_num;
186 fail_on_call_num_[failure_class_] = fail_on_call_num;
187 instance_count_.clear();
188 }
189
190 void Reset() {
191 failure_class_ = FAIL_CLASS_NOTHING;
192 failure_method_ = FAIL_METHOD_NOTHING;
193 instance_count_.clear();
194 fail_on_instance_num_.clear();
195 fail_on_call_num_.clear();
196 }
197
198 private:
199 FailClass failure_class_;
200 FailMethod failure_method_;
201 std::map<FailClass, int> instance_count_;
202 std::map<FailClass, int> fail_on_instance_num_;
203 std::map<FailClass, int> fail_on_call_num_;
204 bool only_trace_calls_;
205 };
206
42 // This browser test is aimed towards exercising the IndexedDB bindings and 207 // This browser test is aimed towards exercising the IndexedDB bindings and
43 // the actual implementation that lives in the browser side. 208 // the actual implementation that lives in the browser side.
44 class IndexedDBBrowserTest : public ContentBrowserTest { 209 class IndexedDBBrowserTest : public ContentBrowserTest {
45 public: 210 public:
46 IndexedDBBrowserTest() : disk_usage_(-1) {} 211 IndexedDBBrowserTest() : disk_usage_(-1) {}
47 212
213 virtual void SetUp() OVERRIDE {
214 GetTestClassFactory()->Reset();
215 IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(GetIDBClassFactory);
ericu 2014/06/17 17:03:05 Can you pass in GetTestClassFactory here, or is a
cmumford 2014/06/17 21:53:18 Yes, I gave it a try, but compiler wasn't able to
ericu 2014/06/17 21:55:45 Yeah, let's stay away from that stuff. There be d
216 ContentBrowserTest::SetUp();
217 }
218
219 virtual void TearDown() OVERRIDE {
220 IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(NULL);
221 ContentBrowserTest::TearDown();
222 }
223
224 void FailOperation(FailClass failure_class,
225 FailMethod failure_method,
226 int fail_on_instance_num,
227 int fail_on_call_num) {
228 GetTestClassFactory()->FailOperation(
229 failure_class, failure_method, fail_on_instance_num, fail_on_call_num);
230 }
231
48 void SimpleTest(const GURL& test_url, bool incognito = false) { 232 void SimpleTest(const GURL& test_url, bool incognito = false) {
49 // The test page will perform tests on IndexedDB, then navigate to either 233 // The test page will perform tests on IndexedDB, then navigate to either
50 // a #pass or #fail ref. 234 // a #pass or #fail ref.
51 Shell* the_browser = incognito ? CreateOffTheRecordBrowser() : shell(); 235 Shell* the_browser = incognito ? CreateOffTheRecordBrowser() : shell();
52 236
53 VLOG(0) << "Navigating to URL and blocking."; 237 VLOG(0) << "Navigating to URL and blocking.";
54 NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2); 238 NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2);
55 VLOG(0) << "Navigation done."; 239 VLOG(0) << "Navigation done.";
56 std::string result = 240 std::string result =
57 the_browser->web_contents()->GetLastCommittedURL().ref(); 241 the_browser->web_contents()->GetLastCommittedURL().ref();
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper( 304 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
121 BrowserMainLoop::GetInstance()->indexed_db_thread()-> 305 BrowserMainLoop::GetInstance()->indexed_db_thread()->
122 message_loop_proxy())); 306 message_loop_proxy()));
123 EXPECT_TRUE(helper->Run()); 307 EXPECT_TRUE(helper->Run());
124 // Wait for DidGetDiskUsage to be called. 308 // Wait for DidGetDiskUsage to be called.
125 base::MessageLoop::current()->RunUntilIdle(); 309 base::MessageLoop::current()->RunUntilIdle();
126 return disk_usage_; 310 return disk_usage_;
127 } 311 }
128 312
129 private: 313 private:
314 static IndexedDBBrowserTestClassFactory* GetTestClassFactory() {
315 static ::base::LazyInstance<IndexedDBBrowserTestClassFactory>::Leaky
316 s_factory = LAZY_INSTANCE_INITIALIZER;
317 return s_factory.Pointer();
318 }
319
320 static IndexedDBClassFactory* GetIDBClassFactory() {
321 return GetTestClassFactory();
322 }
323
130 virtual void DidGetDiskUsage(int64 bytes) { 324 virtual void DidGetDiskUsage(int64 bytes) {
131 EXPECT_GT(bytes, 0); 325 EXPECT_GT(bytes, 0);
132 disk_usage_ = bytes; 326 disk_usage_ = bytes;
133 } 327 }
134 328
135 int64 disk_usage_; 329 int64 disk_usage_;
136 330
137 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTest); 331 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTest);
138 }; 332 };
139 333
(...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after
435 << " with " << numErrors << " errors"; 629 << " with " << numErrors << " errors";
436 signal_when_finished->Signal(); 630 signal_when_finished->Signal();
437 } 631 }
438 632
439 const std::string s_corrupt_db_test_prefix = "/corrupt/test/"; 633 const std::string s_corrupt_db_test_prefix = "/corrupt/test/";
440 634
441 static scoped_ptr<net::test_server::HttpResponse> CorruptDBRequestHandler( 635 static scoped_ptr<net::test_server::HttpResponse> CorruptDBRequestHandler(
442 IndexedDBContextImpl* context, 636 IndexedDBContextImpl* context,
443 const GURL& origin_url, 637 const GURL& origin_url,
444 const std::string& path, 638 const std::string& path,
639 IndexedDBBrowserTest* test,
445 const net::test_server::HttpRequest& request) { 640 const net::test_server::HttpRequest& request) {
446
447 std::string request_path; 641 std::string request_path;
448 if (path.find(s_corrupt_db_test_prefix) != std::string::npos) 642 if (path.find(s_corrupt_db_test_prefix) != std::string::npos)
449 request_path = request.relative_url.substr(s_corrupt_db_test_prefix.size()); 643 request_path = request.relative_url.substr(s_corrupt_db_test_prefix.size());
450 else 644 else
451 return scoped_ptr<net::test_server::HttpResponse>(); 645 return scoped_ptr<net::test_server::HttpResponse>();
452 646
453 // Remove the query string if present. 647 // Remove the query string if present.
454 std::string request_query; 648 std::string request_query;
649 std::vector<std::pair<std::string, std::string> > query_params;
455 size_t query_pos = request_path.find('?'); 650 size_t query_pos = request_path.find('?');
456 if (query_pos != std::string::npos) { 651 if (query_pos != std::string::npos) {
457 request_query = request_path.substr(query_pos + 1); 652 request_query = request_path.substr(query_pos + 1);
458 request_path = request_path.substr(0, query_pos); 653 request_path = request_path.substr(0, query_pos);
654 base::SplitStringIntoKeyValuePairs(request_query, '=', '&', &query_params);
459 } 655 }
460 656
461 if (request_path == "corruptdb" && !request_query.empty()) { 657 if (request_path == "corruptdb" && !request_query.empty()) {
462 VLOG(0) << "Requested to corrupt IndexedDB: " << request_query; 658 VLOG(0) << "Requested to corrupt IndexedDB: " << request_query;
463 base::WaitableEvent signal_when_finished(false, false); 659 base::WaitableEvent signal_when_finished(false, false);
464 context->TaskRunner()->PostTask(FROM_HERE, 660 context->TaskRunner()->PostTask(FROM_HERE,
465 base::Bind(&CorruptIndexedDBDatabase, 661 base::Bind(&CorruptIndexedDBDatabase,
466 base::ConstRef(context), 662 base::ConstRef(context),
467 origin_url, 663 origin_url,
468 &signal_when_finished)); 664 &signal_when_finished));
469 signal_when_finished.Wait(); 665 signal_when_finished.Wait();
470 666
471 scoped_ptr<net::test_server::BasicHttpResponse> http_response( 667 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
472 new net::test_server::BasicHttpResponse); 668 new net::test_server::BasicHttpResponse);
473 http_response->set_code(net::HTTP_OK); 669 http_response->set_code(net::HTTP_OK);
474 return http_response.PassAs<net::test_server::HttpResponse>(); 670 return http_response.PassAs<net::test_server::HttpResponse>();
671 } else if (request_path == "fail" && !query_params.empty()) {
672 FailClass failure_class = FAIL_CLASS_NOTHING;
673 FailMethod failure_method = FAIL_METHOD_NOTHING;
674 int instance_num = 1;
675 int call_num = 1;
676 std::string fail_class;
677 std::string fail_method;
678
679 for (std::vector<std::pair<std::string, std::string> >::iterator it =
680 query_params.begin();
681 it != query_params.end();
682 it++) {
683 if (it->first == "method")
684 fail_method = it->second;
685 else if (it->first == "class")
686 fail_class = it->second;
687 else if (it->first == "instNum")
688 instance_num = atoi(it->second.c_str());
689 else if (it->first == "callNum")
690 call_num = atoi(it->second.c_str());
691 else {
692 VLOG(0) << "Unknown param: \"" << it->first << '"';
693 NOTREACHED();
694 }
695 }
696
697 if (fail_class == "LevelDBTransaction") {
698 failure_class = FAIL_CLASS_LEVELDB_TRANSACTION;
699 if (fail_method == "Get")
700 failure_method = FAIL_METHOD_GET;
701 else if (fail_method == "Commit")
702 failure_method = FAIL_METHOD_COMMIT;
703 else {
704 VLOG(0) << "Unknown method: \"" << fail_method << '"';
ericu 2014/06/17 17:03:05 Nit: It's strange to use two different methods to
cmumford 2014/06/17 21:53:18 Done.
705 NOTREACHED();
706 }
707 }
708
709 DCHECK(instance_num >= 1);
710 DCHECK(call_num >= 1);
711
712 test->FailOperation(failure_class, failure_method, instance_num, call_num);
713
714 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
715 new net::test_server::BasicHttpResponse);
716 http_response->set_code(net::HTTP_OK);
717 return http_response.PassAs<net::test_server::HttpResponse>();
475 } 718 }
476 719
477 // A request for a test resource 720 // A request for a test resource
478 base::FilePath resourcePath = 721 base::FilePath resourcePath =
479 content::GetTestFilePath("indexeddb", request_path.c_str()); 722 content::GetTestFilePath("indexeddb", request_path.c_str());
480 scoped_ptr<net::test_server::BasicHttpResponse> http_response( 723 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
481 new net::test_server::BasicHttpResponse); 724 new net::test_server::BasicHttpResponse);
482 http_response->set_code(net::HTTP_OK); 725 http_response->set_code(net::HTTP_OK);
483 std::string file_contents; 726 std::string file_contents;
484 if (!base::ReadFileToString(resourcePath, &file_contents)) 727 if (!base::ReadFileToString(resourcePath, &file_contents))
(...skipping 10 matching lines...) Expand all
495 738
496 IN_PROC_BROWSER_TEST_P(IndexedDBBrowserCorruptionTest, 739 IN_PROC_BROWSER_TEST_P(IndexedDBBrowserCorruptionTest,
497 OperationOnCorruptedOpenDatabase) { 740 OperationOnCorruptedOpenDatabase) {
498 ASSERT_TRUE(embedded_test_server()->Started() || 741 ASSERT_TRUE(embedded_test_server()->Started() ||
499 embedded_test_server()->InitializeAndWaitUntilReady()); 742 embedded_test_server()->InitializeAndWaitUntilReady());
500 const GURL& origin_url = embedded_test_server()->base_url(); 743 const GURL& origin_url = embedded_test_server()->base_url();
501 embedded_test_server()->RegisterRequestHandler( 744 embedded_test_server()->RegisterRequestHandler(
502 base::Bind(&CorruptDBRequestHandler, 745 base::Bind(&CorruptDBRequestHandler,
503 base::ConstRef(GetContext()), 746 base::ConstRef(GetContext()),
504 origin_url, 747 origin_url,
505 s_corrupt_db_test_prefix)); 748 s_corrupt_db_test_prefix,
749 this));
506 750
507 std::string test_file = s_corrupt_db_test_prefix + 751 std::string test_file = s_corrupt_db_test_prefix +
508 "corrupted_open_db_detection.html#" + GetParam(); 752 "corrupted_open_db_detection.html#" + GetParam();
509 SimpleTest(embedded_test_server()->GetURL(test_file)); 753 SimpleTest(embedded_test_server()->GetURL(test_file));
510 754
511 test_file = s_corrupt_db_test_prefix + "corrupted_open_db_recovery.html"; 755 test_file = s_corrupt_db_test_prefix + "corrupted_open_db_recovery.html";
512 SimpleTest(embedded_test_server()->GetURL(test_file)); 756 SimpleTest(embedded_test_server()->GetURL(test_file));
513 } 757 }
514 758
515 INSTANTIATE_TEST_CASE_P(IndexedDBBrowserCorruptionTestInstantiation, 759 INSTANTIATE_TEST_CASE_P(IndexedDBBrowserCorruptionTestInstantiation,
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
629 #define MAYBE_RenderThreadShutdownTest DISABLED_RenderThreadShutdownTest 873 #define MAYBE_RenderThreadShutdownTest DISABLED_RenderThreadShutdownTest
630 #else 874 #else
631 #define MAYBE_RenderThreadShutdownTest RenderThreadShutdownTest 875 #define MAYBE_RenderThreadShutdownTest RenderThreadShutdownTest
632 #endif 876 #endif
633 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestSingleProcess, 877 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestSingleProcess,
634 MAYBE_RenderThreadShutdownTest) { 878 MAYBE_RenderThreadShutdownTest) {
635 SimpleTest(GetTestUrl("indexeddb", "shutdown_with_requests.html")); 879 SimpleTest(GetTestUrl("indexeddb", "shutdown_with_requests.html"));
636 } 880 }
637 881
638 } // namespace content 882 } // namespace content
OLDNEW
« no previous file with comments | « no previous file | content/browser/indexed_db/leveldb/leveldb_transaction.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698