| 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 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 | |
| OLD | NEW |