| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "components/offline_pages/offline_page_model_impl.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <limits> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/files/file_util.h" | |
| 12 #include "base/location.h" | |
| 13 #include "base/logging.h" | |
| 14 #include "base/metrics/histogram_macros.h" | |
| 15 #include "base/rand_util.h" | |
| 16 #include "base/sequenced_task_runner.h" | |
| 17 #include "base/strings/string16.h" | |
| 18 #include "base/strings/string_number_conversions.h" | |
| 19 #include "base/threading/thread_task_runner_handle.h" | |
| 20 #include "base/time/clock.h" | |
| 21 #include "base/time/time.h" | |
| 22 #include "components/offline_pages/archive_manager.h" | |
| 23 #include "components/offline_pages/client_namespace_constants.h" | |
| 24 #include "components/offline_pages/client_policy_controller.h" | |
| 25 #include "components/offline_pages/offline_page_item.h" | |
| 26 #include "components/offline_pages/offline_page_model_query.h" | |
| 27 #include "components/offline_pages/offline_page_storage_manager.h" | |
| 28 #include "url/gurl.h" | |
| 29 | |
| 30 using ArchiverResult = offline_pages::OfflinePageArchiver::ArchiverResult; | |
| 31 using ClearStorageCallback = | |
| 32 offline_pages::OfflinePageStorageManager::ClearStorageCallback; | |
| 33 using ClearStorageResult = | |
| 34 offline_pages::OfflinePageStorageManager::ClearStorageResult; | |
| 35 | |
| 36 namespace offline_pages { | |
| 37 | |
| 38 namespace { | |
| 39 | |
| 40 // The delay used to schedule the first clear storage request for storage | |
| 41 // manager after the model is loaded. | |
| 42 const base::TimeDelta kStorageManagerStartingDelay = | |
| 43 base::TimeDelta::FromSeconds(20); | |
| 44 | |
| 45 int64_t GenerateOfflineId() { | |
| 46 return base::RandGenerator(std::numeric_limits<int64_t>::max()) + 1; | |
| 47 } | |
| 48 | |
| 49 // The maximum histogram size for the metrics that measure time between views of | |
| 50 // a given page. | |
| 51 const base::TimeDelta kMaxOpenedPageHistogramBucket = | |
| 52 base::TimeDelta::FromDays(90); | |
| 53 | |
| 54 SavePageResult ToSavePageResult(ArchiverResult archiver_result) { | |
| 55 SavePageResult result; | |
| 56 switch (archiver_result) { | |
| 57 case ArchiverResult::SUCCESSFULLY_CREATED: | |
| 58 result = SavePageResult::SUCCESS; | |
| 59 break; | |
| 60 case ArchiverResult::ERROR_DEVICE_FULL: | |
| 61 result = SavePageResult::DEVICE_FULL; | |
| 62 break; | |
| 63 case ArchiverResult::ERROR_CONTENT_UNAVAILABLE: | |
| 64 result = SavePageResult::CONTENT_UNAVAILABLE; | |
| 65 break; | |
| 66 case ArchiverResult::ERROR_ARCHIVE_CREATION_FAILED: | |
| 67 result = SavePageResult::ARCHIVE_CREATION_FAILED; | |
| 68 break; | |
| 69 case ArchiverResult::ERROR_CANCELED: | |
| 70 result = SavePageResult::CANCELLED; | |
| 71 break; | |
| 72 case ArchiverResult::ERROR_SECURITY_CERTIFICATE: | |
| 73 result = SavePageResult::SECURITY_CERTIFICATE_ERROR; | |
| 74 break; | |
| 75 default: | |
| 76 NOTREACHED(); | |
| 77 result = SavePageResult::CONTENT_UNAVAILABLE; | |
| 78 } | |
| 79 return result; | |
| 80 } | |
| 81 | |
| 82 std::string AddHistogramSuffix(const ClientId& client_id, | |
| 83 const char* histogram_name) { | |
| 84 if (client_id.name_space.empty()) { | |
| 85 NOTREACHED(); | |
| 86 return histogram_name; | |
| 87 } | |
| 88 std::string adjusted_histogram_name(histogram_name); | |
| 89 adjusted_histogram_name += "."; | |
| 90 adjusted_histogram_name += client_id.name_space; | |
| 91 return adjusted_histogram_name; | |
| 92 } | |
| 93 | |
| 94 void ReportStorageHistogramsAfterSave( | |
| 95 const ArchiveManager::StorageStats& storage_stats) { | |
| 96 const int kMB = 1024 * 1024; | |
| 97 int free_disk_space_mb = | |
| 98 static_cast<int>(storage_stats.free_disk_space / kMB); | |
| 99 UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.SavePage.FreeSpaceMB", | |
| 100 free_disk_space_mb, 1, 500000, 50); | |
| 101 | |
| 102 int total_page_size_mb = | |
| 103 static_cast<int>(storage_stats.total_archives_size / kMB); | |
| 104 UMA_HISTOGRAM_COUNTS_10000("OfflinePages.TotalPageSize", total_page_size_mb); | |
| 105 } | |
| 106 | |
| 107 void ReportStorageHistogramsAfterDelete( | |
| 108 const ArchiveManager::StorageStats& storage_stats) { | |
| 109 const int kMB = 1024 * 1024; | |
| 110 int free_disk_space_mb = | |
| 111 static_cast<int>(storage_stats.free_disk_space / kMB); | |
| 112 UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.DeletePage.FreeSpaceMB", | |
| 113 free_disk_space_mb, 1, 500000, 50); | |
| 114 | |
| 115 int total_page_size_mb = | |
| 116 static_cast<int>(storage_stats.total_archives_size / kMB); | |
| 117 UMA_HISTOGRAM_COUNTS_10000("OfflinePages.TotalPageSize", total_page_size_mb); | |
| 118 | |
| 119 if (storage_stats.free_disk_space > 0) { | |
| 120 int percentage_of_free = static_cast<int>( | |
| 121 1.0 * storage_stats.total_archives_size / | |
| 122 (storage_stats.total_archives_size + storage_stats.free_disk_space) * | |
| 123 100); | |
| 124 UMA_HISTOGRAM_PERCENTAGE( | |
| 125 "OfflinePages.DeletePage.TotalPageSizeAsPercentageOfFreeSpace", | |
| 126 percentage_of_free); | |
| 127 } | |
| 128 } | |
| 129 | |
| 130 void ReportSavePageResultHistogramAfterSave(const ClientId& client_id, | |
| 131 SavePageResult result) { | |
| 132 // The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION | |
| 133 // macro adapted to allow for a dynamically suffixed histogram name. | |
| 134 // Note: The factory creates and owns the histogram. | |
| 135 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet( | |
| 136 AddHistogramSuffix(client_id, "OfflinePages.SavePageResult"), | |
| 137 1, | |
| 138 static_cast<int>(SavePageResult::RESULT_COUNT), | |
| 139 static_cast<int>(SavePageResult::RESULT_COUNT) + 1, | |
| 140 base::HistogramBase::kUmaTargetedHistogramFlag); | |
| 141 histogram->Add(static_cast<int>(result)); | |
| 142 } | |
| 143 | |
| 144 // Goes through the list of offline pages, compiling the following two metrics: | |
| 145 // - a count of the pages with the same URL | |
| 146 // - The difference between the |created_before| time and the creation time of | |
| 147 // the page with the closest creation time before |created_before|. | |
| 148 // Returns true if there was a page that was saved before |created_before| with | |
| 149 // a matching URL. | |
| 150 bool GetMatchingURLCountAndMostRecentCreationTime( | |
| 151 const std::map<int64_t, OfflinePageItem>& offline_pages, | |
| 152 std::string name_space, | |
| 153 const GURL& url, | |
| 154 base::Time created_before, | |
| 155 int* matching_url_count, | |
| 156 base::TimeDelta* most_recent_creation_time) { | |
| 157 int count = 0; | |
| 158 | |
| 159 // Create a time that is very old, so that any valid time will be newer than | |
| 160 // it. | |
| 161 base::Time latest_time; | |
| 162 bool matching_page = false; | |
| 163 | |
| 164 for (auto& id_page_pair : offline_pages) { | |
| 165 if (id_page_pair.second.client_id.name_space == name_space && | |
| 166 url == id_page_pair.second.url) { | |
| 167 count++; | |
| 168 base::Time page_creation_time = id_page_pair.second.creation_time; | |
| 169 if (page_creation_time < created_before && | |
| 170 page_creation_time > latest_time) { | |
| 171 latest_time = page_creation_time; | |
| 172 matching_page = true; | |
| 173 } | |
| 174 } | |
| 175 } | |
| 176 | |
| 177 if (matching_url_count != nullptr) | |
| 178 *matching_url_count = count; | |
| 179 if (most_recent_creation_time != nullptr && latest_time != base::Time()) | |
| 180 *most_recent_creation_time = created_before - latest_time; | |
| 181 | |
| 182 return matching_page; | |
| 183 } | |
| 184 | |
| 185 void ReportPageHistogramAfterSave( | |
| 186 ClientPolicyController* policy_controller_, | |
| 187 const std::map<int64_t, OfflinePageItem>& offline_pages, | |
| 188 const OfflinePageItem& offline_page, | |
| 189 const base::Time& save_time) { | |
| 190 DCHECK(policy_controller_); | |
| 191 // The histogram below is an expansion of the UMA_HISTOGRAM_TIMES | |
| 192 // macro adapted to allow for a dynamically suffixed histogram name. | |
| 193 // Note: The factory creates and owns the histogram. | |
| 194 base::HistogramBase* histogram = base::Histogram::FactoryTimeGet( | |
| 195 AddHistogramSuffix(offline_page.client_id, "OfflinePages.SavePageTime"), | |
| 196 base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromSeconds(10), | |
| 197 50, base::HistogramBase::kUmaTargetedHistogramFlag); | |
| 198 histogram->AddTime(save_time - offline_page.creation_time); | |
| 199 | |
| 200 // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS | |
| 201 // macro adapted to allow for a dynamically suffixed histogram name. | |
| 202 // Note: The factory creates and owns the histogram. | |
| 203 // Reported as Kb between 1Kb and 10Mb. | |
| 204 histogram = base::Histogram::FactoryGet( | |
| 205 AddHistogramSuffix(offline_page.client_id, "OfflinePages.PageSize"), | |
| 206 1, 10000, 50, base::HistogramBase::kUmaTargetedHistogramFlag); | |
| 207 histogram->Add(offline_page.file_size / 1024); | |
| 208 | |
| 209 if (policy_controller_->IsSupportedByDownload( | |
| 210 offline_page.client_id.name_space)) { | |
| 211 int matching_url_count; | |
| 212 base::TimeDelta time_since_most_recent_duplicate; | |
| 213 if (GetMatchingURLCountAndMostRecentCreationTime( | |
| 214 offline_pages, offline_page.client_id.name_space, offline_page.url, | |
| 215 offline_page.creation_time, &matching_url_count, | |
| 216 &time_since_most_recent_duplicate)) { | |
| 217 // Using CUSTOM_COUNTS instead of time-oriented histogram to record | |
| 218 // samples in seconds rather than milliseconds. | |
| 219 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
| 220 "OfflinePages.DownloadSavedPageTimeSinceDuplicateSaved", | |
| 221 time_since_most_recent_duplicate.InSeconds(), | |
| 222 base::TimeDelta::FromSeconds(1).InSeconds(), | |
| 223 base::TimeDelta::FromDays(7).InSeconds(), 50); | |
| 224 } | |
| 225 UMA_HISTOGRAM_CUSTOM_COUNTS("OfflinePages.DownloadSavedPageDuplicateCount", | |
| 226 matching_url_count, 1, 20, 10); | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 void ReportPageHistogramsAfterDelete( | |
| 231 const std::map<int64_t, OfflinePageItem>& offline_pages, | |
| 232 const std::vector<OfflinePageItem>& deleted_pages, | |
| 233 const base::Time& delete_time) { | |
| 234 const int max_minutes = base::TimeDelta::FromDays(365).InMinutes(); | |
| 235 int64_t total_size = 0; | |
| 236 | |
| 237 for (const auto& page : deleted_pages) { | |
| 238 total_size += page.file_size; | |
| 239 ClientId client_id = page.client_id; | |
| 240 | |
| 241 if (client_id.name_space == kDownloadNamespace) { | |
| 242 int remaining_pages_with_url; | |
| 243 GetMatchingURLCountAndMostRecentCreationTime( | |
| 244 offline_pages, page.client_id.name_space, page.url, base::Time::Max(), | |
| 245 &remaining_pages_with_url, nullptr); | |
| 246 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
| 247 "OfflinePages.DownloadDeletedPageDuplicateCount", | |
| 248 remaining_pages_with_url, 1, 20, 10); | |
| 249 } | |
| 250 | |
| 251 // The histograms below are an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS | |
| 252 // macro adapted to allow for a dynamically suffixed histogram name. | |
| 253 // Note: The factory creates and owns the histogram. | |
| 254 base::HistogramBase* histogram = base::Histogram::FactoryGet( | |
| 255 AddHistogramSuffix(client_id, "OfflinePages.PageLifetime"), | |
| 256 1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag); | |
| 257 histogram->Add((delete_time - page.creation_time).InMinutes()); | |
| 258 | |
| 259 histogram = base::Histogram::FactoryGet( | |
| 260 AddHistogramSuffix( | |
| 261 client_id, "OfflinePages.DeletePage.TimeSinceLastOpen"), | |
| 262 1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag); | |
| 263 histogram->Add((delete_time - page.last_access_time).InMinutes()); | |
| 264 | |
| 265 histogram = base::Histogram::FactoryGet( | |
| 266 AddHistogramSuffix( | |
| 267 client_id, "OfflinePages.DeletePage.LastOpenToCreated"), | |
| 268 1, max_minutes, 100, base::HistogramBase::kUmaTargetedHistogramFlag); | |
| 269 histogram->Add((page.last_access_time - page.creation_time).InMinutes()); | |
| 270 | |
| 271 // Reported as Kb between 1Kb and 10Mb. | |
| 272 histogram = base::Histogram::FactoryGet( | |
| 273 AddHistogramSuffix(client_id, "OfflinePages.DeletePage.PageSize"), | |
| 274 1, 10000, 50, base::HistogramBase::kUmaTargetedHistogramFlag); | |
| 275 histogram->Add(page.file_size / 1024); | |
| 276 | |
| 277 histogram = base::Histogram::FactoryGet( | |
| 278 AddHistogramSuffix(client_id, "OfflinePages.DeletePage.AccessCount"), | |
| 279 1, 1000000, 50, base::HistogramBase::kUmaTargetedHistogramFlag); | |
| 280 histogram->Add(page.access_count); | |
| 281 } | |
| 282 | |
| 283 if (deleted_pages.size() > 1) { | |
| 284 UMA_HISTOGRAM_COUNTS("OfflinePages.BatchDelete.Count", | |
| 285 static_cast<int32_t>(deleted_pages.size())); | |
| 286 UMA_HISTOGRAM_MEMORY_KB( | |
| 287 "OfflinePages.BatchDelete.TotalPageSize", total_size / 1024); | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 void ReportPageHistogramsAfterAccess(const OfflinePageItem& offline_page_item, | |
| 292 const base::Time& access_time) { | |
| 293 // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS | |
| 294 // macro adapted to allow for a dynamically suffixed histogram name. | |
| 295 // Note: The factory creates and owns the histogram. | |
| 296 base::HistogramBase* histogram = base::Histogram::FactoryGet( | |
| 297 AddHistogramSuffix( | |
| 298 offline_page_item.client_id, | |
| 299 offline_page_item.access_count == 0 ? | |
| 300 "OfflinePages.FirstOpenSinceCreated" : | |
| 301 "OfflinePages.OpenSinceLastOpen"), | |
| 302 1, kMaxOpenedPageHistogramBucket.InMinutes(), 50, | |
| 303 base::HistogramBase::kUmaTargetedHistogramFlag); | |
| 304 histogram->Add( | |
| 305 (access_time - offline_page_item.last_access_time).InMinutes()); | |
| 306 } | |
| 307 | |
| 308 } // namespace | |
| 309 | |
| 310 // protected | |
| 311 OfflinePageModelImpl::OfflinePageModelImpl() | |
| 312 : OfflinePageModel(), is_loaded_(false), weak_ptr_factory_(this) {} | |
| 313 | |
| 314 OfflinePageModelImpl::OfflinePageModelImpl( | |
| 315 std::unique_ptr<OfflinePageMetadataStore> store, | |
| 316 const base::FilePath& archives_dir, | |
| 317 const scoped_refptr<base::SequencedTaskRunner>& task_runner) | |
| 318 : store_(std::move(store)), | |
| 319 archives_dir_(archives_dir), | |
| 320 is_loaded_(false), | |
| 321 policy_controller_(new ClientPolicyController()), | |
| 322 archive_manager_(new ArchiveManager(archives_dir, task_runner)), | |
| 323 testing_clock_(nullptr), | |
| 324 weak_ptr_factory_(this) { | |
| 325 archive_manager_->EnsureArchivesDirCreated( | |
| 326 base::Bind(&OfflinePageModelImpl::OnEnsureArchivesDirCreatedDone, | |
| 327 weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now())); | |
| 328 } | |
| 329 | |
| 330 OfflinePageModelImpl::~OfflinePageModelImpl() {} | |
| 331 | |
| 332 void OfflinePageModelImpl::AddObserver(Observer* observer) { | |
| 333 observers_.AddObserver(observer); | |
| 334 } | |
| 335 | |
| 336 void OfflinePageModelImpl::RemoveObserver(Observer* observer) { | |
| 337 observers_.RemoveObserver(observer); | |
| 338 } | |
| 339 | |
| 340 void OfflinePageModelImpl::SavePage( | |
| 341 const SavePageParams& save_page_params, | |
| 342 std::unique_ptr<OfflinePageArchiver> archiver, | |
| 343 const SavePageCallback& callback) { | |
| 344 DCHECK(is_loaded_); | |
| 345 | |
| 346 // Skip saving the page that is not intended to be saved, like local file | |
| 347 // page. | |
| 348 if (!OfflinePageModel::CanSaveURL(save_page_params.url)) { | |
| 349 InformSavePageDone(callback, SavePageResult::SKIPPED, | |
| 350 save_page_params.client_id, kInvalidOfflineId); | |
| 351 return; | |
| 352 } | |
| 353 | |
| 354 // The web contents is not available if archiver is not created and passed. | |
| 355 if (!archiver.get()) { | |
| 356 InformSavePageDone(callback, SavePageResult::CONTENT_UNAVAILABLE, | |
| 357 save_page_params.client_id, kInvalidOfflineId); | |
| 358 return; | |
| 359 } | |
| 360 | |
| 361 // If we already have an offline id, use it. If not, generate one. | |
| 362 int64_t offline_id = save_page_params.proposed_offline_id; | |
| 363 if (offline_id == kInvalidOfflineId) | |
| 364 offline_id = GenerateOfflineId(); | |
| 365 | |
| 366 archiver->CreateArchive( | |
| 367 archives_dir_, offline_id, | |
| 368 base::Bind(&OfflinePageModelImpl::OnCreateArchiveDone, | |
| 369 weak_ptr_factory_.GetWeakPtr(), save_page_params, offline_id, | |
| 370 GetCurrentTime(), callback)); | |
| 371 pending_archivers_.push_back(std::move(archiver)); | |
| 372 } | |
| 373 | |
| 374 void OfflinePageModelImpl::MarkPageAccessed(int64_t offline_id) { | |
| 375 RunWhenLoaded(base::Bind(&OfflinePageModelImpl::MarkPageAccessedWhenLoadDone, | |
| 376 weak_ptr_factory_.GetWeakPtr(), offline_id)); | |
| 377 } | |
| 378 | |
| 379 void OfflinePageModelImpl::MarkPageAccessedWhenLoadDone(int64_t offline_id) { | |
| 380 DCHECK(is_loaded_); | |
| 381 | |
| 382 auto iter = offline_pages_.find(offline_id); | |
| 383 if (iter == offline_pages_.end()) | |
| 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 | |
| OLD | NEW |