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/offline_page_model_impl.h" | |
6 | |
7 #include <algorithm> | |
8 #include <limits> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/files/file_util.h" | |
12 #include "base/location.h" | |
13 #include "base/logging.h" | |
14 #include "base/metrics/histogram_macros.h" | |
15 #include "base/rand_util.h" | |
16 #include "base/sequenced_task_runner.h" | |
17 #include "base/strings/string16.h" | |
18 #include "base/strings/string_number_conversions.h" | |
19 #include "base/threading/thread_task_runner_handle.h" | |
20 #include "base/time/clock.h" | |
21 #include "base/time/time.h" | |
22 #include "components/offline_pages/archive_manager.h" | |
23 #include "components/offline_pages/client_namespace_constants.h" | |
24 #include "components/offline_pages/client_policy_controller.h" | |
25 #include "components/offline_pages/offline_page_item.h" | |
26 #include "components/offline_pages/offline_page_model_query.h" | |
27 #include "components/offline_pages/offline_page_storage_manager.h" | |
28 #include "url/gurl.h" | |
29 | |
30 using ArchiverResult = offline_pages::OfflinePageArchiver::ArchiverResult; | |
31 using ClearStorageCallback = | |
32 offline_pages::OfflinePageStorageManager::ClearStorageCallback; | |
33 using ClearStorageResult = | |
34 offline_pages::OfflinePageStorageManager::ClearStorageResult; | |
35 | |
36 namespace offline_pages { | |
37 | |
38 namespace { | |
39 | |
40 // The delay used to schedule the first clear storage request for storage | |
41 // manager after the model is loaded. | |
42 const base::TimeDelta kStorageManagerStartingDelay = | |
43 base::TimeDelta::FromSeconds(20); | |
44 | |
45 int64_t GenerateOfflineId() { | |
46 return base::RandGenerator(std::numeric_limits<int64_t>::max()) + 1; | |
47 } | |
48 | |
49 // The maximum histogram size for the metrics that measure time between views of | |
50 // a given page. | |
51 const base::TimeDelta kMaxOpenedPageHistogramBucket = | |
52 base::TimeDelta::FromDays(90); | |
53 | |
54 SavePageResult ToSavePageResult(ArchiverResult archiver_result) { | |
55 SavePageResult result; | |
56 switch (archiver_result) { | |
57 case ArchiverResult::SUCCESSFULLY_CREATED: | |
58 result = SavePageResult::SUCCESS; | |
59 break; | |
60 case ArchiverResult::ERROR_DEVICE_FULL: | |
61 result = SavePageResult::DEVICE_FULL; | |
62 break; | |
63 case ArchiverResult::ERROR_CONTENT_UNAVAILABLE: | |
64 result = SavePageResult::CONTENT_UNAVAILABLE; | |
65 break; | |
66 case ArchiverResult::ERROR_ARCHIVE_CREATION_FAILED: | |
67 result = SavePageResult::ARCHIVE_CREATION_FAILED; | |
68 break; | |
69 case ArchiverResult::ERROR_CANCELED: | |
70 result = SavePageResult::CANCELLED; | |
71 break; | |
72 case ArchiverResult::ERROR_SECURITY_CERTIFICATE: | |
73 result = SavePageResult::SECURITY_CERTIFICATE_ERROR; | |
74 break; | |
75 default: | |
76 NOTREACHED(); | |
77 result = SavePageResult::CONTENT_UNAVAILABLE; | |
78 } | |
79 return result; | |
80 } | |
81 | |
82 std::string AddHistogramSuffix(const ClientId& client_id, | |
83 const char* histogram_name) { | |
84 if (client_id.name_space.empty()) { | |
85 NOTREACHED(); | |
86 return histogram_name; | |
87 } | |
88 std::string adjusted_histogram_name(histogram_name); | |
89 adjusted_histogram_name += "."; | |
90 adjusted_histogram_name += client_id.name_space; | |
91 return adjusted_histogram_name; | |
92 } | |
93 | |
94 void ReportStorageHistogramsAfterSave( | |
95 const ArchiveManager::StorageStats& storage_stats) { | |
96 const int kMB = 1024 * 1024; | |
97 int free_disk_space_mb = | |
98 static_cast<int>(storage_stats.free_disk_space / kMB); | |
99 UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.SavePage.FreeSpaceMB", | |
100 free_disk_space_mb, 1, 500000, 50); | |
101 | |
102 int total_page_size_mb = | |
103 static_cast<int>(storage_stats.total_archives_size / kMB); | |
104 UMA_HISTOGRAM_COUNTS_10000("OfflinePages.TotalPageSize", total_page_size_mb); | |
105 } | |
106 | |
107 void ReportStorageHistogramsAfterDelete( | |
108 const ArchiveManager::StorageStats& storage_stats) { | |
109 const int kMB = 1024 * 1024; | |
110 int free_disk_space_mb = | |
111 static_cast<int>(storage_stats.free_disk_space / kMB); | |
112 UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.DeletePage.FreeSpaceMB", | |
113 free_disk_space_mb, 1, 500000, 50); | |
114 | |
115 int total_page_size_mb = | |
116 static_cast<int>(storage_stats.total_archives_size / kMB); | |
117 UMA_HISTOGRAM_COUNTS_10000("OfflinePages.TotalPageSize", total_page_size_mb); | |
118 | |
119 if (storage_stats.free_disk_space > 0) { | |
120 int percentage_of_free = static_cast<int>( | |
121 1.0 * storage_stats.total_archives_size / | |
122 (storage_stats.total_archives_size + storage_stats.free_disk_space) * | |
123 100); | |
124 UMA_HISTOGRAM_PERCENTAGE( | |
125 "OfflinePages.DeletePage.TotalPageSizeAsPercentageOfFreeSpace", | |
126 percentage_of_free); | |
127 } | |
128 } | |
129 | |
130 void ReportSavePageResultHistogramAfterSave(const ClientId& client_id, | |
131 SavePageResult result) { | |
132 // The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION | |
133 // macro adapted to allow for a dynamically suffixed histogram name. | |
134 // Note: The factory creates and owns the histogram. | |
135 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet( | |
136 AddHistogramSuffix(client_id, "OfflinePages.SavePageResult"), | |
137 1, | |
138 static_cast<int>(SavePageResult::RESULT_COUNT), | |
139 static_cast<int>(SavePageResult::RESULT_COUNT) + 1, | |
140 base::HistogramBase::kUmaTargetedHistogramFlag); | |
141 histogram->Add(static_cast<int>(result)); | |
142 } | |
143 | |
144 // Goes through the list of offline pages, compiling the following two metrics: | |
145 // - a count of the pages with the same URL | |
146 // - The difference between the |created_before| time and the creation time of | |
147 // the page with the closest creation time before |created_before|. | |
148 // Returns true if there was a page that was saved before |created_before| with | |
149 // a matching URL. | |
150 bool GetMatchingURLCountAndMostRecentCreationTime( | |
151 const std::map<int64_t, OfflinePageItem>& offline_pages, | |
152 std::string name_space, | |
153 const GURL& url, | |
154 base::Time created_before, | |
155 int* matching_url_count, | |
156 base::TimeDelta* most_recent_creation_time) { | |
157 int count = 0; | |
158 | |
159 // Create a time that is very old, so that any valid time will be newer than | |
160 // it. | |
161 base::Time latest_time; | |
162 bool matching_page = false; | |
163 | |
164 for (auto& id_page_pair : offline_pages) { | |
165 if (id_page_pair.second.client_id.name_space == name_space && | |
166 url == id_page_pair.second.url) { | |
167 count++; | |
168 base::Time page_creation_time = id_page_pair.second.creation_time; | |
169 if (page_creation_time < created_before && | |
170 page_creation_time > latest_time) { | |
171 latest_time = page_creation_time; | |
172 matching_page = true; | |
173 } | |
174 } | |
175 } | |
176 | |
177 if (matching_url_count != nullptr) | |
178 *matching_url_count = count; | |
179 if (most_recent_creation_time != nullptr && latest_time != base::Time()) | |
180 *most_recent_creation_time = created_before - latest_time; | |
181 | |
182 return matching_page; | |
183 } | |
184 | |
185 void ReportPageHistogramAfterSave( | |
186 ClientPolicyController* policy_controller_, | |
187 const std::map<int64_t, OfflinePageItem>& offline_pages, | |
188 const OfflinePageItem& offline_page, | |
189 const base::Time& save_time) { | |
190 DCHECK(policy_controller_); | |
191 // The histogram below is an expansion of the UMA_HISTOGRAM_TIMES | |
192 // macro adapted to allow for a dynamically suffixed histogram name. | |
193 // Note: The factory creates and owns the histogram. | |
194 base::HistogramBase* histogram = base::Histogram::FactoryTimeGet( | |
195 AddHistogramSuffix(offline_page.client_id, "OfflinePages.SavePageTime"), | |
196 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromSeconds(10), | |
197 50, base::HistogramBase::kUmaTargetedHistogramFlag); | |
198 histogram->AddTime(save_time - offline_page.creation_time); | |
199 | |
200 // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS | |
201 // macro adapted to allow for a dynamically suffixed histogram name. | |
202 // Note: The factory creates and owns the histogram. | |
203 // Reported as Kb between 1Kb and 10Mb. | |
204 histogram = base::Histogram::FactoryGet( | |
205 AddHistogramSuffix(offline_page.client_id, "OfflinePages.PageSize"), | |
206 1, 10000, 50, base::HistogramBase::kUmaTargetedHistogramFlag); | |
207 histogram->Add(offline_page.file_size / 1024); | |
208 | |
209 if (policy_controller_->IsSupportedByDownload( | |
210 offline_page.client_id.name_space)) { | |
211 int matching_url_count; | |
212 base::TimeDelta time_since_most_recent_duplicate; | |
213 if (GetMatchingURLCountAndMostRecentCreationTime( | |
214 offline_pages, offline_page.client_id.name_space, offline_page.url, | |
215 offline_page.creation_time, &matching_url_count, | |
216 &time_since_most_recent_duplicate)) { | |
217 // Using CUSTOM_COUNTS instead of time-oriented histogram to record | |
218 // samples in seconds rather than milliseconds. | |
219 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
220 "OfflinePages.DownloadSavedPageTimeSinceDuplicateSaved", | |
221 time_since_most_recent_duplicate.InSeconds(), | |
222 base::TimeDelta::FromSeconds(1).InSeconds(), | |
223 base::TimeDelta::FromDays(7).InSeconds(), 50); | |
224 } | |
225 UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.DownloadSavedPageDuplicateCount", | |
226 matching_url_count, 1, 20, 10); | |
227 } | |
228 } | |
229 | |
230 void ReportPageHistogramsAfterDelete( | |
231 const std::map<int64_t, OfflinePageItem>& offline_pages, | |
232 const std::vector<OfflinePageItem>& deleted_pages, | |
233 const base::Time& delete_time) { | |
234 const int max_minutes = base::TimeDelta::FromDays(365).InMinutes(); | |
235 int64_t total_size = 0; | |
236 | |
237 for (const auto& page : deleted_pages) { | |
238 total_size += page.file_size; | |
239 ClientId client_id = page.client_id; | |
240 | |
241 if (client_id.name_space == kDownloadNamespace) { | |
242 int remaining_pages_with_url; | |
243 GetMatchingURLCountAndMostRecentCreationTime( | |
244 offline_pages, page.client_id.name_space, page.url, base::Time::Max(), | |
245 &remaining_pages_with_url, nullptr); | |
246 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
247 "OfflinePages.DownloadDeletedPageDuplicateCount", | |
248 remaining_pages_with_url, 1, 20, 10); | |
249 } | |
250 | |
251 // The histograms below are an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS | |
252 // macro adapted to allow for a dynamically suffixed histogram name. | |
253 // Note: The factory creates and owns the histogram. | |
254 base::HistogramBase* histogram = base::Histogram::FactoryGet( | |
255 AddHistogramSuffix(client_id, "OfflinePages.PageLifetime"), | |
256 1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag); | |
257 histogram->Add((delete_time - page.creation_time).InMinutes()); | |
258 | |
259 histogram = base::Histogram::FactoryGet( | |
260 AddHistogramSuffix( | |
261 client_id, "OfflinePages.DeletePage.TimeSinceLastOpen"), | |
262 1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag); | |
263 histogram->Add((delete_time - page.last_access_time).InMinutes()); | |
264 | |
265 histogram = base::Histogram::FactoryGet( | |
266 AddHistogramSuffix( | |
267 client_id, "OfflinePages.DeletePage.LastOpenToCreated"), | |
268 1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag); | |
269 histogram->Add((page.last_access_time - page.creation_time).InMinutes()); | |
270 | |
271 // Reported as Kb between 1Kb and 10Mb. | |
272 histogram = base::Histogram::FactoryGet( | |
273 AddHistogramSuffix(client_id, "OfflinePages.DeletePage.PageSize"), | |
274 1, 10000, 50, base::HistogramBase::kUmaTargetedHistogramFlag); | |
275 histogram->Add(page.file_size / 1024); | |
276 | |
277 histogram = base::Histogram::FactoryGet( | |
278 AddHistogramSuffix(client_id, "OfflinePages.DeletePage.AccessCount"), | |
279 1, 1000000, 50, base::HistogramBase::kUmaTargetedHistogramFlag); | |
280 histogram->Add(page.access_count); | |
281 } | |
282 | |
283 if (deleted_pages.size() > 1) { | |
284 UMA_HISTOGRAM_COUNTS("OfflinePages.BatchDelete.Count", | |
285 static_cast<int32_t>(deleted_pages.size())); | |
286 UMA_HISTOGRAM_MEMORY_KB( | |
287 "OfflinePages.BatchDelete.TotalPageSize", total_size / 1024); | |
288 } | |
289 } | |
290 | |
291 void ReportPageHistogramsAfterAccess(const OfflinePageItem& offline_page_item, | |
292 const base::Time& access_time) { | |
293 // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS | |
294 // macro adapted to allow for a dynamically suffixed histogram name. | |
295 // Note: The factory creates and owns the histogram. | |
296 base::HistogramBase* histogram = base::Histogram::FactoryGet( | |
297 AddHistogramSuffix( | |
298 offline_page_item.client_id, | |
299 offline_page_item.access_count == 0 ? | |
300 "OfflinePages.FirstOpenSinceCreated" : | |
301 "OfflinePages.OpenSinceLastOpen"), | |
302 1, kMaxOpenedPageHistogramBucket.InMinutes(), 50, | |
303 base::HistogramBase::kUmaTargetedHistogramFlag); | |
304 histogram->Add( | |
305 (access_time - offline_page_item.last_access_time).InMinutes()); | |
306 } | |
307 | |
308 } // namespace | |
309 | |
310 // protected | |
311 OfflinePageModelImpl::OfflinePageModelImpl() | |
312 : OfflinePageModel(), is_loaded_(false), weak_ptr_factory_(this) {} | |
313 | |
314 OfflinePageModelImpl::OfflinePageModelImpl( | |
315 std::unique_ptr<OfflinePageMetadataStore> store, | |
316 const base::FilePath& archives_dir, | |
317 const scoped_refptr<base::SequencedTaskRunner>& task_runner) | |
318 : store_(std::move(store)), | |
319 archives_dir_(archives_dir), | |
320 is_loaded_(false), | |
321 policy_controller_(new ClientPolicyController()), | |
322 archive_manager_(new ArchiveManager(archives_dir, task_runner)), | |
323 testing_clock_(nullptr), | |
324 weak_ptr_factory_(this) { | |
325 archive_manager_->EnsureArchivesDirCreated( | |
326 base::Bind(&OfflinePageModelImpl::OnEnsureArchivesDirCreatedDone, | |
327 weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now())); | |
328 } | |
329 | |
330 OfflinePageModelImpl::~OfflinePageModelImpl() {} | |
331 | |
332 void OfflinePageModelImpl::AddObserver(Observer* observer) { | |
333 observers_.AddObserver(observer); | |
334 } | |
335 | |
336 void OfflinePageModelImpl::RemoveObserver(Observer* observer) { | |
337 observers_.RemoveObserver(observer); | |
338 } | |
339 | |
340 void OfflinePageModelImpl::SavePage( | |
341 const SavePageParams& save_page_params, | |
342 std::unique_ptr<OfflinePageArchiver> archiver, | |
343 const SavePageCallback& callback) { | |
344 DCHECK(is_loaded_); | |
345 | |
346 // Skip saving the page that is not intended to be saved, like local file | |
347 // page. | |
348 if (!OfflinePageModel::CanSaveURL(save_page_params.url)) { | |
349 InformSavePageDone(callback, SavePageResult::SKIPPED, | |
350 save_page_params.client_id, kInvalidOfflineId); | |
351 return; | |
352 } | |
353 | |
354 // The web contents is not available if archiver is not created and passed. | |
355 if (!archiver.get()) { | |
356 InformSavePageDone(callback, SavePageResult::CONTENT_UNAVAILABLE, | |
357 save_page_params.client_id, kInvalidOfflineId); | |
358 return; | |
359 } | |
360 | |
361 // If we already have an offline id, use it. If not, generate one. | |
362 int64_t offline_id = save_page_params.proposed_offline_id; | |
363 if (offline_id == kInvalidOfflineId) | |
364 offline_id = GenerateOfflineId(); | |
365 | |
366 archiver->CreateArchive( | |
367 archives_dir_, offline_id, | |
368 base::Bind(&OfflinePageModelImpl::OnCreateArchiveDone, | |
369 weak_ptr_factory_.GetWeakPtr(), save_page_params, offline_id, | |
370 GetCurrentTime(), callback)); | |
371 pending_archivers_.push_back(std::move(archiver)); | |
372 } | |
373 | |
374 void OfflinePageModelImpl::MarkPageAccessed(int64_t offline_id) { | |
375 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::MarkPageAccessedWhenLoadDone, | |
376 weak_ptr_factory_.GetWeakPtr(), offline_id)); | |
377 } | |
378 | |
379 void OfflinePageModelImpl::MarkPageAccessedWhenLoadDone(int64_t offline_id) { | |
380 DCHECK(is_loaded_); | |
381 | |
382 auto iter = offline_pages_.find(offline_id); | |
383 if (iter == offline_pages_.end() || iter->second.IsExpired()) | |
384 return; | |
385 | |
386 // Make a copy of the cached item and update it. The cached item should only | |
387 // be updated upon the successful store operation. | |
388 OfflinePageItem offline_page_item = iter->second; | |
389 | |
390 ReportPageHistogramsAfterAccess(offline_page_item, GetCurrentTime()); | |
391 | |
392 offline_page_item.last_access_time = GetCurrentTime(); | |
393 offline_page_item.access_count++; | |
394 | |
395 std::vector<OfflinePageItem> items = { offline_page_item }; | |
396 store_->UpdateOfflinePages( | |
397 items, base::Bind(&OfflinePageModelImpl::OnMarkPageAccesseDone, | |
398 weak_ptr_factory_.GetWeakPtr(), offline_page_item)); | |
399 } | |
400 | |
401 void OfflinePageModelImpl::DeletePagesByOfflineId( | |
402 const std::vector<int64_t>& offline_ids, | |
403 const DeletePageCallback& callback) { | |
404 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::DoDeletePagesByOfflineId, | |
405 weak_ptr_factory_.GetWeakPtr(), offline_ids, | |
406 callback)); | |
407 } | |
408 | |
409 void OfflinePageModelImpl::DoDeletePagesByOfflineId( | |
410 const std::vector<int64_t>& offline_ids, | |
411 const DeletePageCallback& callback) { | |
412 DCHECK(is_loaded_); | |
413 | |
414 std::vector<base::FilePath> paths_to_delete; | |
415 for (const auto& offline_id : offline_ids) { | |
416 auto iter = offline_pages_.find(offline_id); | |
417 if (iter != offline_pages_.end() && !iter->second.IsExpired()) { | |
418 paths_to_delete.push_back(iter->second.file_path); | |
419 } | |
420 } | |
421 | |
422 // If there're no pages to delete, return early. | |
423 if (paths_to_delete.empty()) { | |
424 InformDeletePageDone(callback, DeletePageResult::SUCCESS); | |
425 return; | |
426 } | |
427 | |
428 archive_manager_->DeleteMultipleArchives( | |
429 paths_to_delete, | |
430 base::Bind(&OfflinePageModelImpl::OnDeleteArchiveFilesDone, | |
431 weak_ptr_factory_.GetWeakPtr(), offline_ids, callback)); | |
432 } | |
433 | |
434 void OfflinePageModelImpl::DeletePagesByClientIds( | |
435 const std::vector<ClientId>& client_ids, | |
436 const DeletePageCallback& callback) { | |
437 OfflinePageModelQueryBuilder builder; | |
438 builder | |
439 .SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING, | |
440 client_ids) | |
441 .AllowExpiredPages(true); | |
442 auto delete_pages = base::Bind(&OfflinePageModelImpl::DeletePages, | |
443 weak_ptr_factory_.GetWeakPtr(), callback); | |
444 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery, | |
445 weak_ptr_factory_.GetWeakPtr(), | |
446 base::Passed(builder.Build(GetPolicyController())), | |
447 delete_pages)); | |
448 } | |
449 | |
450 void OfflinePageModelImpl::DeletePages( | |
451 const DeletePageCallback& callback, | |
452 const MultipleOfflinePageItemResult& pages) { | |
453 DCHECK(is_loaded_); | |
454 | |
455 std::vector<int64_t> offline_ids; | |
456 for (auto& page : pages) | |
457 offline_ids.emplace_back(page.offline_id); | |
458 | |
459 DoDeletePagesByOfflineId(offline_ids, callback); | |
460 } | |
461 | |
462 void OfflinePageModelImpl::GetPagesByClientIds( | |
463 const std::vector<ClientId>& client_ids, | |
464 const MultipleOfflinePageItemCallback& callback) { | |
465 OfflinePageModelQueryBuilder builder; | |
466 builder.SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING, | |
467 client_ids); | |
468 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery, | |
469 weak_ptr_factory_.GetWeakPtr(), | |
470 base::Passed(builder.Build(GetPolicyController())), | |
471 callback)); | |
472 } | |
473 | |
474 void OfflinePageModelImpl::DeleteCachedPagesByURLPredicate( | |
475 const UrlPredicate& predicate, | |
476 const DeletePageCallback& callback) { | |
477 RunWhenLoaded( | |
478 base::Bind(&OfflinePageModelImpl::DoDeleteCachedPagesByURLPredicate, | |
479 weak_ptr_factory_.GetWeakPtr(), predicate, callback)); | |
480 } | |
481 | |
482 void OfflinePageModelImpl::DoDeleteCachedPagesByURLPredicate( | |
483 const UrlPredicate& predicate, | |
484 const DeletePageCallback& callback) { | |
485 DCHECK(is_loaded_); | |
486 | |
487 std::vector<int64_t> offline_ids; | |
488 for (const auto& id_page_pair : offline_pages_) { | |
489 if (IsRemovedOnCacheReset(id_page_pair.second) && | |
490 predicate.Run(id_page_pair.second.url)) { | |
491 offline_ids.push_back(id_page_pair.first); | |
492 } | |
493 } | |
494 DoDeletePagesByOfflineId(offline_ids, callback); | |
495 } | |
496 | |
497 void OfflinePageModelImpl::CheckPagesExistOffline( | |
498 const std::set<GURL>& urls, | |
499 const CheckPagesExistOfflineCallback& callback) { | |
500 OfflinePageModelQueryBuilder builder; | |
501 builder | |
502 .SetUrls(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING, | |
503 std::vector<GURL>(urls.begin(), urls.end())) | |
504 .RequireRestrictedToOriginalTab( | |
505 OfflinePageModelQueryBuilder::Requirement::EXCLUDE_MATCHING); | |
506 auto pages_to_urls = base::Bind( | |
507 [](const CheckPagesExistOfflineCallback& callback, | |
508 const MultipleOfflinePageItemResult& pages) { | |
509 CheckPagesExistOfflineResult result; | |
510 for (auto& page : pages) | |
511 result.insert(page.url); | |
512 callback.Run(result); | |
513 }, | |
514 callback); | |
515 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery, | |
516 weak_ptr_factory_.GetWeakPtr(), | |
517 base::Passed(builder.Build(GetPolicyController())), | |
518 pages_to_urls)); | |
519 } | |
520 | |
521 void OfflinePageModelImpl::GetPagesMatchingQuery( | |
522 std::unique_ptr<OfflinePageModelQuery> query, | |
523 const MultipleOfflinePageItemCallback& callback) { | |
524 DCHECK(query); | |
525 | |
526 MultipleOfflinePageItemResult offline_pages_result; | |
527 | |
528 for (const auto& id_page_pair : offline_pages_) { | |
529 if (query->Matches(id_page_pair.second)) | |
530 offline_pages_result.emplace_back(id_page_pair.second); | |
531 } | |
532 | |
533 callback.Run(offline_pages_result); | |
534 } | |
535 | |
536 void OfflinePageModelImpl::GetAllPages( | |
537 const MultipleOfflinePageItemCallback& callback) { | |
538 OfflinePageModelQueryBuilder builder; | |
539 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery, | |
540 weak_ptr_factory_.GetWeakPtr(), | |
541 base::Passed(builder.Build(GetPolicyController())), | |
542 callback)); | |
543 } | |
544 | |
545 void OfflinePageModelImpl::GetAllPagesWithExpired( | |
546 const MultipleOfflinePageItemCallback& callback) { | |
547 OfflinePageModelQueryBuilder builder; | |
548 builder.AllowExpiredPages(true); | |
549 | |
550 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery, | |
551 weak_ptr_factory_.GetWeakPtr(), | |
552 base::Passed(builder.Build(GetPolicyController())), | |
553 callback)); | |
554 } | |
555 | |
556 void OfflinePageModelImpl::GetOfflineIdsForClientId( | |
557 const ClientId& client_id, | |
558 const MultipleOfflineIdCallback& callback) { | |
559 RunWhenLoaded( | |
560 base::Bind(&OfflinePageModelImpl::GetOfflineIdsForClientIdWhenLoadDone, | |
561 weak_ptr_factory_.GetWeakPtr(), client_id, callback)); | |
562 } | |
563 | |
564 void OfflinePageModelImpl::GetOfflineIdsForClientIdWhenLoadDone( | |
565 const ClientId& client_id, | |
566 const MultipleOfflineIdCallback& callback) const { | |
567 callback.Run(MaybeGetOfflineIdsForClientId(client_id)); | |
568 } | |
569 | |
570 const std::vector<int64_t> OfflinePageModelImpl::MaybeGetOfflineIdsForClientId( | |
571 const ClientId& client_id) const { | |
572 DCHECK(is_loaded_); | |
573 std::vector<int64_t> results; | |
574 | |
575 // We want only all pages, including those marked for deletion. | |
576 // TODO(fgorski): actually use an index rather than linear scan. | |
577 for (const auto& id_page_pair : offline_pages_) { | |
578 if (id_page_pair.second.client_id == client_id && | |
579 !id_page_pair.second.IsExpired()) { | |
580 results.push_back(id_page_pair.second.offline_id); | |
581 } | |
582 } | |
583 return results; | |
584 } | |
585 | |
586 void OfflinePageModelImpl::GetPageByOfflineId( | |
587 int64_t offline_id, | |
588 const SingleOfflinePageItemCallback& callback) { | |
589 std::vector<int64_t> query_ids; | |
590 query_ids.emplace_back(offline_id); | |
591 | |
592 OfflinePageModelQueryBuilder builder; | |
593 builder.SetOfflinePageIds( | |
594 OfflinePageModelQuery::Requirement::INCLUDE_MATCHING, query_ids); | |
595 | |
596 auto multiple_callback = base::Bind( | |
597 [](const SingleOfflinePageItemCallback& callback, | |
598 const MultipleOfflinePageItemResult& result) { | |
599 DCHECK_LE(result.size(), 1U); | |
600 if (result.empty()) { | |
601 callback.Run(nullptr); | |
602 } else { | |
603 callback.Run(&result[0]); | |
604 } | |
605 }, | |
606 callback); | |
607 | |
608 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery, | |
609 weak_ptr_factory_.GetWeakPtr(), | |
610 base::Passed(builder.Build(GetPolicyController())), | |
611 multiple_callback)); | |
612 } | |
613 | |
614 void OfflinePageModelImpl::GetPagesByURL( | |
615 const GURL& url, | |
616 URLSearchMode url_search_mode, | |
617 const MultipleOfflinePageItemCallback& callback) { | |
618 RunWhenLoaded( | |
619 base::Bind(&OfflinePageModelImpl::GetPagesByURLWhenLoadDone, | |
620 weak_ptr_factory_.GetWeakPtr(), url, | |
621 url_search_mode, callback)); | |
622 } | |
623 | |
624 void OfflinePageModelImpl::GetPagesByURLWhenLoadDone( | |
625 const GURL& url, | |
626 URLSearchMode url_search_mode, | |
627 const MultipleOfflinePageItemCallback& callback) const { | |
628 std::vector<OfflinePageItem> result; | |
629 | |
630 GURL::Replacements remove_params; | |
631 remove_params.ClearRef(); | |
632 | |
633 GURL url_without_fragment = | |
634 url.ReplaceComponents(remove_params); | |
635 | |
636 for (const auto& id_page_pair : offline_pages_) { | |
637 if (id_page_pair.second.IsExpired()) | |
638 continue; | |
639 // First, search by last committed URL with fragment stripped. | |
640 if (url_without_fragment == | |
641 id_page_pair.second.url.ReplaceComponents(remove_params)) { | |
642 result.push_back(id_page_pair.second); | |
643 continue; | |
644 } | |
645 // Then, search by original request URL if |url_search_mode| wants it. | |
646 // Note that we want to do the exact match with fragment included. This is | |
647 // because original URL is used for redirect purpose and it is always safer | |
648 // to support the exact redirect. | |
649 if (url_search_mode == URLSearchMode::SEARCH_BY_ALL_URLS && | |
650 url == id_page_pair.second.original_url) { | |
651 result.push_back(id_page_pair.second); | |
652 } | |
653 } | |
654 | |
655 callback.Run(result); | |
656 } | |
657 | |
658 void OfflinePageModelImpl::CheckMetadataConsistency() { | |
659 archive_manager_->GetAllArchives( | |
660 base::Bind(&OfflinePageModelImpl::CheckMetadataConsistencyForArchivePaths, | |
661 weak_ptr_factory_.GetWeakPtr())); | |
662 } | |
663 | |
664 void OfflinePageModelImpl::ExpirePages( | |
665 const std::vector<int64_t>& offline_ids, | |
666 const base::Time& expiration_time, | |
667 const base::Callback<void(bool)>& callback) { | |
668 std::vector<base::FilePath> paths_to_delete; | |
669 std::vector<OfflinePageItem> items_to_update; | |
670 for (int64_t offline_id : offline_ids) { | |
671 auto iter = offline_pages_.find(offline_id); | |
672 if (iter == offline_pages_.end()) | |
673 continue; | |
674 | |
675 OfflinePageItem offline_page = iter->second; | |
676 paths_to_delete.push_back(offline_page.file_path); | |
677 offline_page.expiration_time = expiration_time; | |
678 | |
679 items_to_update.push_back(offline_page); | |
680 } | |
681 | |
682 store_->UpdateOfflinePages( | |
683 items_to_update, | |
684 base::Bind(&OfflinePageModelImpl::OnExpirePageDone, | |
685 weak_ptr_factory_.GetWeakPtr(), expiration_time)); | |
686 | |
687 if (paths_to_delete.empty()) { | |
688 callback.Run(true); | |
689 return; | |
690 } | |
691 archive_manager_->DeleteMultipleArchives(paths_to_delete, callback); | |
692 } | |
693 | |
694 void OfflinePageModelImpl::OnExpirePageDone( | |
695 const base::Time& expiration_time, | |
696 std::unique_ptr<OfflinePagesUpdateResult> result) { | |
697 UMA_HISTOGRAM_BOOLEAN("OfflinePages.ExpirePage.StoreUpdateResult", | |
698 result->updated_items.size() > 0); | |
699 for (const auto& expired_page : result->updated_items) { | |
700 const auto& iter = offline_pages_.find(expired_page.offline_id); | |
701 if (iter == offline_pages_.end()) | |
702 continue; | |
703 | |
704 iter->second.expiration_time = expiration_time; | |
705 ClientId client_id = iter->second.client_id; | |
706 offline_event_logger_.RecordPageExpired( | |
707 std::to_string(expired_page.offline_id)); | |
708 base::HistogramBase* histogram = base::Histogram::FactoryGet( | |
709 AddHistogramSuffix(client_id, "OfflinePages.ExpirePage.PageLifetime"), | |
710 1, base::TimeDelta::FromDays(30).InMinutes(), 50, | |
711 base::HistogramBase::kUmaTargetedHistogramFlag); | |
712 histogram->Add((expiration_time - iter->second.creation_time).InMinutes()); | |
713 histogram = base::Histogram::FactoryGet( | |
714 AddHistogramSuffix(client_id, | |
715 "OfflinePages.ExpirePage.TimeSinceLastAccess"), | |
716 1, base::TimeDelta::FromDays(30).InMinutes(), 50, | |
717 base::HistogramBase::kUmaTargetedHistogramFlag); | |
718 histogram->Add( | |
719 (expiration_time - iter->second.last_access_time).InMinutes()); | |
720 } | |
721 } | |
722 | |
723 ClientPolicyController* OfflinePageModelImpl::GetPolicyController() { | |
724 return policy_controller_.get(); | |
725 } | |
726 | |
727 OfflinePageMetadataStore* OfflinePageModelImpl::GetStoreForTesting() { | |
728 return store_.get(); | |
729 } | |
730 | |
731 OfflinePageStorageManager* OfflinePageModelImpl::GetStorageManager() { | |
732 return storage_manager_.get(); | |
733 } | |
734 | |
735 bool OfflinePageModelImpl::is_loaded() const { | |
736 return is_loaded_; | |
737 } | |
738 | |
739 OfflineEventLogger* OfflinePageModelImpl::GetLogger() { | |
740 return &offline_event_logger_; | |
741 } | |
742 | |
743 void OfflinePageModelImpl::OnCreateArchiveDone( | |
744 const SavePageParams& save_page_params, | |
745 int64_t offline_id, | |
746 const base::Time& start_time, | |
747 const SavePageCallback& callback, | |
748 OfflinePageArchiver* archiver, | |
749 ArchiverResult archiver_result, | |
750 const GURL& url, | |
751 const base::FilePath& file_path, | |
752 const base::string16& title, | |
753 int64_t file_size) { | |
754 if (save_page_params.url != url) { | |
755 DVLOG(1) << "Saved URL does not match requested URL."; | |
756 // TODO(fgorski): We have created an archive for a wrong URL. It should be | |
757 // deleted from here, once archiver has the right functionality. | |
758 InformSavePageDone(callback, SavePageResult::ARCHIVE_CREATION_FAILED, | |
759 save_page_params.client_id, offline_id); | |
760 DeletePendingArchiver(archiver); | |
761 return; | |
762 } | |
763 | |
764 if (archiver_result != ArchiverResult::SUCCESSFULLY_CREATED) { | |
765 SavePageResult result = ToSavePageResult(archiver_result); | |
766 InformSavePageDone( | |
767 callback, result, save_page_params.client_id, offline_id); | |
768 DeletePendingArchiver(archiver); | |
769 return; | |
770 } | |
771 OfflinePageItem offline_page_item(url, offline_id, save_page_params.client_id, | |
772 file_path, file_size, start_time); | |
773 offline_page_item.title = title; | |
774 offline_page_item.original_url = save_page_params.original_url; | |
775 store_->AddOfflinePage(offline_page_item, | |
776 base::Bind(&OfflinePageModelImpl::OnAddOfflinePageDone, | |
777 weak_ptr_factory_.GetWeakPtr(), archiver, | |
778 callback, offline_page_item)); | |
779 } | |
780 | |
781 void OfflinePageModelImpl::OnAddOfflinePageDone( | |
782 OfflinePageArchiver* archiver, | |
783 const SavePageCallback& callback, | |
784 const OfflinePageItem& offline_page, | |
785 ItemActionStatus status) { | |
786 SavePageResult result; | |
787 if (status == ItemActionStatus::SUCCESS) { | |
788 offline_pages_[offline_page.offline_id] = offline_page; | |
789 result = SavePageResult::SUCCESS; | |
790 ReportPageHistogramAfterSave(policy_controller_.get(), offline_pages_, | |
791 offline_page, GetCurrentTime()); | |
792 offline_event_logger_.RecordPageSaved( | |
793 offline_page.client_id.name_space, offline_page.url.spec(), | |
794 std::to_string(offline_page.offline_id)); | |
795 } else if (status == ItemActionStatus::ALREADY_EXISTS) { | |
796 result = SavePageResult::ALREADY_EXISTS; | |
797 } else { | |
798 result = SavePageResult::STORE_FAILURE; | |
799 } | |
800 InformSavePageDone(callback, result, offline_page.client_id, | |
801 offline_page.offline_id); | |
802 if (result == SavePageResult::SUCCESS) { | |
803 DeleteExistingPagesWithSameURL(offline_page); | |
804 } else { | |
805 PostClearStorageIfNeededTask(false /* delayed */); | |
806 } | |
807 | |
808 DeletePendingArchiver(archiver); | |
809 for (Observer& observer : observers_) | |
810 observer.OfflinePageModelChanged(this); | |
811 } | |
812 | |
813 void OfflinePageModelImpl::OnMarkPageAccesseDone( | |
814 const OfflinePageItem& offline_page_item, | |
815 std::unique_ptr<OfflinePagesUpdateResult> result) { | |
816 // Update the item in the cache only upon success. | |
817 if (result->updated_items.size() > 0) | |
818 offline_pages_[offline_page_item.offline_id] = offline_page_item; | |
819 | |
820 // No need to fire OfflinePageModelChanged event since updating access info | |
821 // should not have any impact to the UI. | |
822 } | |
823 | |
824 void OfflinePageModelImpl::OnEnsureArchivesDirCreatedDone( | |
825 const base::TimeTicks& start_time) { | |
826 UMA_HISTOGRAM_TIMES("OfflinePages.Model.ArchiveDirCreationTime", | |
827 base::TimeTicks::Now() - start_time); | |
828 | |
829 const int kResetAttemptsLeft = 1; | |
830 store_->Initialize(base::Bind(&OfflinePageModelImpl::OnStoreInitialized, | |
831 weak_ptr_factory_.GetWeakPtr(), start_time, | |
832 kResetAttemptsLeft)); | |
833 } | |
834 | |
835 void OfflinePageModelImpl::OnStoreInitialized(const base::TimeTicks& start_time, | |
836 int reset_attempts_left, | |
837 bool success) { | |
838 if (success) { | |
839 DCHECK_EQ(store_->state(), StoreState::LOADED); | |
840 store_->GetOfflinePages( | |
841 base::Bind(&OfflinePageModelImpl::OnInitialGetOfflinePagesDone, | |
842 weak_ptr_factory_.GetWeakPtr(), start_time)); | |
843 return; | |
844 } | |
845 | |
846 DCHECK_EQ(store_->state(), StoreState::FAILED_LOADING); | |
847 // If there are no more reset attempts left, stop here. | |
848 if (reset_attempts_left == 0) { | |
849 FinalizeModelLoad(); | |
850 return; | |
851 } | |
852 | |
853 // Otherwise reduce the remaining attempts counter and reset store. | |
854 store_->Reset(base::Bind(&OfflinePageModelImpl::OnStoreResetDone, | |
855 weak_ptr_factory_.GetWeakPtr(), start_time, | |
856 reset_attempts_left - 1)); | |
857 } | |
858 | |
859 void OfflinePageModelImpl::OnStoreResetDone(const base::TimeTicks& start_time, | |
860 int reset_attempts_left, | |
861 bool success) { | |
862 if (success) { | |
863 DCHECK_EQ(store_->state(), StoreState::NOT_LOADED); | |
864 store_->Initialize(base::Bind(&OfflinePageModelImpl::OnStoreInitialized, | |
865 weak_ptr_factory_.GetWeakPtr(), start_time, | |
866 reset_attempts_left)); | |
867 return; | |
868 } | |
869 | |
870 DCHECK_EQ(store_->state(), StoreState::FAILED_RESET); | |
871 FinalizeModelLoad(); | |
872 } | |
873 | |
874 void OfflinePageModelImpl::OnInitialGetOfflinePagesDone( | |
875 const base::TimeTicks& start_time, | |
876 const std::vector<OfflinePageItem>& offline_pages) { | |
877 DCHECK(!is_loaded_); | |
878 | |
879 UMA_HISTOGRAM_TIMES("OfflinePages.Model.ConstructionToLoadedEventTime", | |
880 base::TimeTicks::Now() - start_time); | |
881 | |
882 CacheLoadedData(offline_pages); | |
883 FinalizeModelLoad(); | |
884 | |
885 // Ensure necessary cleanup operations are started. | |
886 CheckMetadataConsistency(); | |
887 } | |
888 | |
889 void OfflinePageModelImpl::FinalizeModelLoad() { | |
890 is_loaded_ = true; | |
891 | |
892 // All actions below are meant to be taken regardless of successful load of | |
893 // the store. | |
894 | |
895 // Inform observers the load is done. | |
896 for (Observer& observer : observers_) | |
897 observer.OfflinePageModelLoaded(this); | |
898 | |
899 // Run all the delayed tasks. | |
900 for (const auto& delayed_task : delayed_tasks_) | |
901 delayed_task.Run(); | |
902 delayed_tasks_.clear(); | |
903 | |
904 // Clear storage. | |
905 PostClearStorageIfNeededTask(true /* delayed */); | |
906 } | |
907 | |
908 void OfflinePageModelImpl::InformSavePageDone(const SavePageCallback& callback, | |
909 SavePageResult result, | |
910 const ClientId& client_id, | |
911 int64_t offline_id) { | |
912 ReportSavePageResultHistogramAfterSave(client_id, result); | |
913 archive_manager_->GetStorageStats( | |
914 base::Bind(&ReportStorageHistogramsAfterSave)); | |
915 callback.Run(result, offline_id); | |
916 } | |
917 | |
918 void OfflinePageModelImpl::DeleteExistingPagesWithSameURL( | |
919 const OfflinePageItem& offline_page) { | |
920 // Remove existing pages generated by the same policy and with same url. | |
921 size_t pages_allowed = | |
922 policy_controller_->GetPolicy(offline_page.client_id.name_space) | |
923 .pages_allowed_per_url; | |
924 if (pages_allowed == kUnlimitedPages) | |
925 return; | |
926 GetPagesByURL( | |
927 offline_page.url, | |
928 URLSearchMode::SEARCH_BY_FINAL_URL_ONLY, | |
929 base::Bind(&OfflinePageModelImpl::OnPagesFoundWithSameURL, | |
930 weak_ptr_factory_.GetWeakPtr(), offline_page, pages_allowed)); | |
931 } | |
932 | |
933 void OfflinePageModelImpl::OnPagesFoundWithSameURL( | |
934 const OfflinePageItem& offline_page, | |
935 size_t pages_allowed, | |
936 const MultipleOfflinePageItemResult& items) { | |
937 std::vector<OfflinePageItem> pages_to_delete; | |
938 for (const auto& item : items) { | |
939 if (item.offline_id != offline_page.offline_id && | |
940 item.client_id.name_space == offline_page.client_id.name_space) { | |
941 pages_to_delete.push_back(item); | |
942 } | |
943 } | |
944 // Only keep |pages_allowed|-1 of most fresh pages and delete others, by | |
945 // sorting pages with the oldest ones first and resize the vector. | |
946 if (pages_to_delete.size() >= pages_allowed) { | |
947 sort(pages_to_delete.begin(), pages_to_delete.end(), | |
948 [](const OfflinePageItem& a, const OfflinePageItem& b) -> bool { | |
949 return a.last_access_time < b.last_access_time; | |
950 }); | |
951 pages_to_delete.resize(pages_to_delete.size() - pages_allowed + 1); | |
952 } | |
953 std::vector<int64_t> page_ids_to_delete; | |
954 for (const auto& item : pages_to_delete) | |
955 page_ids_to_delete.push_back(item.offline_id); | |
956 DeletePagesByOfflineId( | |
957 page_ids_to_delete, | |
958 base::Bind(&OfflinePageModelImpl::OnDeleteOldPagesWithSameURL, | |
959 weak_ptr_factory_.GetWeakPtr())); | |
960 } | |
961 | |
962 void OfflinePageModelImpl::OnDeleteOldPagesWithSameURL( | |
963 DeletePageResult result) { | |
964 // TODO(romax) Add UMAs for failure cases. | |
965 PostClearStorageIfNeededTask(false /* delayed */); | |
966 } | |
967 | |
968 void OfflinePageModelImpl::DeletePendingArchiver( | |
969 OfflinePageArchiver* archiver) { | |
970 pending_archivers_.erase(std::find(pending_archivers_.begin(), | |
971 pending_archivers_.end(), archiver)); | |
972 } | |
973 | |
974 void OfflinePageModelImpl::OnDeleteArchiveFilesDone( | |
975 const std::vector<int64_t>& offline_ids, | |
976 const DeletePageCallback& callback, | |
977 bool success) { | |
978 if (!success) { | |
979 InformDeletePageDone(callback, DeletePageResult::DEVICE_FAILURE); | |
980 return; | |
981 } | |
982 | |
983 store_->RemoveOfflinePages( | |
984 offline_ids, base::Bind(&OfflinePageModelImpl::OnRemoveOfflinePagesDone, | |
985 weak_ptr_factory_.GetWeakPtr(), callback)); | |
986 } | |
987 | |
988 void OfflinePageModelImpl::OnRemoveOfflinePagesDone( | |
989 const DeletePageCallback& callback, | |
990 std::unique_ptr<OfflinePagesUpdateResult> result) { | |
991 ReportPageHistogramsAfterDelete(offline_pages_, result->updated_items, | |
992 GetCurrentTime()); | |
993 | |
994 // This part of the loop is explicitly broken out, as it should be gone in | |
995 // fully asynchronous code. | |
996 for (const auto& page : result->updated_items) { | |
997 int64_t offline_id = page.offline_id; | |
998 offline_event_logger_.RecordPageDeleted(std::to_string(offline_id)); | |
999 auto iter = offline_pages_.find(offline_id); | |
1000 if (iter == offline_pages_.end()) | |
1001 continue; | |
1002 offline_pages_.erase(iter); | |
1003 } | |
1004 | |
1005 for (const auto& page : result->updated_items) { | |
1006 for (Observer& observer : observers_) | |
1007 observer.OfflinePageDeleted(page.offline_id, page.client_id); | |
1008 } | |
1009 | |
1010 // TODO(fgorski): React the FAILED_INITIALIZATION, FAILED_RESET here. | |
1011 // TODO(fgorski): We need a better callback interface for the Remove action on | |
1012 // the this class. Currently removing an item that does not exist is | |
1013 // considered a success, but not called out as such to the caller. | |
1014 DeletePageResult delete_result; | |
1015 if (result->store_state == StoreState::LOADED) | |
1016 delete_result = DeletePageResult::SUCCESS; | |
1017 else | |
1018 delete_result = DeletePageResult::STORE_FAILURE; | |
1019 | |
1020 InformDeletePageDone(callback, delete_result); | |
1021 } | |
1022 | |
1023 void OfflinePageModelImpl::InformDeletePageDone( | |
1024 const DeletePageCallback& callback, | |
1025 DeletePageResult result) { | |
1026 UMA_HISTOGRAM_ENUMERATION("OfflinePages.DeletePageResult", | |
1027 static_cast<int>(result), | |
1028 static_cast<int>(DeletePageResult::RESULT_COUNT)); | |
1029 archive_manager_->GetStorageStats( | |
1030 base::Bind(&ReportStorageHistogramsAfterDelete)); | |
1031 if (!callback.is_null()) | |
1032 callback.Run(result); | |
1033 } | |
1034 | |
1035 void OfflinePageModelImpl::CheckMetadataConsistencyForArchivePaths( | |
1036 const std::set<base::FilePath>& archive_paths) { | |
1037 ExpirePagesMissingArchiveFile(archive_paths); | |
1038 DeleteOrphanedArchives(archive_paths); | |
1039 } | |
1040 | |
1041 void OfflinePageModelImpl::ExpirePagesMissingArchiveFile( | |
1042 const std::set<base::FilePath>& archive_paths) { | |
1043 std::vector<int64_t> ids_of_pages_missing_archive_file; | |
1044 for (const auto& id_page_pair : offline_pages_) { | |
1045 if (archive_paths.count(id_page_pair.second.file_path) == 0UL) | |
1046 ids_of_pages_missing_archive_file.push_back(id_page_pair.first); | |
1047 } | |
1048 | |
1049 if (ids_of_pages_missing_archive_file.empty()) | |
1050 return; | |
1051 | |
1052 ExpirePages( | |
1053 ids_of_pages_missing_archive_file, GetCurrentTime(), | |
1054 base::Bind(&OfflinePageModelImpl::OnExpirePagesMissingArchiveFileDone, | |
1055 weak_ptr_factory_.GetWeakPtr(), | |
1056 ids_of_pages_missing_archive_file)); | |
1057 } | |
1058 | |
1059 void OfflinePageModelImpl::OnExpirePagesMissingArchiveFileDone( | |
1060 const std::vector<int64_t>& offline_ids, | |
1061 bool success) { | |
1062 UMA_HISTOGRAM_COUNTS("OfflinePages.Consistency.PagesMissingArchiveFileCount", | |
1063 static_cast<int32_t>(offline_ids.size())); | |
1064 UMA_HISTOGRAM_BOOLEAN( | |
1065 "OfflinePages.Consistency.ExpirePagesMissingArchiveFileResult", success); | |
1066 } | |
1067 | |
1068 void OfflinePageModelImpl::DeleteOrphanedArchives( | |
1069 const std::set<base::FilePath>& archive_paths) { | |
1070 // Archives are considered orphaned unless they are pointed to by some pages. | |
1071 std::set<base::FilePath> orphaned_archive_set(archive_paths); | |
1072 for (const auto& id_page_pair : offline_pages_) | |
1073 orphaned_archive_set.erase(id_page_pair.second.file_path); | |
1074 | |
1075 if (orphaned_archive_set.empty()) | |
1076 return; | |
1077 | |
1078 std::vector<base::FilePath> orphaned_archives(orphaned_archive_set.begin(), | |
1079 orphaned_archive_set.end()); | |
1080 archive_manager_->DeleteMultipleArchives( | |
1081 orphaned_archives, | |
1082 base::Bind(&OfflinePageModelImpl::OnDeleteOrphanedArchivesDone, | |
1083 weak_ptr_factory_.GetWeakPtr(), orphaned_archives)); | |
1084 } | |
1085 | |
1086 void OfflinePageModelImpl::OnDeleteOrphanedArchivesDone( | |
1087 const std::vector<base::FilePath>& archives, | |
1088 bool success) { | |
1089 UMA_HISTOGRAM_COUNTS("OfflinePages.Consistency.OrphanedArchivesCount", | |
1090 static_cast<int32_t>(archives.size())); | |
1091 UMA_HISTOGRAM_BOOLEAN("OfflinePages.Consistency.DeleteOrphanedArchivesResult", | |
1092 success); | |
1093 } | |
1094 | |
1095 void OfflinePageModelImpl::CacheLoadedData( | |
1096 const std::vector<OfflinePageItem>& offline_pages) { | |
1097 offline_pages_.clear(); | |
1098 for (const auto& offline_page : offline_pages) | |
1099 offline_pages_[offline_page.offline_id] = offline_page; | |
1100 } | |
1101 | |
1102 void OfflinePageModelImpl::ClearStorageIfNeeded( | |
1103 const ClearStorageCallback& callback) { | |
1104 // Create Storage Manager if necessary. | |
1105 if (!storage_manager_) { | |
1106 storage_manager_.reset(new OfflinePageStorageManager( | |
1107 this, GetPolicyController(), archive_manager_.get())); | |
1108 } | |
1109 storage_manager_->ClearPagesIfNeeded(callback); | |
1110 } | |
1111 | |
1112 void OfflinePageModelImpl::OnStorageCleared(size_t expired_page_count, | |
1113 ClearStorageResult result) { | |
1114 UMA_HISTOGRAM_ENUMERATION("OfflinePages.ClearStorageResult", | |
1115 static_cast<int>(result), | |
1116 static_cast<int>(ClearStorageResult::RESULT_COUNT)); | |
1117 if (expired_page_count > 0) { | |
1118 UMA_HISTOGRAM_COUNTS("OfflinePages.ExpirePage.BatchSize", | |
1119 static_cast<int32_t>(expired_page_count)); | |
1120 } | |
1121 } | |
1122 | |
1123 void OfflinePageModelImpl::PostClearStorageIfNeededTask(bool delayed) { | |
1124 base::TimeDelta delay = | |
1125 delayed ? kStorageManagerStartingDelay : base::TimeDelta(); | |
1126 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
1127 FROM_HERE, base::Bind(&OfflinePageModelImpl::ClearStorageIfNeeded, | |
1128 weak_ptr_factory_.GetWeakPtr(), | |
1129 base::Bind(&OfflinePageModelImpl::OnStorageCleared, | |
1130 weak_ptr_factory_.GetWeakPtr())), | |
1131 delay); | |
1132 } | |
1133 | |
1134 bool OfflinePageModelImpl::IsRemovedOnCacheReset( | |
1135 const OfflinePageItem& offline_page) const { | |
1136 return policy_controller_->IsRemovedOnCacheReset( | |
1137 offline_page.client_id.name_space); | |
1138 } | |
1139 | |
1140 void OfflinePageModelImpl::RunWhenLoaded(const base::Closure& task) { | |
1141 if (!is_loaded_) { | |
1142 delayed_tasks_.push_back(task); | |
1143 return; | |
1144 } | |
1145 | |
1146 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, task); | |
1147 } | |
1148 | |
1149 base::Time OfflinePageModelImpl::GetCurrentTime() const { | |
1150 return testing_clock_ ? testing_clock_->Now() : base::Time::Now(); | |
1151 } | |
1152 | |
1153 } // namespace offline_pages | |
OLD | NEW |