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

Side by Side Diff: components/offline_pages/offline_page_model_impl.cc

Issue 2489443002: Move all components/offline_pages/ files into component/offline_pages/core (Closed)
Patch Set: update Created 4 years, 1 month 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
OLDNEW
(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 const std::map<int64_t, OfflinePageItem>& offline_pages,
187 const OfflinePageItem& offline_page,
188 const base::Time& save_time) {
189 // The histogram below is an expansion of the UMA_HISTOGRAM_TIMES
190 // macro adapted to allow for a dynamically suffixed histogram name.
191 // Note: The factory creates and owns the histogram.
192 base::HistogramBase* histogram = base::Histogram::FactoryTimeGet(
193 AddHistogramSuffix(offline_page.client_id, "OfflinePages.SavePageTime"),
194 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromSeconds(10),
195 50, base::HistogramBase::kUmaTargetedHistogramFlag);
196 histogram->AddTime(save_time - offline_page.creation_time);
197
198 // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS
199 // macro adapted to allow for a dynamically suffixed histogram name.
200 // Note: The factory creates and owns the histogram.
201 // Reported as Kb between 1Kb and 10Mb.
202 histogram = base::Histogram::FactoryGet(
203 AddHistogramSuffix(offline_page.client_id, "OfflinePages.PageSize"),
204 1, 10000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
205 histogram->Add(offline_page.file_size / 1024);
206
207 if (offline_page.client_id.name_space == kDownloadNamespace) {
208 int matching_url_count;
209 base::TimeDelta time_since_most_recent_duplicate;
210 if (GetMatchingURLCountAndMostRecentCreationTime(
211 offline_pages, offline_page.client_id.name_space, offline_page.url,
212 offline_page.creation_time, &matching_url_count,
213 &time_since_most_recent_duplicate)) {
214 // Using CUSTOM_COUNTS instead of time-oriented histogram to record
215 // samples in seconds rather than milliseconds.
216 UMA_HISTOGRAM_CUSTOM_COUNTS(
217 "OfflinePages.DownloadSavedPageTimeSinceDuplicateSaved",
218 time_since_most_recent_duplicate.InSeconds(),
219 base::TimeDelta::FromSeconds(1).InSeconds(),
220 base::TimeDelta::FromDays(7).InSeconds(), 50);
221 }
222 UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.DownloadSavedPageDuplicateCount",
223 matching_url_count, 1, 20, 10);
224 }
225 }
226
227 void ReportPageHistogramsAfterDelete(
228 const std::map<int64_t, OfflinePageItem>& offline_pages,
229 const std::vector<OfflinePageItem>& deleted_pages,
230 const base::Time& delete_time) {
231 const int max_minutes = base::TimeDelta::FromDays(365).InMinutes();
232 int64_t total_size = 0;
233
234 for (const auto& page : deleted_pages) {
235 total_size += page.file_size;
236 ClientId client_id = page.client_id;
237
238 if (client_id.name_space == kDownloadNamespace) {
239 int remaining_pages_with_url;
240 GetMatchingURLCountAndMostRecentCreationTime(
241 offline_pages, page.client_id.name_space, page.url, base::Time::Max(),
242 &remaining_pages_with_url, nullptr);
243 UMA_HISTOGRAM_CUSTOM_COUNTS(
244 "OfflinePages.DownloadDeletedPageDuplicateCount",
245 remaining_pages_with_url, 1, 20, 10);
246 }
247
248 // The histograms below are an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS
249 // macro adapted to allow for a dynamically suffixed histogram name.
250 // Note: The factory creates and owns the histogram.
251 base::HistogramBase* histogram = base::Histogram::FactoryGet(
252 AddHistogramSuffix(client_id, "OfflinePages.PageLifetime"),
253 1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag);
254 histogram->Add((delete_time - page.creation_time).InMinutes());
255
256 histogram = base::Histogram::FactoryGet(
257 AddHistogramSuffix(
258 client_id, "OfflinePages.DeletePage.TimeSinceLastOpen"),
259 1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag);
260 histogram->Add((delete_time - page.last_access_time).InMinutes());
261
262 histogram = base::Histogram::FactoryGet(
263 AddHistogramSuffix(
264 client_id, "OfflinePages.DeletePage.LastOpenToCreated"),
265 1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag);
266 histogram->Add((page.last_access_time - page.creation_time).InMinutes());
267
268 // Reported as Kb between 1Kb and 10Mb.
269 histogram = base::Histogram::FactoryGet(
270 AddHistogramSuffix(client_id, "OfflinePages.DeletePage.PageSize"),
271 1, 10000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
272 histogram->Add(page.file_size / 1024);
273
274 histogram = base::Histogram::FactoryGet(
275 AddHistogramSuffix(client_id, "OfflinePages.DeletePage.AccessCount"),
276 1, 1000000, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
277 histogram->Add(page.access_count);
278 }
279
280 if (deleted_pages.size() > 1) {
281 UMA_HISTOGRAM_COUNTS("OfflinePages.BatchDelete.Count",
282 static_cast<int32_t>(deleted_pages.size()));
283 UMA_HISTOGRAM_MEMORY_KB(
284 "OfflinePages.BatchDelete.TotalPageSize", total_size / 1024);
285 }
286 }
287
288 void ReportPageHistogramsAfterAccess(const OfflinePageItem& offline_page_item,
289 const base::Time& access_time) {
290 // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS
291 // macro adapted to allow for a dynamically suffixed histogram name.
292 // Note: The factory creates and owns the histogram.
293 base::HistogramBase* histogram = base::Histogram::FactoryGet(
294 AddHistogramSuffix(
295 offline_page_item.client_id,
296 offline_page_item.access_count == 0 ?
297 "OfflinePages.FirstOpenSinceCreated" :
298 "OfflinePages.OpenSinceLastOpen"),
299 1, kMaxOpenedPageHistogramBucket.InMinutes(), 50,
300 base::HistogramBase::kUmaTargetedHistogramFlag);
301 histogram->Add(
302 (access_time - offline_page_item.last_access_time).InMinutes());
303 }
304
305 } // namespace
306
307 // protected
308 OfflinePageModelImpl::OfflinePageModelImpl()
309 : OfflinePageModel(), is_loaded_(false), weak_ptr_factory_(this) {}
310
311 OfflinePageModelImpl::OfflinePageModelImpl(
312 std::unique_ptr<OfflinePageMetadataStore> store,
313 const base::FilePath& archives_dir,
314 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
315 : store_(std::move(store)),
316 archives_dir_(archives_dir),
317 is_loaded_(false),
318 policy_controller_(new ClientPolicyController()),
319 archive_manager_(new ArchiveManager(archives_dir, task_runner)),
320 testing_clock_(nullptr),
321 weak_ptr_factory_(this) {
322 archive_manager_->EnsureArchivesDirCreated(
323 base::Bind(&OfflinePageModelImpl::OnEnsureArchivesDirCreatedDone,
324 weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now()));
325 }
326
327 OfflinePageModelImpl::~OfflinePageModelImpl() {}
328
329 void OfflinePageModelImpl::AddObserver(Observer* observer) {
330 observers_.AddObserver(observer);
331 }
332
333 void OfflinePageModelImpl::RemoveObserver(Observer* observer) {
334 observers_.RemoveObserver(observer);
335 }
336
337 void OfflinePageModelImpl::SavePage(
338 const SavePageParams& save_page_params,
339 std::unique_ptr<OfflinePageArchiver> archiver,
340 const SavePageCallback& callback) {
341 DCHECK(is_loaded_);
342
343 // Skip saving the page that is not intended to be saved, like local file
344 // page.
345 if (!OfflinePageModel::CanSaveURL(save_page_params.url)) {
346 InformSavePageDone(callback, SavePageResult::SKIPPED,
347 save_page_params.client_id, kInvalidOfflineId);
348 return;
349 }
350
351 // The web contents is not available if archiver is not created and passed.
352 if (!archiver.get()) {
353 InformSavePageDone(callback, SavePageResult::CONTENT_UNAVAILABLE,
354 save_page_params.client_id, kInvalidOfflineId);
355 return;
356 }
357
358 // If we already have an offline id, use it. If not, generate one.
359 int64_t offline_id = save_page_params.proposed_offline_id;
360 if (offline_id == kInvalidOfflineId)
361 offline_id = GenerateOfflineId();
362
363 archiver->CreateArchive(
364 archives_dir_, offline_id,
365 base::Bind(&OfflinePageModelImpl::OnCreateArchiveDone,
366 weak_ptr_factory_.GetWeakPtr(), save_page_params, offline_id,
367 GetCurrentTime(), callback));
368 pending_archivers_.push_back(std::move(archiver));
369 }
370
371 void OfflinePageModelImpl::MarkPageAccessed(int64_t offline_id) {
372 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::MarkPageAccessedWhenLoadDone,
373 weak_ptr_factory_.GetWeakPtr(), offline_id));
374 }
375
376 void OfflinePageModelImpl::MarkPageAccessedWhenLoadDone(int64_t offline_id) {
377 DCHECK(is_loaded_);
378
379 auto iter = offline_pages_.find(offline_id);
380 if (iter == offline_pages_.end() || iter->second.IsExpired())
381 return;
382
383 // Make a copy of the cached item and update it. The cached item should only
384 // be updated upon the successful store operation.
385 OfflinePageItem offline_page_item = iter->second;
386
387 ReportPageHistogramsAfterAccess(offline_page_item, GetCurrentTime());
388
389 offline_page_item.last_access_time = GetCurrentTime();
390 offline_page_item.access_count++;
391
392 std::vector<OfflinePageItem> items = { offline_page_item };
393 store_->UpdateOfflinePages(
394 items, base::Bind(&OfflinePageModelImpl::OnMarkPageAccesseDone,
395 weak_ptr_factory_.GetWeakPtr(), offline_page_item));
396 }
397
398 void OfflinePageModelImpl::DeletePagesByOfflineId(
399 const std::vector<int64_t>& offline_ids,
400 const DeletePageCallback& callback) {
401 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::DoDeletePagesByOfflineId,
402 weak_ptr_factory_.GetWeakPtr(), offline_ids,
403 callback));
404 }
405
406 void OfflinePageModelImpl::DoDeletePagesByOfflineId(
407 const std::vector<int64_t>& offline_ids,
408 const DeletePageCallback& callback) {
409 DCHECK(is_loaded_);
410
411 std::vector<base::FilePath> paths_to_delete;
412 for (const auto& offline_id : offline_ids) {
413 auto iter = offline_pages_.find(offline_id);
414 if (iter != offline_pages_.end() && !iter->second.IsExpired()) {
415 paths_to_delete.push_back(iter->second.file_path);
416 }
417 }
418
419 // If there're no pages to delete, return early.
420 if (paths_to_delete.empty()) {
421 InformDeletePageDone(callback, DeletePageResult::SUCCESS);
422 return;
423 }
424
425 archive_manager_->DeleteMultipleArchives(
426 paths_to_delete,
427 base::Bind(&OfflinePageModelImpl::OnDeleteArchiveFilesDone,
428 weak_ptr_factory_.GetWeakPtr(), offline_ids, callback));
429 }
430
431 void OfflinePageModelImpl::DeletePagesByClientIds(
432 const std::vector<ClientId>& client_ids,
433 const DeletePageCallback& callback) {
434 OfflinePageModelQueryBuilder builder;
435 builder
436 .SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
437 client_ids)
438 .AllowExpiredPages(true);
439 auto delete_pages = base::Bind(&OfflinePageModelImpl::DeletePages,
440 weak_ptr_factory_.GetWeakPtr(), callback);
441 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery,
442 weak_ptr_factory_.GetWeakPtr(),
443 base::Passed(builder.Build(GetPolicyController())),
444 delete_pages));
445 }
446
447 void OfflinePageModelImpl::DeletePages(
448 const DeletePageCallback& callback,
449 const MultipleOfflinePageItemResult& pages) {
450 DCHECK(is_loaded_);
451
452 std::vector<int64_t> offline_ids;
453 for (auto& page : pages)
454 offline_ids.emplace_back(page.offline_id);
455
456 DoDeletePagesByOfflineId(offline_ids, callback);
457 }
458
459 void OfflinePageModelImpl::GetPagesByClientIds(
460 const std::vector<ClientId>& client_ids,
461 const MultipleOfflinePageItemCallback& callback) {
462 OfflinePageModelQueryBuilder builder;
463 builder.SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
464 client_ids);
465 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery,
466 weak_ptr_factory_.GetWeakPtr(),
467 base::Passed(builder.Build(GetPolicyController())),
468 callback));
469 }
470
471 void OfflinePageModelImpl::DeleteCachedPagesByURLPredicate(
472 const UrlPredicate& predicate,
473 const DeletePageCallback& callback) {
474 RunWhenLoaded(
475 base::Bind(&OfflinePageModelImpl::DoDeleteCachedPagesByURLPredicate,
476 weak_ptr_factory_.GetWeakPtr(), predicate, callback));
477 }
478
479 void OfflinePageModelImpl::DoDeleteCachedPagesByURLPredicate(
480 const UrlPredicate& predicate,
481 const DeletePageCallback& callback) {
482 DCHECK(is_loaded_);
483
484 std::vector<int64_t> offline_ids;
485 for (const auto& id_page_pair : offline_pages_) {
486 if (IsRemovedOnCacheReset(id_page_pair.second) &&
487 predicate.Run(id_page_pair.second.url)) {
488 offline_ids.push_back(id_page_pair.first);
489 }
490 }
491 DoDeletePagesByOfflineId(offline_ids, callback);
492 }
493
494 void OfflinePageModelImpl::CheckPagesExistOffline(
495 const std::set<GURL>& urls,
496 const CheckPagesExistOfflineCallback& callback) {
497 OfflinePageModelQueryBuilder builder;
498 builder
499 .SetUrls(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
500 std::vector<GURL>(urls.begin(), urls.end()))
501 .RequireRestrictedToOriginalTab(
502 OfflinePageModelQueryBuilder::Requirement::EXCLUDE_MATCHING);
503 auto pages_to_urls = base::Bind(
504 [](const CheckPagesExistOfflineCallback& callback,
505 const MultipleOfflinePageItemResult& pages) {
506 CheckPagesExistOfflineResult result;
507 for (auto& page : pages)
508 result.insert(page.url);
509 callback.Run(result);
510 },
511 callback);
512 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery,
513 weak_ptr_factory_.GetWeakPtr(),
514 base::Passed(builder.Build(GetPolicyController())),
515 pages_to_urls));
516 }
517
518 void OfflinePageModelImpl::GetPagesMatchingQuery(
519 std::unique_ptr<OfflinePageModelQuery> query,
520 const MultipleOfflinePageItemCallback& callback) {
521 DCHECK(query);
522
523 MultipleOfflinePageItemResult offline_pages_result;
524
525 for (const auto& id_page_pair : offline_pages_) {
526 if (query->Matches(id_page_pair.second))
527 offline_pages_result.emplace_back(id_page_pair.second);
528 }
529
530 callback.Run(offline_pages_result);
531 }
532
533 void OfflinePageModelImpl::GetAllPages(
534 const MultipleOfflinePageItemCallback& callback) {
535 OfflinePageModelQueryBuilder builder;
536 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery,
537 weak_ptr_factory_.GetWeakPtr(),
538 base::Passed(builder.Build(GetPolicyController())),
539 callback));
540 }
541
542 void OfflinePageModelImpl::GetAllPagesWithExpired(
543 const MultipleOfflinePageItemCallback& callback) {
544 OfflinePageModelQueryBuilder builder;
545 builder.AllowExpiredPages(true);
546
547 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery,
548 weak_ptr_factory_.GetWeakPtr(),
549 base::Passed(builder.Build(GetPolicyController())),
550 callback));
551 }
552
553 void OfflinePageModelImpl::GetOfflineIdsForClientId(
554 const ClientId& client_id,
555 const MultipleOfflineIdCallback& callback) {
556 RunWhenLoaded(
557 base::Bind(&OfflinePageModelImpl::GetOfflineIdsForClientIdWhenLoadDone,
558 weak_ptr_factory_.GetWeakPtr(), client_id, callback));
559 }
560
561 void OfflinePageModelImpl::GetOfflineIdsForClientIdWhenLoadDone(
562 const ClientId& client_id,
563 const MultipleOfflineIdCallback& callback) const {
564 callback.Run(MaybeGetOfflineIdsForClientId(client_id));
565 }
566
567 const std::vector<int64_t> OfflinePageModelImpl::MaybeGetOfflineIdsForClientId(
568 const ClientId& client_id) const {
569 DCHECK(is_loaded_);
570 std::vector<int64_t> results;
571
572 // We want only all pages, including those marked for deletion.
573 // TODO(fgorski): actually use an index rather than linear scan.
574 for (const auto& id_page_pair : offline_pages_) {
575 if (id_page_pair.second.client_id == client_id &&
576 !id_page_pair.second.IsExpired()) {
577 results.push_back(id_page_pair.second.offline_id);
578 }
579 }
580 return results;
581 }
582
583 void OfflinePageModelImpl::GetPageByOfflineId(
584 int64_t offline_id,
585 const SingleOfflinePageItemCallback& callback) {
586 std::vector<int64_t> query_ids;
587 query_ids.emplace_back(offline_id);
588
589 OfflinePageModelQueryBuilder builder;
590 builder.SetOfflinePageIds(
591 OfflinePageModelQuery::Requirement::INCLUDE_MATCHING, query_ids);
592
593 auto multiple_callback = base::Bind(
594 [](const SingleOfflinePageItemCallback& callback,
595 const MultipleOfflinePageItemResult& result) {
596 DCHECK_LE(result.size(), 1U);
597 if (result.empty()) {
598 callback.Run(nullptr);
599 } else {
600 callback.Run(&result[0]);
601 }
602 },
603 callback);
604
605 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::GetPagesMatchingQuery,
606 weak_ptr_factory_.GetWeakPtr(),
607 base::Passed(builder.Build(GetPolicyController())),
608 multiple_callback));
609 }
610
611 void OfflinePageModelImpl::GetPagesByOnlineURL(
612 const GURL& online_url,
613 const MultipleOfflinePageItemCallback& callback) {
614 RunWhenLoaded(
615 base::Bind(&OfflinePageModelImpl::GetPagesByOnlineURLWhenLoadDone,
616 weak_ptr_factory_.GetWeakPtr(), online_url, callback));
617 }
618
619 void OfflinePageModelImpl::GetPagesByOnlineURLWhenLoadDone(
620 const GURL& online_url,
621 const MultipleOfflinePageItemCallback& callback) const {
622 std::vector<OfflinePageItem> result;
623
624 GURL::Replacements remove_params;
625 remove_params.ClearRef();
626
627 GURL online_url_without_fragment =
628 online_url.ReplaceComponents(remove_params);
629
630 for (const auto& id_page_pair : offline_pages_) {
631 if (id_page_pair.second.IsExpired())
632 continue;
633 if (online_url == id_page_pair.second.url) {
634 result.push_back(id_page_pair.second);
635 continue;
636 }
637 // If the full URL does not match, try with the fragment identifier
638 // stripped.
639 if (online_url_without_fragment ==
640 id_page_pair.second.url.ReplaceComponents(remove_params)) {
641 result.push_back(id_page_pair.second);
642 }
643 }
644
645 callback.Run(result);
646 }
647
648 void OfflinePageModelImpl::CheckMetadataConsistency() {
649 DCHECK(is_loaded_);
650 archive_manager_->GetAllArchives(
651 base::Bind(&OfflinePageModelImpl::CheckMetadataConsistencyForArchivePaths,
652 weak_ptr_factory_.GetWeakPtr()));
653 }
654
655 void OfflinePageModelImpl::ExpirePages(
656 const std::vector<int64_t>& offline_ids,
657 const base::Time& expiration_time,
658 const base::Callback<void(bool)>& callback) {
659 std::vector<base::FilePath> paths_to_delete;
660 std::vector<OfflinePageItem> items_to_update;
661 for (int64_t offline_id : offline_ids) {
662 auto iter = offline_pages_.find(offline_id);
663 if (iter == offline_pages_.end())
664 continue;
665
666 OfflinePageItem offline_page = iter->second;
667 paths_to_delete.push_back(offline_page.file_path);
668 offline_page.expiration_time = expiration_time;
669
670 items_to_update.push_back(offline_page);
671 }
672
673 store_->UpdateOfflinePages(
674 items_to_update,
675 base::Bind(&OfflinePageModelImpl::OnExpirePageDone,
676 weak_ptr_factory_.GetWeakPtr(), expiration_time));
677
678 if (paths_to_delete.empty()) {
679 callback.Run(true);
680 return;
681 }
682 archive_manager_->DeleteMultipleArchives(paths_to_delete, callback);
683 }
684
685 void OfflinePageModelImpl::OnExpirePageDone(
686 const base::Time& expiration_time,
687 std::unique_ptr<OfflinePagesUpdateResult> result) {
688 UMA_HISTOGRAM_BOOLEAN("OfflinePages.ExpirePage.StoreUpdateResult",
689 result->updated_items.size() > 0);
690 for (const auto& expired_page : result->updated_items) {
691 const auto& iter = offline_pages_.find(expired_page.offline_id);
692 if (iter == offline_pages_.end())
693 continue;
694
695 iter->second.expiration_time = expiration_time;
696 ClientId client_id = iter->second.client_id;
697 offline_event_logger_.RecordPageExpired(
698 std::to_string(expired_page.offline_id));
699 base::HistogramBase* histogram = base::Histogram::FactoryGet(
700 AddHistogramSuffix(client_id, "OfflinePages.ExpirePage.PageLifetime"),
701 1, base::TimeDelta::FromDays(30).InMinutes(), 50,
702 base::HistogramBase::kUmaTargetedHistogramFlag);
703 histogram->Add((expiration_time - iter->second.creation_time).InMinutes());
704 histogram = base::Histogram::FactoryGet(
705 AddHistogramSuffix(client_id,
706 "OfflinePages.ExpirePage.TimeSinceLastAccess"),
707 1, base::TimeDelta::FromDays(30).InMinutes(), 50,
708 base::HistogramBase::kUmaTargetedHistogramFlag);
709 histogram->Add(
710 (expiration_time - iter->second.last_access_time).InMinutes());
711 }
712 }
713
714 ClientPolicyController* OfflinePageModelImpl::GetPolicyController() {
715 return policy_controller_.get();
716 }
717
718 OfflinePageMetadataStore* OfflinePageModelImpl::GetStoreForTesting() {
719 return store_.get();
720 }
721
722 OfflinePageStorageManager* OfflinePageModelImpl::GetStorageManager() {
723 return storage_manager_.get();
724 }
725
726 bool OfflinePageModelImpl::is_loaded() const {
727 return is_loaded_;
728 }
729
730 OfflineEventLogger* OfflinePageModelImpl::GetLogger() {
731 return &offline_event_logger_;
732 }
733
734 void OfflinePageModelImpl::OnCreateArchiveDone(
735 const SavePageParams& save_page_params,
736 int64_t offline_id,
737 const base::Time& start_time,
738 const SavePageCallback& callback,
739 OfflinePageArchiver* archiver,
740 ArchiverResult archiver_result,
741 const GURL& url,
742 const base::FilePath& file_path,
743 const base::string16& title,
744 int64_t file_size) {
745 if (save_page_params.url != url) {
746 DVLOG(1) << "Saved URL does not match requested URL.";
747 // TODO(fgorski): We have created an archive for a wrong URL. It should be
748 // deleted from here, once archiver has the right functionality.
749 InformSavePageDone(callback, SavePageResult::ARCHIVE_CREATION_FAILED,
750 save_page_params.client_id, offline_id);
751 DeletePendingArchiver(archiver);
752 return;
753 }
754
755 if (archiver_result != ArchiverResult::SUCCESSFULLY_CREATED) {
756 SavePageResult result = ToSavePageResult(archiver_result);
757 InformSavePageDone(
758 callback, result, save_page_params.client_id, offline_id);
759 DeletePendingArchiver(archiver);
760 return;
761 }
762 OfflinePageItem offline_page_item(url, offline_id, save_page_params.client_id,
763 file_path, file_size, start_time);
764 offline_page_item.title = title;
765 offline_page_item.original_url = save_page_params.original_url;
766 store_->AddOfflinePage(offline_page_item,
767 base::Bind(&OfflinePageModelImpl::OnAddOfflinePageDone,
768 weak_ptr_factory_.GetWeakPtr(), archiver,
769 callback, offline_page_item));
770 }
771
772 void OfflinePageModelImpl::OnAddOfflinePageDone(
773 OfflinePageArchiver* archiver,
774 const SavePageCallback& callback,
775 const OfflinePageItem& offline_page,
776 ItemActionStatus status) {
777 SavePageResult result;
778 if (status == ItemActionStatus::SUCCESS) {
779 offline_pages_[offline_page.offline_id] = offline_page;
780 result = SavePageResult::SUCCESS;
781 ReportPageHistogramAfterSave(offline_pages_, offline_page,
782 GetCurrentTime());
783 offline_event_logger_.RecordPageSaved(
784 offline_page.client_id.name_space, offline_page.url.spec(),
785 std::to_string(offline_page.offline_id));
786 } else if (status == ItemActionStatus::ALREADY_EXISTS) {
787 result = SavePageResult::ALREADY_EXISTS;
788 } else {
789 result = SavePageResult::STORE_FAILURE;
790 }
791 InformSavePageDone(callback, result, offline_page.client_id,
792 offline_page.offline_id);
793 if (result == SavePageResult::SUCCESS) {
794 DeleteExistingPagesWithSameURL(offline_page);
795 } else {
796 PostClearStorageIfNeededTask();
797 }
798
799 DeletePendingArchiver(archiver);
800 for (Observer& observer : observers_)
801 observer.OfflinePageModelChanged(this);
802 }
803
804 void OfflinePageModelImpl::OnMarkPageAccesseDone(
805 const OfflinePageItem& offline_page_item,
806 std::unique_ptr<OfflinePagesUpdateResult> result) {
807 // Update the item in the cache only upon success.
808 if (result->updated_items.size() > 0)
809 offline_pages_[offline_page_item.offline_id] = offline_page_item;
810
811 // No need to fire OfflinePageModelChanged event since updating access info
812 // should not have any impact to the UI.
813 }
814
815 void OfflinePageModelImpl::OnEnsureArchivesDirCreatedDone(
816 const base::TimeTicks& start_time) {
817 UMA_HISTOGRAM_TIMES("OfflinePages.Model.ArchiveDirCreationTime",
818 base::TimeTicks::Now() - start_time);
819
820 store_->GetOfflinePages(base::Bind(&OfflinePageModelImpl::OnLoadDone,
821 weak_ptr_factory_.GetWeakPtr(),
822 start_time));
823 }
824
825 void OfflinePageModelImpl::OnLoadDone(
826 const base::TimeTicks& start_time,
827 OfflinePageMetadataStore::LoadStatus load_status,
828 const std::vector<OfflinePageItem>& offline_pages) {
829 DCHECK(!is_loaded_);
830 is_loaded_ = true;
831
832 // TODO(jianli): rebuild the store upon failure.
833
834 if (load_status == OfflinePageMetadataStore::LOAD_SUCCEEDED)
835 CacheLoadedData(offline_pages);
836
837 UMA_HISTOGRAM_TIMES("OfflinePages.Model.ConstructionToLoadedEventTime",
838 base::TimeTicks::Now() - start_time);
839
840 // Create Storage Manager.
841 storage_manager_.reset(new OfflinePageStorageManager(
842 this, GetPolicyController(), archive_manager_.get()));
843
844 // Run all the delayed tasks.
845 for (const auto& delayed_task : delayed_tasks_)
846 delayed_task.Run();
847 delayed_tasks_.clear();
848
849 for (Observer& observer : observers_)
850 observer.OfflinePageModelLoaded(this);
851
852 CheckMetadataConsistency();
853
854 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
855 FROM_HERE, base::Bind(&OfflinePageModelImpl::ClearStorageIfNeeded,
856 weak_ptr_factory_.GetWeakPtr(),
857 base::Bind(&OfflinePageModelImpl::OnStorageCleared,
858 weak_ptr_factory_.GetWeakPtr())),
859 kStorageManagerStartingDelay);
860 }
861
862 void OfflinePageModelImpl::InformSavePageDone(const SavePageCallback& callback,
863 SavePageResult result,
864 const ClientId& client_id,
865 int64_t offline_id) {
866 ReportSavePageResultHistogramAfterSave(client_id, result);
867 archive_manager_->GetStorageStats(
868 base::Bind(&ReportStorageHistogramsAfterSave));
869 callback.Run(result, offline_id);
870 }
871
872 void OfflinePageModelImpl::DeleteExistingPagesWithSameURL(
873 const OfflinePageItem& offline_page) {
874 // Remove existing pages generated by the same policy and with same url.
875 size_t pages_allowed =
876 policy_controller_->GetPolicy(offline_page.client_id.name_space)
877 .pages_allowed_per_url;
878 if (pages_allowed == kUnlimitedPages)
879 return;
880 GetPagesByOnlineURL(
881 offline_page.url,
882 base::Bind(&OfflinePageModelImpl::OnPagesFoundWithSameURL,
883 weak_ptr_factory_.GetWeakPtr(), offline_page, pages_allowed));
884 }
885
886 void OfflinePageModelImpl::OnPagesFoundWithSameURL(
887 const OfflinePageItem& offline_page,
888 size_t pages_allowed,
889 const MultipleOfflinePageItemResult& items) {
890 std::vector<OfflinePageItem> pages_to_delete;
891 for (const auto& item : items) {
892 if (item.offline_id != offline_page.offline_id &&
893 item.client_id.name_space == offline_page.client_id.name_space) {
894 pages_to_delete.push_back(item);
895 }
896 }
897 // Only keep |pages_allowed|-1 of most fresh pages and delete others, by
898 // sorting pages with the oldest ones first and resize the vector.
899 if (pages_to_delete.size() >= pages_allowed) {
900 sort(pages_to_delete.begin(), pages_to_delete.end(),
901 [](const OfflinePageItem& a, const OfflinePageItem& b) -> bool {
902 return a.last_access_time < b.last_access_time;
903 });
904 pages_to_delete.resize(pages_to_delete.size() - pages_allowed + 1);
905 }
906 std::vector<int64_t> page_ids_to_delete;
907 for (const auto& item : pages_to_delete)
908 page_ids_to_delete.push_back(item.offline_id);
909 DeletePagesByOfflineId(
910 page_ids_to_delete,
911 base::Bind(&OfflinePageModelImpl::OnDeleteOldPagesWithSameURL,
912 weak_ptr_factory_.GetWeakPtr()));
913 }
914
915 void OfflinePageModelImpl::OnDeleteOldPagesWithSameURL(
916 DeletePageResult result) {
917 // TODO(romax) Add UMAs for failure cases.
918 PostClearStorageIfNeededTask();
919 }
920
921 void OfflinePageModelImpl::DeletePendingArchiver(
922 OfflinePageArchiver* archiver) {
923 pending_archivers_.erase(std::find(pending_archivers_.begin(),
924 pending_archivers_.end(), archiver));
925 }
926
927 void OfflinePageModelImpl::OnDeleteArchiveFilesDone(
928 const std::vector<int64_t>& offline_ids,
929 const DeletePageCallback& callback,
930 bool success) {
931 if (!success) {
932 InformDeletePageDone(callback, DeletePageResult::DEVICE_FAILURE);
933 return;
934 }
935
936 store_->RemoveOfflinePages(
937 offline_ids, base::Bind(&OfflinePageModelImpl::OnRemoveOfflinePagesDone,
938 weak_ptr_factory_.GetWeakPtr(), callback));
939 }
940
941 void OfflinePageModelImpl::OnRemoveOfflinePagesDone(
942 const DeletePageCallback& callback,
943 std::unique_ptr<OfflinePagesUpdateResult> result) {
944 ReportPageHistogramsAfterDelete(offline_pages_, result->updated_items,
945 GetCurrentTime());
946
947 // This part of the loop is explicitly broken out, as it should be gone in
948 // fully asynchronous code.
949 for (const auto& page : result->updated_items) {
950 int64_t offline_id = page.offline_id;
951 offline_event_logger_.RecordPageDeleted(std::to_string(offline_id));
952 auto iter = offline_pages_.find(offline_id);
953 if (iter == offline_pages_.end())
954 continue;
955 offline_pages_.erase(iter);
956 }
957
958 for (const auto& page : result->updated_items) {
959 for (Observer& observer : observers_)
960 observer.OfflinePageDeleted(page.offline_id, page.client_id);
961 }
962
963 // TODO(fgorski): React the FAILED_INITIALIZATION, FAILED_RESET here.
964 // TODO(fgorski): We need a better callback interface for the Remove action on
965 // the this class. Currently removing an item that does not exist is
966 // considered a success, but not called out as such to the caller.
967 DeletePageResult delete_result;
968 if (result->store_state == StoreState::LOADED)
969 delete_result = DeletePageResult::SUCCESS;
970 else
971 delete_result = DeletePageResult::STORE_FAILURE;
972
973 InformDeletePageDone(callback, delete_result);
974 }
975
976 void OfflinePageModelImpl::InformDeletePageDone(
977 const DeletePageCallback& callback,
978 DeletePageResult result) {
979 UMA_HISTOGRAM_ENUMERATION("OfflinePages.DeletePageResult",
980 static_cast<int>(result),
981 static_cast<int>(DeletePageResult::RESULT_COUNT));
982 archive_manager_->GetStorageStats(
983 base::Bind(&ReportStorageHistogramsAfterDelete));
984 if (!callback.is_null())
985 callback.Run(result);
986 }
987
988 void OfflinePageModelImpl::CheckMetadataConsistencyForArchivePaths(
989 const std::set<base::FilePath>& archive_paths) {
990 ExpirePagesMissingArchiveFile(archive_paths);
991 DeleteOrphanedArchives(archive_paths);
992 }
993
994 void OfflinePageModelImpl::ExpirePagesMissingArchiveFile(
995 const std::set<base::FilePath>& archive_paths) {
996 std::vector<int64_t> ids_of_pages_missing_archive_file;
997 for (const auto& id_page_pair : offline_pages_) {
998 if (archive_paths.count(id_page_pair.second.file_path) == 0UL)
999 ids_of_pages_missing_archive_file.push_back(id_page_pair.first);
1000 }
1001
1002 if (ids_of_pages_missing_archive_file.empty())
1003 return;
1004
1005 ExpirePages(
1006 ids_of_pages_missing_archive_file, GetCurrentTime(),
1007 base::Bind(&OfflinePageModelImpl::OnExpirePagesMissingArchiveFileDone,
1008 weak_ptr_factory_.GetWeakPtr(),
1009 ids_of_pages_missing_archive_file));
1010 }
1011
1012 void OfflinePageModelImpl::OnExpirePagesMissingArchiveFileDone(
1013 const std::vector<int64_t>& offline_ids,
1014 bool success) {
1015 UMA_HISTOGRAM_COUNTS("OfflinePages.Consistency.PagesMissingArchiveFileCount",
1016 static_cast<int32_t>(offline_ids.size()));
1017 UMA_HISTOGRAM_BOOLEAN(
1018 "OfflinePages.Consistency.ExpirePagesMissingArchiveFileResult", success);
1019 }
1020
1021 void OfflinePageModelImpl::DeleteOrphanedArchives(
1022 const std::set<base::FilePath>& archive_paths) {
1023 // Archives are considered orphaned unless they are pointed to by some pages.
1024 std::set<base::FilePath> orphaned_archive_set(archive_paths);
1025 for (const auto& id_page_pair : offline_pages_)
1026 orphaned_archive_set.erase(id_page_pair.second.file_path);
1027
1028 if (orphaned_archive_set.empty())
1029 return;
1030
1031 std::vector<base::FilePath> orphaned_archives(orphaned_archive_set.begin(),
1032 orphaned_archive_set.end());
1033 archive_manager_->DeleteMultipleArchives(
1034 orphaned_archives,
1035 base::Bind(&OfflinePageModelImpl::OnDeleteOrphanedArchivesDone,
1036 weak_ptr_factory_.GetWeakPtr(), orphaned_archives));
1037 }
1038
1039 void OfflinePageModelImpl::OnDeleteOrphanedArchivesDone(
1040 const std::vector<base::FilePath>& archives,
1041 bool success) {
1042 UMA_HISTOGRAM_COUNTS("OfflinePages.Consistency.OrphanedArchivesCount",
1043 static_cast<int32_t>(archives.size()));
1044 UMA_HISTOGRAM_BOOLEAN("OfflinePages.Consistency.DeleteOrphanedArchivesResult",
1045 success);
1046 }
1047
1048 void OfflinePageModelImpl::CacheLoadedData(
1049 const std::vector<OfflinePageItem>& offline_pages) {
1050 offline_pages_.clear();
1051 for (const auto& offline_page : offline_pages)
1052 offline_pages_[offline_page.offline_id] = offline_page;
1053 }
1054
1055 void OfflinePageModelImpl::ClearStorageIfNeeded(
1056 const ClearStorageCallback& callback) {
1057 storage_manager_->ClearPagesIfNeeded(callback);
1058 }
1059
1060 void OfflinePageModelImpl::OnStorageCleared(size_t expired_page_count,
1061 ClearStorageResult result) {
1062 UMA_HISTOGRAM_ENUMERATION("OfflinePages.ClearStorageResult",
1063 static_cast<int>(result),
1064 static_cast<int>(ClearStorageResult::RESULT_COUNT));
1065 if (expired_page_count > 0) {
1066 UMA_HISTOGRAM_COUNTS("OfflinePages.ExpirePage.BatchSize",
1067 static_cast<int32_t>(expired_page_count));
1068 }
1069 }
1070
1071 void OfflinePageModelImpl::PostClearStorageIfNeededTask() {
1072 base::ThreadTaskRunnerHandle::Get()->PostTask(
1073 FROM_HERE, base::Bind(&OfflinePageModelImpl::ClearStorageIfNeeded,
1074 weak_ptr_factory_.GetWeakPtr(),
1075 base::Bind(&OfflinePageModelImpl::OnStorageCleared,
1076 weak_ptr_factory_.GetWeakPtr())));
1077 }
1078
1079 bool OfflinePageModelImpl::IsRemovedOnCacheReset(
1080 const OfflinePageItem& offline_page) const {
1081 return policy_controller_->IsRemovedOnCacheReset(
1082 offline_page.client_id.name_space);
1083 }
1084
1085 void OfflinePageModelImpl::RunWhenLoaded(const base::Closure& task) {
1086 if (!is_loaded_) {
1087 delayed_tasks_.push_back(task);
1088 return;
1089 }
1090
1091 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, task);
1092 }
1093
1094 base::Time OfflinePageModelImpl::GetCurrentTime() const {
1095 return testing_clock_ ? testing_clock_->Now() : base::Time::Now();
1096 }
1097
1098 } // namespace offline_pages
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698