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

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: rebase Created 4 years 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 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())
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()) {
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.SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
439 client_ids);
440 auto delete_pages = base::Bind(&OfflinePageModelImpl::DeletePages,
441 weak_ptr_factory_.GetWeakPtr(), callback);
442 RunWhenLoaded(base::Bind(
443 &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
444 weak_ptr_factory_.GetWeakPtr(),
445 base::Passed(builder.Build(GetPolicyController())), delete_pages));
446 }
447
448 void OfflinePageModelImpl::DeletePages(
449 const DeletePageCallback& callback,
450 const MultipleOfflinePageItemResult& pages) {
451 DCHECK(is_loaded_);
452
453 std::vector<int64_t> offline_ids;
454 for (auto& page : pages)
455 offline_ids.emplace_back(page.offline_id);
456
457 DoDeletePagesByOfflineId(offline_ids, callback);
458 }
459
460 void OfflinePageModelImpl::GetPagesMatchingQuery(
461 std::unique_ptr<OfflinePageModelQuery> query,
462 const MultipleOfflinePageItemCallback& callback) {
463 RunWhenLoaded(base::Bind(
464 &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
465 weak_ptr_factory_.GetWeakPtr(), base::Passed(&query), callback));
466 }
467
468 void OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone(
469 std::unique_ptr<OfflinePageModelQuery> query,
470 const MultipleOfflinePageItemCallback& callback) {
471 DCHECK(query);
472 DCHECK(is_loaded_);
473
474 MultipleOfflinePageItemResult offline_pages_result;
475
476 for (const auto& id_page_pair : offline_pages_) {
477 if (query->Matches(id_page_pair.second))
478 offline_pages_result.emplace_back(id_page_pair.second);
479 }
480
481 callback.Run(offline_pages_result);
482 }
483
484 void OfflinePageModelImpl::GetPagesByClientIds(
485 const std::vector<ClientId>& client_ids,
486 const MultipleOfflinePageItemCallback& callback) {
487 OfflinePageModelQueryBuilder builder;
488 builder.SetClientIds(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
489 client_ids);
490 RunWhenLoaded(
491 base::Bind(&OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
492 weak_ptr_factory_.GetWeakPtr(),
493 base::Passed(builder.Build(GetPolicyController())), callback));
494 }
495
496 void OfflinePageModelImpl::DeleteCachedPagesByURLPredicate(
497 const UrlPredicate& predicate,
498 const DeletePageCallback& callback) {
499 RunWhenLoaded(
500 base::Bind(&OfflinePageModelImpl::DoDeleteCachedPagesByURLPredicate,
501 weak_ptr_factory_.GetWeakPtr(), predicate, callback));
502 }
503
504 void OfflinePageModelImpl::DoDeleteCachedPagesByURLPredicate(
505 const UrlPredicate& predicate,
506 const DeletePageCallback& callback) {
507 DCHECK(is_loaded_);
508
509 std::vector<int64_t> offline_ids;
510 for (const auto& id_page_pair : offline_pages_) {
511 if (IsRemovedOnCacheReset(id_page_pair.second) &&
512 predicate.Run(id_page_pair.second.url)) {
513 offline_ids.push_back(id_page_pair.first);
514 }
515 }
516 DoDeletePagesByOfflineId(offline_ids, callback);
517 }
518
519 void OfflinePageModelImpl::CheckPagesExistOffline(
520 const std::set<GURL>& urls,
521 const CheckPagesExistOfflineCallback& callback) {
522 OfflinePageModelQueryBuilder builder;
523 builder
524 .SetUrls(OfflinePageModelQuery::Requirement::INCLUDE_MATCHING,
525 std::vector<GURL>(urls.begin(), urls.end()))
526 .RequireRestrictedToOriginalTab(
527 OfflinePageModelQueryBuilder::Requirement::EXCLUDE_MATCHING);
528 auto pages_to_urls = base::Bind(
529 [](const CheckPagesExistOfflineCallback& callback,
530 const MultipleOfflinePageItemResult& pages) {
531 CheckPagesExistOfflineResult result;
532 for (auto& page : pages)
533 result.insert(page.url);
534 callback.Run(result);
535 },
536 callback);
537 RunWhenLoaded(base::Bind(
538 &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
539 weak_ptr_factory_.GetWeakPtr(),
540 base::Passed(builder.Build(GetPolicyController())), pages_to_urls));
541 }
542
543 void OfflinePageModelImpl::GetAllPages(
544 const MultipleOfflinePageItemCallback& callback) {
545 OfflinePageModelQueryBuilder builder;
546 RunWhenLoaded(
547 base::Bind(&OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
548 weak_ptr_factory_.GetWeakPtr(),
549 base::Passed(builder.Build(GetPolicyController())), callback));
550 }
551
552 void OfflinePageModelImpl::GetOfflineIdsForClientId(
553 const ClientId& client_id,
554 const MultipleOfflineIdCallback& callback) {
555 RunWhenLoaded(
556 base::Bind(&OfflinePageModelImpl::GetOfflineIdsForClientIdWhenLoadDone,
557 weak_ptr_factory_.GetWeakPtr(), client_id, callback));
558 }
559
560 void OfflinePageModelImpl::GetOfflineIdsForClientIdWhenLoadDone(
561 const ClientId& client_id,
562 const MultipleOfflineIdCallback& callback) const {
563 DCHECK(is_loaded_);
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 results.push_back(id_page_pair.second.offline_id);
577 }
578 return results;
579 }
580
581 void OfflinePageModelImpl::GetPageByOfflineId(
582 int64_t offline_id,
583 const SingleOfflinePageItemCallback& callback) {
584 std::vector<int64_t> query_ids;
585 query_ids.emplace_back(offline_id);
586
587 OfflinePageModelQueryBuilder builder;
588 builder.SetOfflinePageIds(
589 OfflinePageModelQuery::Requirement::INCLUDE_MATCHING, query_ids);
590
591 auto multiple_callback = base::Bind(
592 [](const SingleOfflinePageItemCallback& callback,
593 const MultipleOfflinePageItemResult& result) {
594 DCHECK_LE(result.size(), 1U);
595 if (result.empty()) {
596 callback.Run(nullptr);
597 } else {
598 callback.Run(&result[0]);
599 }
600 },
601 callback);
602
603 RunWhenLoaded(base::Bind(
604 &OfflinePageModelImpl::GetPagesMatchingQueryWhenLoadDone,
605 weak_ptr_factory_.GetWeakPtr(),
606 base::Passed(builder.Build(GetPolicyController())), multiple_callback));
607 }
608
609 void OfflinePageModelImpl::GetPagesByURL(
610 const GURL& url,
611 URLSearchMode url_search_mode,
612 const MultipleOfflinePageItemCallback& callback) {
613 RunWhenLoaded(
614 base::Bind(&OfflinePageModelImpl::GetPagesByURLWhenLoadDone,
615 weak_ptr_factory_.GetWeakPtr(), url,
616 url_search_mode, callback));
617 }
618
619 void OfflinePageModelImpl::GetPagesByURLWhenLoadDone(
620 const GURL& url,
621 URLSearchMode url_search_mode,
622 const MultipleOfflinePageItemCallback& callback) const {
623 DCHECK(is_loaded_);
624 std::vector<OfflinePageItem> result;
625
626 GURL::Replacements remove_params;
627 remove_params.ClearRef();
628
629 GURL url_without_fragment =
630 url.ReplaceComponents(remove_params);
631
632 for (const auto& id_page_pair : offline_pages_) {
633 // First, search by last committed URL with fragment stripped.
634 if (url_without_fragment ==
635 id_page_pair.second.url.ReplaceComponents(remove_params)) {
636 result.push_back(id_page_pair.second);
637 continue;
638 }
639 // Then, search by original request URL if |url_search_mode| wants it.
640 // Note that we want to do the exact match with fragment included. This is
641 // because original URL is used for redirect purpose and it is always safer
642 // to support the exact redirect.
643 if (url_search_mode == URLSearchMode::SEARCH_BY_ALL_URLS &&
644 url == id_page_pair.second.original_url) {
645 result.push_back(id_page_pair.second);
646 }
647 }
648
649 callback.Run(result);
650 }
651
652 void OfflinePageModelImpl::CheckMetadataConsistency() {
653 archive_manager_->GetAllArchives(
654 base::Bind(&OfflinePageModelImpl::CheckMetadataConsistencyForArchivePaths,
655 weak_ptr_factory_.GetWeakPtr()));
656 }
657
658 ClientPolicyController* OfflinePageModelImpl::GetPolicyController() {
659 return policy_controller_.get();
660 }
661
662 OfflinePageMetadataStore* OfflinePageModelImpl::GetStoreForTesting() {
663 return store_.get();
664 }
665
666 OfflinePageStorageManager* OfflinePageModelImpl::GetStorageManager() {
667 return storage_manager_.get();
668 }
669
670 bool OfflinePageModelImpl::is_loaded() const {
671 return is_loaded_;
672 }
673
674 OfflineEventLogger* OfflinePageModelImpl::GetLogger() {
675 return &offline_event_logger_;
676 }
677
678 void OfflinePageModelImpl::OnCreateArchiveDone(
679 const SavePageParams& save_page_params,
680 int64_t offline_id,
681 const base::Time& start_time,
682 const SavePageCallback& callback,
683 OfflinePageArchiver* archiver,
684 ArchiverResult archiver_result,
685 const GURL& url,
686 const base::FilePath& file_path,
687 const base::string16& title,
688 int64_t file_size) {
689 if (save_page_params.url != url) {
690 DVLOG(1) << "Saved URL does not match requested URL.";
691 // TODO(fgorski): We have created an archive for a wrong URL. It should be
692 // deleted from here, once archiver has the right functionality.
693 InformSavePageDone(callback, SavePageResult::ARCHIVE_CREATION_FAILED,
694 save_page_params.client_id, offline_id);
695 DeletePendingArchiver(archiver);
696 return;
697 }
698
699 if (archiver_result != ArchiverResult::SUCCESSFULLY_CREATED) {
700 SavePageResult result = ToSavePageResult(archiver_result);
701 InformSavePageDone(
702 callback, result, save_page_params.client_id, offline_id);
703 DeletePendingArchiver(archiver);
704 return;
705 }
706 OfflinePageItem offline_page_item(url, offline_id, save_page_params.client_id,
707 file_path, file_size, start_time);
708 offline_page_item.title = title;
709 offline_page_item.original_url = save_page_params.original_url;
710 store_->AddOfflinePage(offline_page_item,
711 base::Bind(&OfflinePageModelImpl::OnAddOfflinePageDone,
712 weak_ptr_factory_.GetWeakPtr(), archiver,
713 callback, offline_page_item));
714 }
715
716 void OfflinePageModelImpl::OnAddOfflinePageDone(
717 OfflinePageArchiver* archiver,
718 const SavePageCallback& callback,
719 const OfflinePageItem& offline_page,
720 ItemActionStatus status) {
721 SavePageResult result;
722 if (status == ItemActionStatus::SUCCESS) {
723 offline_pages_[offline_page.offline_id] = offline_page;
724 result = SavePageResult::SUCCESS;
725 ReportPageHistogramAfterSave(policy_controller_.get(), offline_pages_,
726 offline_page, GetCurrentTime());
727 offline_event_logger_.RecordPageSaved(
728 offline_page.client_id.name_space, offline_page.url.spec(),
729 std::to_string(offline_page.offline_id));
730 } else if (status == ItemActionStatus::ALREADY_EXISTS) {
731 result = SavePageResult::ALREADY_EXISTS;
732 } else {
733 result = SavePageResult::STORE_FAILURE;
734 }
735 InformSavePageDone(callback, result, offline_page.client_id,
736 offline_page.offline_id);
737 if (result == SavePageResult::SUCCESS) {
738 DeleteExistingPagesWithSameURL(offline_page);
739 } else {
740 PostClearStorageIfNeededTask(false /* delayed */);
741 }
742
743 DeletePendingArchiver(archiver);
744 for (Observer& observer : observers_)
745 observer.OfflinePageModelChanged(this);
746 }
747
748 void OfflinePageModelImpl::OnMarkPageAccesseDone(
749 const OfflinePageItem& offline_page_item,
750 std::unique_ptr<OfflinePagesUpdateResult> result) {
751 // Update the item in the cache only upon success.
752 if (result->updated_items.size() > 0)
753 offline_pages_[offline_page_item.offline_id] = offline_page_item;
754
755 // No need to fire OfflinePageModelChanged event since updating access info
756 // should not have any impact to the UI.
757 }
758
759 void OfflinePageModelImpl::OnEnsureArchivesDirCreatedDone(
760 const base::TimeTicks& start_time) {
761 UMA_HISTOGRAM_TIMES("OfflinePages.Model.ArchiveDirCreationTime",
762 base::TimeTicks::Now() - start_time);
763
764 const int kResetAttemptsLeft = 1;
765 store_->Initialize(base::Bind(&OfflinePageModelImpl::OnStoreInitialized,
766 weak_ptr_factory_.GetWeakPtr(), start_time,
767 kResetAttemptsLeft));
768 }
769
770 void OfflinePageModelImpl::OnStoreInitialized(const base::TimeTicks& start_time,
771 int reset_attempts_left,
772 bool success) {
773 if (success) {
774 DCHECK_EQ(store_->state(), StoreState::LOADED);
775 store_->GetOfflinePages(
776 base::Bind(&OfflinePageModelImpl::OnInitialGetOfflinePagesDone,
777 weak_ptr_factory_.GetWeakPtr(), start_time));
778 return;
779 }
780
781 DCHECK_EQ(store_->state(), StoreState::FAILED_LOADING);
782 // If there are no more reset attempts left, stop here.
783 if (reset_attempts_left == 0) {
784 FinalizeModelLoad();
785 return;
786 }
787
788 // Otherwise reduce the remaining attempts counter and reset store.
789 store_->Reset(base::Bind(&OfflinePageModelImpl::OnStoreResetDone,
790 weak_ptr_factory_.GetWeakPtr(), start_time,
791 reset_attempts_left - 1));
792 }
793
794 void OfflinePageModelImpl::OnStoreResetDone(const base::TimeTicks& start_time,
795 int reset_attempts_left,
796 bool success) {
797 if (success) {
798 DCHECK_EQ(store_->state(), StoreState::NOT_LOADED);
799 store_->Initialize(base::Bind(&OfflinePageModelImpl::OnStoreInitialized,
800 weak_ptr_factory_.GetWeakPtr(), start_time,
801 reset_attempts_left));
802 return;
803 }
804
805 DCHECK_EQ(store_->state(), StoreState::FAILED_RESET);
806 FinalizeModelLoad();
807 }
808
809 void OfflinePageModelImpl::OnInitialGetOfflinePagesDone(
810 const base::TimeTicks& start_time,
811 const std::vector<OfflinePageItem>& offline_pages) {
812 DCHECK(!is_loaded_);
813
814 UMA_HISTOGRAM_TIMES("OfflinePages.Model.ConstructionToLoadedEventTime",
815 base::TimeTicks::Now() - start_time);
816
817 CacheLoadedData(offline_pages);
818 FinalizeModelLoad();
819
820 // Ensure necessary cleanup operations are started.
821 CheckMetadataConsistency();
822 }
823
824 void OfflinePageModelImpl::FinalizeModelLoad() {
825 is_loaded_ = true;
826
827 // All actions below are meant to be taken regardless of successful load of
828 // the store.
829
830 // Inform observers the load is done.
831 for (Observer& observer : observers_)
832 observer.OfflinePageModelLoaded(this);
833
834 // Run all the delayed tasks.
835 for (const auto& delayed_task : delayed_tasks_)
836 delayed_task.Run();
837 delayed_tasks_.clear();
838
839 // Clear storage.
840 PostClearStorageIfNeededTask(true /* delayed */);
841 }
842
843 void OfflinePageModelImpl::InformSavePageDone(const SavePageCallback& callback,
844 SavePageResult result,
845 const ClientId& client_id,
846 int64_t offline_id) {
847 ReportSavePageResultHistogramAfterSave(client_id, result);
848 archive_manager_->GetStorageStats(
849 base::Bind(&ReportStorageHistogramsAfterSave));
850 callback.Run(result, offline_id);
851 }
852
853 void OfflinePageModelImpl::DeleteExistingPagesWithSameURL(
854 const OfflinePageItem& offline_page) {
855 // Remove existing pages generated by the same policy and with same url.
856 size_t pages_allowed =
857 policy_controller_->GetPolicy(offline_page.client_id.name_space)
858 .pages_allowed_per_url;
859 if (pages_allowed == kUnlimitedPages)
860 return;
861 GetPagesByURL(
862 offline_page.url,
863 URLSearchMode::SEARCH_BY_FINAL_URL_ONLY,
864 base::Bind(&OfflinePageModelImpl::OnPagesFoundWithSameURL,
865 weak_ptr_factory_.GetWeakPtr(), offline_page, pages_allowed));
866 }
867
868 void OfflinePageModelImpl::OnPagesFoundWithSameURL(
869 const OfflinePageItem& offline_page,
870 size_t pages_allowed,
871 const MultipleOfflinePageItemResult& items) {
872 std::vector<OfflinePageItem> pages_to_delete;
873 for (const auto& item : items) {
874 if (item.offline_id != offline_page.offline_id &&
875 item.client_id.name_space == offline_page.client_id.name_space) {
876 pages_to_delete.push_back(item);
877 }
878 }
879 // Only keep |pages_allowed|-1 of most fresh pages and delete others, by
880 // sorting pages with the oldest ones first and resize the vector.
881 if (pages_to_delete.size() >= pages_allowed) {
882 sort(pages_to_delete.begin(), pages_to_delete.end(),
883 [](const OfflinePageItem& a, const OfflinePageItem& b) -> bool {
884 return a.last_access_time < b.last_access_time;
885 });
886 pages_to_delete.resize(pages_to_delete.size() - pages_allowed + 1);
887 }
888 std::vector<int64_t> page_ids_to_delete;
889 for (const auto& item : pages_to_delete)
890 page_ids_to_delete.push_back(item.offline_id);
891 DeletePagesByOfflineId(
892 page_ids_to_delete,
893 base::Bind(&OfflinePageModelImpl::OnDeleteOldPagesWithSameURL,
894 weak_ptr_factory_.GetWeakPtr()));
895 }
896
897 void OfflinePageModelImpl::OnDeleteOldPagesWithSameURL(
898 DeletePageResult result) {
899 // TODO(romax) Add UMAs for failure cases.
900 PostClearStorageIfNeededTask(false /* delayed */);
901 }
902
903 void OfflinePageModelImpl::DeletePendingArchiver(
904 OfflinePageArchiver* archiver) {
905 pending_archivers_.erase(std::find(pending_archivers_.begin(),
906 pending_archivers_.end(), archiver));
907 }
908
909 void OfflinePageModelImpl::OnDeleteArchiveFilesDone(
910 const std::vector<int64_t>& offline_ids,
911 const DeletePageCallback& callback,
912 bool success) {
913 if (!success) {
914 InformDeletePageDone(callback, DeletePageResult::DEVICE_FAILURE);
915 return;
916 }
917
918 store_->RemoveOfflinePages(
919 offline_ids, base::Bind(&OfflinePageModelImpl::OnRemoveOfflinePagesDone,
920 weak_ptr_factory_.GetWeakPtr(), callback));
921 }
922
923 void OfflinePageModelImpl::OnRemoveOfflinePagesDone(
924 const DeletePageCallback& callback,
925 std::unique_ptr<OfflinePagesUpdateResult> result) {
926 ReportPageHistogramsAfterDelete(offline_pages_, result->updated_items,
927 GetCurrentTime());
928
929 // This part of the loop is explicitly broken out, as it should be gone in
930 // fully asynchronous code.
931 for (const auto& page : result->updated_items) {
932 int64_t offline_id = page.offline_id;
933 offline_event_logger_.RecordPageDeleted(std::to_string(offline_id));
934 auto iter = offline_pages_.find(offline_id);
935 if (iter == offline_pages_.end())
936 continue;
937 offline_pages_.erase(iter);
938 }
939
940 for (const auto& page : result->updated_items) {
941 for (Observer& observer : observers_)
942 observer.OfflinePageDeleted(page.offline_id, page.client_id);
943 }
944
945 // TODO(fgorski): React the FAILED_INITIALIZATION, FAILED_RESET here.
946 // TODO(fgorski): We need a better callback interface for the Remove action on
947 // the this class. Currently removing an item that does not exist is
948 // considered a success, but not called out as such to the caller.
949 DeletePageResult delete_result;
950 if (result->store_state == StoreState::LOADED)
951 delete_result = DeletePageResult::SUCCESS;
952 else
953 delete_result = DeletePageResult::STORE_FAILURE;
954
955 InformDeletePageDone(callback, delete_result);
956 }
957
958 void OfflinePageModelImpl::InformDeletePageDone(
959 const DeletePageCallback& callback,
960 DeletePageResult result) {
961 UMA_HISTOGRAM_ENUMERATION("OfflinePages.DeletePageResult",
962 static_cast<int>(result),
963 static_cast<int>(DeletePageResult::RESULT_COUNT));
964 archive_manager_->GetStorageStats(
965 base::Bind(&ReportStorageHistogramsAfterDelete));
966 if (!callback.is_null())
967 callback.Run(result);
968 }
969
970 void OfflinePageModelImpl::CheckMetadataConsistencyForArchivePaths(
971 const std::set<base::FilePath>& archive_paths) {
972 DeletePagesMissingArchiveFile(archive_paths);
973 DeleteOrphanedArchives(archive_paths);
974 }
975
976 void OfflinePageModelImpl::DeletePagesMissingArchiveFile(
977 const std::set<base::FilePath>& archive_paths) {
978 std::vector<int64_t> ids_of_pages_missing_archive_file;
979 for (const auto& id_page_pair : offline_pages_) {
980 if (archive_paths.count(id_page_pair.second.file_path) == 0UL)
981 ids_of_pages_missing_archive_file.push_back(id_page_pair.first);
982 }
983
984 if (ids_of_pages_missing_archive_file.empty())
985 return;
986
987 DeletePagesByOfflineId(
988 ids_of_pages_missing_archive_file,
989 base::Bind(&OfflinePageModelImpl::OnDeletePagesMissingArchiveFileDone,
990 weak_ptr_factory_.GetWeakPtr(),
991 ids_of_pages_missing_archive_file));
992 }
993
994 void OfflinePageModelImpl::OnDeletePagesMissingArchiveFileDone(
995 const std::vector<int64_t>& offline_ids,
996 DeletePageResult result) {
997 UMA_HISTOGRAM_COUNTS("OfflinePages.Consistency.PagesMissingArchiveFileCount",
998 static_cast<int32_t>(offline_ids.size()));
999 UMA_HISTOGRAM_ENUMERATION(
1000 "OfflinePages.Consistency.DeletePagesMissingArchiveFileResult",
1001 static_cast<int>(result),
1002 static_cast<int>(DeletePageResult::RESULT_COUNT));
1003 }
1004
1005 void OfflinePageModelImpl::DeleteOrphanedArchives(
1006 const std::set<base::FilePath>& archive_paths) {
1007 // Archives are considered orphaned unless they are pointed to by some pages.
1008 std::set<base::FilePath> orphaned_archive_set(archive_paths);
1009 for (const auto& id_page_pair : offline_pages_)
1010 orphaned_archive_set.erase(id_page_pair.second.file_path);
1011
1012 if (orphaned_archive_set.empty())
1013 return;
1014
1015 std::vector<base::FilePath> orphaned_archives(orphaned_archive_set.begin(),
1016 orphaned_archive_set.end());
1017 archive_manager_->DeleteMultipleArchives(
1018 orphaned_archives,
1019 base::Bind(&OfflinePageModelImpl::OnDeleteOrphanedArchivesDone,
1020 weak_ptr_factory_.GetWeakPtr(), orphaned_archives));
1021 }
1022
1023 void OfflinePageModelImpl::OnDeleteOrphanedArchivesDone(
1024 const std::vector<base::FilePath>& archives,
1025 bool success) {
1026 UMA_HISTOGRAM_COUNTS("OfflinePages.Consistency.OrphanedArchivesCount",
1027 static_cast<int32_t>(archives.size()));
1028 UMA_HISTOGRAM_BOOLEAN("OfflinePages.Consistency.DeleteOrphanedArchivesResult",
1029 success);
1030 }
1031
1032 void OfflinePageModelImpl::CacheLoadedData(
1033 const std::vector<OfflinePageItem>& offline_pages) {
1034 offline_pages_.clear();
1035 for (const auto& offline_page : offline_pages)
1036 offline_pages_[offline_page.offline_id] = offline_page;
1037 }
1038
1039 void OfflinePageModelImpl::ClearStorageIfNeeded(
1040 const ClearStorageCallback& callback) {
1041 // Create Storage Manager if necessary.
1042 if (!storage_manager_) {
1043 storage_manager_.reset(new OfflinePageStorageManager(
1044 this, GetPolicyController(), archive_manager_.get()));
1045 }
1046 storage_manager_->ClearPagesIfNeeded(callback);
1047 }
1048
1049 void OfflinePageModelImpl::OnStorageCleared(size_t deleted_page_count,
1050 ClearStorageResult result) {
1051 UMA_HISTOGRAM_ENUMERATION("OfflinePages.ClearStorageResult",
1052 static_cast<int>(result),
1053 static_cast<int>(ClearStorageResult::RESULT_COUNT));
1054 if (deleted_page_count > 0) {
1055 UMA_HISTOGRAM_COUNTS("OfflinePages.ClearStorageBatchSize",
1056 static_cast<int32_t>(deleted_page_count));
1057 }
1058 }
1059
1060 void OfflinePageModelImpl::PostClearStorageIfNeededTask(bool delayed) {
1061 base::TimeDelta delay =
1062 delayed ? kStorageManagerStartingDelay : base::TimeDelta();
1063 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
1064 FROM_HERE, base::Bind(&OfflinePageModelImpl::ClearStorageIfNeeded,
1065 weak_ptr_factory_.GetWeakPtr(),
1066 base::Bind(&OfflinePageModelImpl::OnStorageCleared,
1067 weak_ptr_factory_.GetWeakPtr())),
1068 delay);
1069 }
1070
1071 bool OfflinePageModelImpl::IsRemovedOnCacheReset(
1072 const OfflinePageItem& offline_page) const {
1073 return policy_controller_->IsRemovedOnCacheReset(
1074 offline_page.client_id.name_space);
1075 }
1076
1077 void OfflinePageModelImpl::RunWhenLoaded(const base::Closure& task) {
1078 if (!is_loaded_) {
1079 delayed_tasks_.push_back(task);
1080 return;
1081 }
1082
1083 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, task);
1084 }
1085
1086 base::Time OfflinePageModelImpl::GetCurrentTime() const {
1087 return testing_clock_ ? testing_clock_->Now() : base::Time::Now();
1088 }
1089
1090 } // namespace offline_pages
OLDNEW
« no previous file with comments | « components/offline_pages/offline_page_model_impl.h ('k') | components/offline_pages/offline_page_model_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698