OLD | NEW |
| (Empty) |
1 // Copyright 2016 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 "components/offline_pages/background/request_queue_store_sql.h" | |
6 | |
7 #include <unordered_set> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/files/file_path.h" | |
11 #include "base/files/file_util.h" | |
12 #include "base/location.h" | |
13 #include "base/logging.h" | |
14 #include "base/sequenced_task_runner.h" | |
15 #include "base/threading/thread_task_runner_handle.h" | |
16 #include "components/offline_pages/background/save_page_request.h" | |
17 #include "sql/connection.h" | |
18 #include "sql/statement.h" | |
19 #include "sql/transaction.h" | |
20 | |
21 namespace offline_pages { | |
22 | |
23 template class StoreUpdateResult<SavePageRequest>; | |
24 | |
25 namespace { | |
26 | |
27 using StoreStateCallback = base::Callback<void(StoreState)>; | |
28 | |
29 // This is a macro instead of a const so that | |
30 // it can be used inline in other SQL statements below. | |
31 #define REQUEST_QUEUE_TABLE_NAME "request_queue_v1" | |
32 const bool kUserRequested = true; | |
33 | |
34 bool CreateRequestQueueTable(sql::Connection* db) { | |
35 const char kSql[] = "CREATE TABLE IF NOT EXISTS " REQUEST_QUEUE_TABLE_NAME | |
36 " (request_id INTEGER PRIMARY KEY NOT NULL," | |
37 " creation_time INTEGER NOT NULL," | |
38 " activation_time INTEGER NOT NULL DEFAULT 0," | |
39 " last_attempt_time INTEGER NOT NULL DEFAULT 0," | |
40 " started_attempt_count INTEGER NOT NULL," | |
41 " completed_attempt_count INTEGER NOT NULL," | |
42 " state INTEGER NOT NULL DEFAULT 0," | |
43 " url VARCHAR NOT NULL," | |
44 " client_namespace VARCHAR NOT NULL," | |
45 " client_id VARCHAR NOT NULL" | |
46 ")"; | |
47 return db->Execute(kSql); | |
48 } | |
49 | |
50 bool CreateSchema(sql::Connection* db) { | |
51 // If there is not already a state column, we need to drop the old table. We | |
52 // are choosing to drop instead of upgrade since the feature is not yet | |
53 // released, so we don't use a transaction to protect existing data, or try to | |
54 // migrate it. | |
55 if (!db->DoesColumnExist(REQUEST_QUEUE_TABLE_NAME, "state")) { | |
56 if (!db->Execute("DROP TABLE IF EXISTS " REQUEST_QUEUE_TABLE_NAME)) | |
57 return false; | |
58 } | |
59 | |
60 if (!CreateRequestQueueTable(db)) | |
61 return false; | |
62 | |
63 // TODO(fgorski): Add indices here. | |
64 return true; | |
65 } | |
66 | |
67 // Create a save page request from a SQL result. Expects complete rows with | |
68 // all columns present. Columns are in order they are defined in select query | |
69 // in |GetOneRequest| method. | |
70 std::unique_ptr<SavePageRequest> MakeSavePageRequest( | |
71 const sql::Statement& statement) { | |
72 const int64_t id = statement.ColumnInt64(0); | |
73 const base::Time creation_time = | |
74 base::Time::FromInternalValue(statement.ColumnInt64(1)); | |
75 const base::Time activation_time = | |
76 base::Time::FromInternalValue(statement.ColumnInt64(2)); | |
77 const base::Time last_attempt_time = | |
78 base::Time::FromInternalValue(statement.ColumnInt64(3)); | |
79 const int64_t started_attempt_count = statement.ColumnInt64(4); | |
80 const int64_t completed_attempt_count = statement.ColumnInt64(5); | |
81 const SavePageRequest::RequestState state = | |
82 static_cast<SavePageRequest::RequestState>(statement.ColumnInt64(6)); | |
83 const GURL url(statement.ColumnString(7)); | |
84 const ClientId client_id(statement.ColumnString(8), | |
85 statement.ColumnString(9)); | |
86 | |
87 DVLOG(2) << "making save page request - id " << id << " url " << url | |
88 << " client_id " << client_id.name_space << "-" << client_id.id | |
89 << " creation time " << creation_time << " user requested " | |
90 << kUserRequested; | |
91 | |
92 std::unique_ptr<SavePageRequest> request(new SavePageRequest( | |
93 id, url, client_id, creation_time, activation_time, kUserRequested)); | |
94 request->set_last_attempt_time(last_attempt_time); | |
95 request->set_started_attempt_count(started_attempt_count); | |
96 request->set_completed_attempt_count(completed_attempt_count); | |
97 request->set_request_state(state); | |
98 return request; | |
99 } | |
100 | |
101 // Get a request for a specific id. | |
102 std::unique_ptr<SavePageRequest> GetOneRequest(sql::Connection* db, | |
103 const int64_t request_id) { | |
104 const char kSql[] = | |
105 "SELECT request_id, creation_time, activation_time," | |
106 " last_attempt_time, started_attempt_count, completed_attempt_count," | |
107 " state, url, client_namespace, client_id" | |
108 " FROM " REQUEST_QUEUE_TABLE_NAME " WHERE request_id=?"; | |
109 | |
110 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
111 statement.BindInt64(0, request_id); | |
112 | |
113 if (statement.Step()) | |
114 return MakeSavePageRequest(statement); | |
115 return std::unique_ptr<SavePageRequest>(nullptr); | |
116 } | |
117 | |
118 ItemActionStatus DeleteRequestById(sql::Connection* db, int64_t request_id) { | |
119 const char kSql[] = | |
120 "DELETE FROM " REQUEST_QUEUE_TABLE_NAME " WHERE request_id=?"; | |
121 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
122 statement.BindInt64(0, request_id); | |
123 if (!statement.Run()) | |
124 return ItemActionStatus::STORE_ERROR; | |
125 else if (db->GetLastChangeCount() == 0) | |
126 return ItemActionStatus::NOT_FOUND; | |
127 return ItemActionStatus::SUCCESS; | |
128 } | |
129 | |
130 ItemActionStatus Insert(sql::Connection* db, const SavePageRequest& request) { | |
131 const char kSql[] = | |
132 "INSERT OR IGNORE INTO " REQUEST_QUEUE_TABLE_NAME | |
133 " (request_id, creation_time, activation_time," | |
134 " last_attempt_time, started_attempt_count, completed_attempt_count," | |
135 " state, url, client_namespace, client_id)" | |
136 " VALUES " | |
137 " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; | |
138 | |
139 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
140 statement.BindInt64(0, request.request_id()); | |
141 statement.BindInt64(1, request.creation_time().ToInternalValue()); | |
142 statement.BindInt64(2, request.activation_time().ToInternalValue()); | |
143 statement.BindInt64(3, request.last_attempt_time().ToInternalValue()); | |
144 statement.BindInt64(4, request.started_attempt_count()); | |
145 statement.BindInt64(5, request.completed_attempt_count()); | |
146 statement.BindInt64(6, static_cast<int64_t>(request.request_state())); | |
147 statement.BindString(7, request.url().spec()); | |
148 statement.BindString(8, request.client_id().name_space); | |
149 statement.BindString(9, request.client_id().id); | |
150 | |
151 if (!statement.Run()) | |
152 return ItemActionStatus::STORE_ERROR; | |
153 if (db->GetLastChangeCount() == 0) | |
154 return ItemActionStatus::ALREADY_EXISTS; | |
155 return ItemActionStatus::SUCCESS; | |
156 } | |
157 | |
158 ItemActionStatus Update(sql::Connection* db, const SavePageRequest& request) { | |
159 const char kSql[] = | |
160 "UPDATE OR IGNORE " REQUEST_QUEUE_TABLE_NAME | |
161 " SET creation_time = ?, activation_time = ?, last_attempt_time = ?," | |
162 " started_attempt_count = ?, completed_attempt_count = ?, state = ?," | |
163 " url = ?, client_namespace = ?, client_id = ?" | |
164 " WHERE request_id = ?"; | |
165 | |
166 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
167 statement.BindInt64(0, request.creation_time().ToInternalValue()); | |
168 statement.BindInt64(1, request.activation_time().ToInternalValue()); | |
169 statement.BindInt64(2, request.last_attempt_time().ToInternalValue()); | |
170 statement.BindInt64(3, request.started_attempt_count()); | |
171 statement.BindInt64(4, request.completed_attempt_count()); | |
172 statement.BindInt64(5, static_cast<int64_t>(request.request_state())); | |
173 statement.BindString(6, request.url().spec()); | |
174 statement.BindString(7, request.client_id().name_space); | |
175 statement.BindString(8, request.client_id().id); | |
176 statement.BindInt64(9, request.request_id()); | |
177 | |
178 if (!statement.Run()) | |
179 return ItemActionStatus::STORE_ERROR; | |
180 if (db->GetLastChangeCount() == 0) | |
181 return ItemActionStatus::NOT_FOUND; | |
182 return ItemActionStatus::SUCCESS; | |
183 } | |
184 | |
185 void PostStoreUpdateResultForIds( | |
186 scoped_refptr<base::SingleThreadTaskRunner> runner, | |
187 StoreState store_state, | |
188 const std::vector<int64_t>& item_ids, | |
189 ItemActionStatus action_status, | |
190 const RequestQueueStore::UpdateCallback& callback) { | |
191 std::unique_ptr<UpdateRequestsResult> result( | |
192 new UpdateRequestsResult(store_state)); | |
193 for (const auto& item_id : item_ids) | |
194 result->item_statuses.push_back(std::make_pair(item_id, action_status)); | |
195 runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result))); | |
196 } | |
197 | |
198 void PostStoreErrorForAllRequests( | |
199 scoped_refptr<base::SingleThreadTaskRunner> runner, | |
200 const std::vector<SavePageRequest>& items, | |
201 const RequestQueueStore::UpdateCallback& callback) { | |
202 std::vector<int64_t> item_ids; | |
203 for (const auto& item : items) | |
204 item_ids.push_back(item.request_id()); | |
205 PostStoreUpdateResultForIds(runner, StoreState::LOADED, item_ids, | |
206 ItemActionStatus::STORE_ERROR, callback); | |
207 } | |
208 | |
209 void PostStoreErrorForAllIds( | |
210 scoped_refptr<base::SingleThreadTaskRunner> runner, | |
211 const std::vector<int64_t>& item_ids, | |
212 const RequestQueueStore::UpdateCallback& callback) { | |
213 PostStoreUpdateResultForIds(runner, StoreState::LOADED, item_ids, | |
214 ItemActionStatus::STORE_ERROR, callback); | |
215 } | |
216 | |
217 bool InitDatabase(sql::Connection* db, const base::FilePath& path) { | |
218 db->set_page_size(4096); | |
219 db->set_cache_size(500); | |
220 db->set_histogram_tag("BackgroundRequestQueue"); | |
221 db->set_exclusive_locking(); | |
222 | |
223 base::File::Error err; | |
224 if (!base::CreateDirectoryAndGetError(path.DirName(), &err)) | |
225 return false; | |
226 if (!db->Open(path)) | |
227 return false; | |
228 db->Preload(); | |
229 | |
230 return CreateSchema(db); | |
231 } | |
232 | |
233 void GetRequestsSync(sql::Connection* db, | |
234 scoped_refptr<base::SingleThreadTaskRunner> runner, | |
235 const RequestQueueStore::GetRequestsCallback& callback) { | |
236 const char kSql[] = | |
237 "SELECT request_id, creation_time, activation_time," | |
238 " last_attempt_time, started_attempt_count, completed_attempt_count," | |
239 " state, url, client_namespace, client_id" | |
240 " FROM " REQUEST_QUEUE_TABLE_NAME; | |
241 | |
242 sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql)); | |
243 | |
244 std::vector<std::unique_ptr<SavePageRequest>> requests; | |
245 while (statement.Step()) | |
246 requests.push_back(MakeSavePageRequest(statement)); | |
247 | |
248 runner->PostTask(FROM_HERE, base::Bind(callback, statement.Succeeded(), | |
249 base::Passed(&requests))); | |
250 } | |
251 | |
252 void GetRequestsByIdsSync(sql::Connection* db, | |
253 scoped_refptr<base::SingleThreadTaskRunner> runner, | |
254 const std::vector<int64_t>& request_ids, | |
255 const RequestQueueStore::UpdateCallback& callback) { | |
256 // TODO(fgorski): Perhaps add metrics here. | |
257 std::unique_ptr<UpdateRequestsResult> result( | |
258 new UpdateRequestsResult(StoreState::LOADED)); | |
259 | |
260 // If you create a transaction but don't Commit() it is automatically | |
261 // rolled back by its destructor when it falls out of scope. | |
262 sql::Transaction transaction(db); | |
263 if (!transaction.Begin()) { | |
264 PostStoreErrorForAllIds(runner, request_ids, callback); | |
265 return; | |
266 } | |
267 | |
268 // Make sure not to include the same request multiple times, preserving the | |
269 // order of non-duplicated IDs in the result. | |
270 std::unordered_set<int64_t> processed_ids; | |
271 for (int64_t request_id : request_ids) { | |
272 if (!processed_ids.insert(request_id).second) | |
273 continue; | |
274 std::unique_ptr<SavePageRequest> request = GetOneRequest(db, request_id); | |
275 if (request.get()) | |
276 result->updated_items.push_back(*request); | |
277 ItemActionStatus status = | |
278 request.get() ? ItemActionStatus::SUCCESS : ItemActionStatus::NOT_FOUND; | |
279 result->item_statuses.push_back(std::make_pair(request_id, status)); | |
280 } | |
281 | |
282 if (!transaction.Commit()) { | |
283 PostStoreErrorForAllIds(runner, request_ids, callback); | |
284 return; | |
285 } | |
286 | |
287 runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result))); | |
288 } | |
289 | |
290 void AddRequestSync(sql::Connection* db, | |
291 scoped_refptr<base::SingleThreadTaskRunner> runner, | |
292 const SavePageRequest& request, | |
293 const RequestQueueStore::AddCallback& callback) { | |
294 // TODO(fgorski): add UMA metrics here. | |
295 ItemActionStatus status = Insert(db, request); | |
296 runner->PostTask(FROM_HERE, base::Bind(callback, status)); | |
297 } | |
298 | |
299 void UpdateRequestsSync(sql::Connection* db, | |
300 scoped_refptr<base::SingleThreadTaskRunner> runner, | |
301 const std::vector<SavePageRequest>& requests, | |
302 const RequestQueueStore::UpdateCallback& callback) { | |
303 // TODO(fgorski): add UMA metrics here. | |
304 std::unique_ptr<UpdateRequestsResult> result( | |
305 new UpdateRequestsResult(StoreState::LOADED)); | |
306 | |
307 sql::Transaction transaction(db); | |
308 if (!transaction.Begin()) { | |
309 PostStoreErrorForAllRequests(runner, requests, callback); | |
310 return; | |
311 } | |
312 | |
313 for (const auto& request : requests) { | |
314 ItemActionStatus status = Update(db, request); | |
315 result->item_statuses.push_back( | |
316 std::make_pair(request.request_id(), status)); | |
317 if (status == ItemActionStatus::SUCCESS) | |
318 result->updated_items.push_back(request); | |
319 } | |
320 | |
321 if (!transaction.Commit()) { | |
322 PostStoreErrorForAllRequests(runner, requests, callback); | |
323 return; | |
324 } | |
325 | |
326 runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result))); | |
327 } | |
328 | |
329 void RemoveRequestsSync(sql::Connection* db, | |
330 scoped_refptr<base::SingleThreadTaskRunner> runner, | |
331 const std::vector<int64_t>& request_ids, | |
332 const RequestQueueStore::UpdateCallback& callback) { | |
333 // TODO(fgorski): Perhaps add metrics here. | |
334 std::unique_ptr<UpdateRequestsResult> result( | |
335 new UpdateRequestsResult(StoreState::LOADED)); | |
336 | |
337 // If you create a transaction but don't Commit() it is automatically | |
338 // rolled back by its destructor when it falls out of scope. | |
339 sql::Transaction transaction(db); | |
340 if (!transaction.Begin()) { | |
341 PostStoreErrorForAllIds(runner, request_ids, callback); | |
342 return; | |
343 } | |
344 | |
345 // Read the request before we delete it, and if the delete worked, put it on | |
346 // the queue of requests that got deleted. | |
347 for (int64_t request_id : request_ids) { | |
348 std::unique_ptr<SavePageRequest> request = GetOneRequest(db, request_id); | |
349 ItemActionStatus status = DeleteRequestById(db, request_id); | |
350 result->item_statuses.push_back(std::make_pair(request_id, status)); | |
351 if (status == ItemActionStatus::SUCCESS) | |
352 result->updated_items.push_back(*request); | |
353 } | |
354 | |
355 if (!transaction.Commit()) { | |
356 PostStoreErrorForAllIds(runner, request_ids, callback); | |
357 return; | |
358 } | |
359 | |
360 runner->PostTask(FROM_HERE, base::Bind(callback, base::Passed(&result))); | |
361 } | |
362 | |
363 void OpenConnectionSync(sql::Connection* db, | |
364 scoped_refptr<base::SingleThreadTaskRunner> runner, | |
365 const base::FilePath& path, | |
366 const StoreStateCallback& callback) { | |
367 StoreState state = | |
368 InitDatabase(db, path) ? StoreState::LOADED : StoreState::FAILED_LOADING; | |
369 runner->PostTask(FROM_HERE, base::Bind(callback, state)); | |
370 } | |
371 | |
372 void ResetSync(sql::Connection* db, | |
373 const base::FilePath& db_file_path, | |
374 scoped_refptr<base::SingleThreadTaskRunner> runner, | |
375 const StoreStateCallback& callback) { | |
376 // This method deletes the content of the whole store and reinitializes it. | |
377 bool success = db->Raze(); | |
378 db->Close(); | |
379 StoreState state; | |
380 if (!success) | |
381 state = StoreState::FAILED_RESET; | |
382 if (InitDatabase(db, db_file_path)) | |
383 state = StoreState::LOADED; | |
384 else | |
385 state = StoreState::FAILED_LOADING; | |
386 | |
387 runner->PostTask(FROM_HERE, base::Bind(callback, state)); | |
388 } | |
389 | |
390 } // anonymous namespace | |
391 | |
392 RequestQueueStoreSQL::RequestQueueStoreSQL( | |
393 scoped_refptr<base::SequencedTaskRunner> background_task_runner, | |
394 const base::FilePath& path) | |
395 : background_task_runner_(std::move(background_task_runner)), | |
396 db_file_path_(path.AppendASCII("RequestQueue.db")), | |
397 weak_ptr_factory_(this) { | |
398 OpenConnection(); | |
399 } | |
400 | |
401 RequestQueueStoreSQL::~RequestQueueStoreSQL() { | |
402 if (db_.get()) | |
403 background_task_runner_->DeleteSoon(FROM_HERE, db_.release()); | |
404 } | |
405 | |
406 bool RequestQueueStoreSQL::CheckDb(const base::Closure& callback) { | |
407 DCHECK(db_.get()); | |
408 if (!db_.get()) { | |
409 // Nothing to do, but post a callback instead of calling directly | |
410 // to preserve the async style behavior to prevent bugs. | |
411 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback); | |
412 return false; | |
413 } | |
414 return true; | |
415 } | |
416 | |
417 void RequestQueueStoreSQL::GetRequests(const GetRequestsCallback& callback) { | |
418 DCHECK(db_.get()); | |
419 std::vector<std::unique_ptr<SavePageRequest>> requests; | |
420 if (!CheckDb(base::Bind(callback, false, base::Passed(&requests)))) | |
421 return; | |
422 | |
423 background_task_runner_->PostTask( | |
424 FROM_HERE, base::Bind(&GetRequestsSync, db_.get(), | |
425 base::ThreadTaskRunnerHandle::Get(), callback)); | |
426 } | |
427 | |
428 void RequestQueueStoreSQL::GetRequestsByIds( | |
429 const std::vector<int64_t>& request_ids, | |
430 const UpdateCallback& callback) { | |
431 if (!db_.get()) { | |
432 PostStoreErrorForAllIds(base::ThreadTaskRunnerHandle::Get(), request_ids, | |
433 callback); | |
434 return; | |
435 } | |
436 | |
437 background_task_runner_->PostTask( | |
438 FROM_HERE, | |
439 base::Bind(&GetRequestsByIdsSync, db_.get(), | |
440 base::ThreadTaskRunnerHandle::Get(), request_ids, callback)); | |
441 } | |
442 | |
443 void RequestQueueStoreSQL::AddRequest(const SavePageRequest& request, | |
444 const AddCallback& callback) { | |
445 if (!CheckDb(base::Bind(callback, ItemActionStatus::STORE_ERROR))) | |
446 return; | |
447 | |
448 background_task_runner_->PostTask( | |
449 FROM_HERE, | |
450 base::Bind(&AddRequestSync, db_.get(), | |
451 base::ThreadTaskRunnerHandle::Get(), request, callback)); | |
452 } | |
453 | |
454 void RequestQueueStoreSQL::UpdateRequests( | |
455 const std::vector<SavePageRequest>& requests, | |
456 const UpdateCallback& callback) { | |
457 if (!db_.get()) { | |
458 PostStoreErrorForAllRequests(base::ThreadTaskRunnerHandle::Get(), requests, | |
459 callback); | |
460 return; | |
461 } | |
462 | |
463 background_task_runner_->PostTask( | |
464 FROM_HERE, | |
465 base::Bind(&UpdateRequestsSync, db_.get(), | |
466 base::ThreadTaskRunnerHandle::Get(), requests, callback)); | |
467 } | |
468 | |
469 void RequestQueueStoreSQL::RemoveRequests( | |
470 const std::vector<int64_t>& request_ids, | |
471 const UpdateCallback& callback) { | |
472 if (!db_.get()) { | |
473 PostStoreErrorForAllIds(base::ThreadTaskRunnerHandle::Get(), request_ids, | |
474 callback); | |
475 return; | |
476 } | |
477 | |
478 background_task_runner_->PostTask( | |
479 FROM_HERE, | |
480 base::Bind(&RemoveRequestsSync, db_.get(), | |
481 base::ThreadTaskRunnerHandle::Get(), request_ids, callback)); | |
482 } | |
483 | |
484 void RequestQueueStoreSQL::Reset(const ResetCallback& callback) { | |
485 DCHECK(db_.get()); | |
486 if (!CheckDb(base::Bind(callback, false))) | |
487 return; | |
488 | |
489 background_task_runner_->PostTask( | |
490 FROM_HERE, | |
491 base::Bind(&ResetSync, db_.get(), db_file_path_, | |
492 base::ThreadTaskRunnerHandle::Get(), | |
493 base::Bind(&RequestQueueStoreSQL::OnResetDone, | |
494 weak_ptr_factory_.GetWeakPtr(), callback))); | |
495 } | |
496 | |
497 void RequestQueueStoreSQL::OpenConnection() { | |
498 DCHECK(!db_); | |
499 db_.reset(new sql::Connection()); | |
500 background_task_runner_->PostTask( | |
501 FROM_HERE, | |
502 base::Bind(&OpenConnectionSync, db_.get(), | |
503 base::ThreadTaskRunnerHandle::Get(), db_file_path_, | |
504 base::Bind(&RequestQueueStoreSQL::OnOpenConnectionDone, | |
505 weak_ptr_factory_.GetWeakPtr()))); | |
506 } | |
507 | |
508 void RequestQueueStoreSQL::OnOpenConnectionDone(StoreState state) { | |
509 DCHECK(db_.get()); | |
510 | |
511 state_ = state; | |
512 | |
513 // Unfortunately we were not able to open DB connection. | |
514 if (state_ != StoreState::LOADED) | |
515 db_.reset(); | |
516 } | |
517 | |
518 void RequestQueueStoreSQL::OnResetDone(const ResetCallback& callback, | |
519 StoreState state) { | |
520 // Complete connection initialization post reset. | |
521 OnOpenConnectionDone(state); | |
522 callback.Run(state == StoreState::LOADED); | |
523 } | |
524 | |
525 StoreState RequestQueueStoreSQL::state() const { | |
526 return state_; | |
527 } | |
528 | |
529 } // namespace offline_pages | |
OLD | NEW |