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

Side by Side Diff: chrome/browser/win/jumplist.cc

Issue 2752063002: Remove JumpListIconsOld directory and set upper limit for delete attempts (Closed)
Patch Set: Remove JumpListIconsOld related file operations. Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 The Chromium Authors. All rights reserved. 1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/win/jumplist.h" 5 #include "chrome/browser/win/jumplist.h"
6 6
7 #include <Shlwapi.h> 7 #include <Shlwapi.h>
8 #include <windows.h> 8 #include <windows.h>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/bind_helpers.h" 11 #include "base/bind_helpers.h"
12 #include "base/callback_helpers.h" 12 #include "base/callback_helpers.h"
13 #include "base/command_line.h" 13 #include "base/command_line.h"
14 #include "base/files/file_enumerator.h"
14 #include "base/files/file_util.h" 15 #include "base/files/file_util.h"
15 #include "base/macros.h" 16 #include "base/macros.h"
16 #include "base/metrics/histogram_macros.h" 17 #include "base/metrics/histogram_macros.h"
17 #include "base/path_service.h" 18 #include "base/path_service.h"
18 #include "base/strings/string_util.h" 19 #include "base/strings/string_util.h"
19 #include "base/strings/utf_string_conversions.h" 20 #include "base/strings/utf_string_conversions.h"
20 #include "base/threading/thread.h" 21 #include "base/threading/thread.h"
21 #include "base/threading/thread_restrictions.h" 22 #include "base/threading/thread_restrictions.h"
22 #include "base/trace_event/trace_event.h" 23 #include "base/trace_event/trace_event.h"
23 #include "chrome/browser/chrome_notification_types.h" 24 #include "chrome/browser/chrome_notification_types.h"
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
56 #include "url/gurl.h" 57 #include "url/gurl.h"
57 58
58 using content::BrowserThread; 59 using content::BrowserThread;
59 using JumpListData = JumpList::JumpListData; 60 using JumpListData = JumpList::JumpListData;
60 61
61 namespace { 62 namespace {
62 63
63 // Delay jumplist updates to allow collapsing of redundant update requests. 64 // Delay jumplist updates to allow collapsing of redundant update requests.
64 const int kDelayForJumplistUpdateInMS = 3500; 65 const int kDelayForJumplistUpdateInMS = 3500;
65 66
67 // Maximum number of icon files allowed to delete per update
68 const int kMaxIconFilesDeletedPerUpdate = 100;
69
66 // Append the common switches to each shell link. 70 // Append the common switches to each shell link.
67 void AppendCommonSwitches(ShellLinkItem* shell_link) { 71 void AppendCommonSwitches(ShellLinkItem* shell_link) {
68 const char* kSwitchNames[] = { switches::kUserDataDir }; 72 const char* kSwitchNames[] = { switches::kUserDataDir };
69 const base::CommandLine& command_line = 73 const base::CommandLine& command_line =
70 *base::CommandLine::ForCurrentProcess(); 74 *base::CommandLine::ForCurrentProcess();
71 shell_link->GetCommandLine()->CopySwitchesFrom(command_line, 75 shell_link->GetCommandLine()->CopySwitchesFrom(command_line,
72 kSwitchNames, 76 kSwitchNames,
73 arraysize(kSwitchNames)); 77 arraysize(kSwitchNames));
74 } 78 }
75 79
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after
222 if (!UpdateTaskCategory(&jumplist_updater, incognito_availability)) 226 if (!UpdateTaskCategory(&jumplist_updater, incognito_availability))
223 return false; 227 return false;
224 228
225 // Commit this transaction and send the updated JumpList to Windows. 229 // Commit this transaction and send the updated JumpList to Windows.
226 if (!jumplist_updater.CommitUpdate()) 230 if (!jumplist_updater.CommitUpdate())
227 return false; 231 return false;
228 232
229 return true; 233 return true;
230 } 234 }
231 235
232 // Renames the directory |from_dir| to |to_path|. This method fails if any 236 // This method is an exact copy of base::DeleteFileRecursive except that it has
233 // process has a handle open in |from_dir| or if |to_path| exists. Base::Move() 237 // an upper limit of file numbers to delete each time.
234 // tries to rename a file and if this fails, it tries copy-n-delete; This 238 // Deletes all files and directories in a path under the limit.
235 // RenameDirectory method only does the rename part. 239 // Returns false on the first failure it encounters, or the number of files
236 bool RenameDirectory(const base::FilePath& from_path, 240 // deleted exceeds the upper limit |upper_limit|.
237 const base::FilePath& to_path) { 241 bool DeleteFileRecursiveWithUpperLimit(
242 const base::FilePath& path,
243 const base::FilePath::StringType& pattern,
244 bool recursive,
245 int upper_limit = INT_MAX) {
grt (UTC plus 2) 2017/03/20 09:15:36 it's confusing for a caller to invoke DeleteFileRe
chengx 2017/03/21 01:37:10 I renamed this method to DeleteFileRecursive, remo
246 base::FileEnumerator traversal(
247 path, false,
248 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES, pattern);
249 int file_deleted = 0;
250 for (base::FilePath current = traversal.Next(); !current.empty();
251 current = traversal.Next(), file_deleted++) {
252 // Try to clear the read-only bit if we find it.
253 base::FileEnumerator::FileInfo info = traversal.GetInfo();
254 if ((info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) &&
255 (recursive || !info.IsDirectory())) {
256 SetFileAttributes(
257 current.value().c_str(),
258 info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
259 }
260
261 if (info.IsDirectory()) {
262 if (recursive && (!DeleteFileRecursiveWithUpperLimit(current, pattern,
grt (UTC plus 2) 2017/03/20 09:15:36 |file_deleted| should not be incremented if IsDire
chengx 2017/03/21 01:37:10 I have removed the |recursive| input parameter, so
263 true, upper_limit) ||
264 !::RemoveDirectory(current.value().c_str())))
265 return false;
grt (UTC plus 2) 2017/03/20 09:15:37 nit: braces around this since the conditional span
chengx 2017/03/21 01:37:11 Done.
266 } else if (!::DeleteFile(current.value().c_str())) {
267 return false;
grt (UTC plus 2) 2017/03/20 09:15:37 why not keep going trying to delete other files in
chengx 2017/03/21 01:37:10 I think it just made sense if it returns false whe
268 }
269 if (file_deleted > upper_limit)
270 return false;
271 }
272 return true;
273 }
274
275 // This method deletes a file or content of a directory.
276 // When deleting the content of a directory, |upper_limit| can be specified to
277 // to set up the maximum number of files in this directory and any of its
278 // sub-directory.
grt (UTC plus 2) 2017/03/20 09:15:36 are there ever sub-directories in JumplistIcons{,O
chengx 2017/03/21 01:37:10 There should not be sub-dir in JumplistIcos{,Old}
279 bool DeleteDirectoryContentWithUpperLimit(const base::FilePath& path,
280 bool recursive,
grt (UTC plus 2) 2017/03/20 09:15:36 don't overly generalize this -- the only caller pa
chengx 2017/03/21 01:37:10 Sure, the input parameter |recursive| is gone now.
281 int upper_limit = INT_MAX) {
238 base::ThreadRestrictions::AssertIOAllowed(); 282 base::ThreadRestrictions::AssertIOAllowed();
239 if (from_path.ReferencesParent() || to_path.ReferencesParent()) 283
284 if (path.empty())
285 return true;
286
287 if (path.value().length() >= MAX_PATH)
240 return false; 288 return false;
241 if (from_path.value().length() >= MAX_PATH || 289
242 to_path.value().length() >= MAX_PATH) { 290 // Handle any path with wildcards.
grt (UTC plus 2) 2017/03/20 09:15:36 remove wildcard support here -- it's unused
chengx 2017/03/21 01:37:10 Done.
291 if (path.BaseName().value().find_first_of(L"*?") !=
292 base::FilePath::StringType::npos) {
293 return DeleteFileRecursiveWithUpperLimit(
294 path.DirName(), path.BaseName().value(), recursive, upper_limit);
295 }
296 DWORD attr = GetFileAttributes(path.value().c_str());
297 // We're done if we can't find the path.
298 if (attr == INVALID_FILE_ATTRIBUTES)
299 return true;
300 // We may need to clear the read-only bit.
301 if ((attr & FILE_ATTRIBUTE_READONLY) &&
302 !SetFileAttributes(path.value().c_str(),
303 attr & ~FILE_ATTRIBUTE_READONLY)) {
243 return false; 304 return false;
244 } 305 }
245 return MoveFileEx(from_path.value().c_str(), to_path.value().c_str(), 0) != 0; 306 // If |path| is a file, simply delete it.
307 if (!(attr & FILE_ATTRIBUTE_DIRECTORY))
308 return !!::DeleteFile(path.value().c_str());
309 // If |path| is a directory and |recursive| is true, delete all its content
310 // but save the row directory. The raw directory is deleted if
grt (UTC plus 2) 2017/03/20 09:15:37 row -> raw
chengx 2017/03/21 01:37:11 Done. Sorry for the typo.
311 // |remove_directory| is true.
312 return !recursive ||
313 DeleteFileRecursiveWithUpperLimit(path, L"*", true, upper_limit);
314 }
315
316 // This method deletes a file or all the content of a directory depending on
317 // what |path| is. There method is run in the asynchronous manner, so there is
318 // return value.
319 void DeleteDirectory(const base::FilePath& path, bool remove_directory = true) {
320 base::ThreadRestrictions::AssertIOAllowed();
321
322 if (path.empty() || path.value().length() >= MAX_PATH)
323 return;
324
325 DWORD attr = GetFileAttributes(path.value().c_str());
326 // We're done if we can't find the path.
327 if (attr == INVALID_FILE_ATTRIBUTES)
328 return;
329 // We may need to clear the read-only bit.
330 if ((attr & FILE_ATTRIBUTE_READONLY) &&
331 !SetFileAttributes(path.value().c_str(),
332 attr & ~FILE_ATTRIBUTE_READONLY)) {
333 return;
334 }
335 // If |path| is a file, delete it.
336 if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) {
337 ::DeleteFile(path.value().c_str());
338 return;
339 }
340 // If |path| is a directory, delete all its content. The raw directory is
341 // deleted if |remove_directory| is true.
342 if (DeleteFileRecursiveWithUpperLimit(path, L"*", true) && remove_directory)
343 ::RemoveDirectory(path.value().c_str());
344 return;
246 } 345 }
247 346
248 // Updates the jumplist, once all the data has been fetched. 347 // Updates the jumplist, once all the data has been fetched.
249 void RunUpdateOnFileThread( 348 void RunUpdateOnFileThread(
250 IncognitoModePrefs::Availability incognito_availability, 349 IncognitoModePrefs::Availability incognito_availability,
251 const std::wstring& app_id, 350 const std::wstring& app_id,
252 const base::FilePath& icon_dir, 351 const base::FilePath& icon_dir,
253 base::RefCountedData<JumpListData>* ref_counted_data) { 352 base::RefCountedData<JumpListData>* ref_counted_data) {
254 JumpListData* data = &ref_counted_data->data; 353 JumpListData* data = &ref_counted_data->data;
255 ShellLinkItemList local_most_visited_pages; 354 ShellLinkItemList local_most_visited_pages;
256 ShellLinkItemList local_recently_closed_pages; 355 ShellLinkItemList local_recently_closed_pages;
257 356
258 { 357 {
259 base::AutoLock auto_lock(data->list_lock_); 358 base::AutoLock auto_lock(data->list_lock_);
260 // Make sure we are not out of date: if icon_urls_ is not empty, then 359 // Make sure we are not out of date: if icon_urls_ is not empty, then
261 // another notification has been received since we processed this one 360 // another notification has been received since we processed this one
262 if (!data->icon_urls_.empty()) 361 if (!data->icon_urls_.empty())
263 return; 362 return;
264 363
265 // Make local copies of lists so we can release the lock. 364 // Make local copies of lists so we can release the lock.
266 local_most_visited_pages = data->most_visited_pages_; 365 local_most_visited_pages = data->most_visited_pages_;
267 local_recently_closed_pages = data->recently_closed_pages_; 366 local_recently_closed_pages = data->recently_closed_pages_;
268 } 367 }
269 368
270 // Delete the directory which contains old icon files, rename the current 369 enum FolderDeleteResult {
271 // icon directory, and create a new directory which contains new JumpList 370 SUCCEED = 0,
grt (UTC plus 2) 2017/03/20 09:15:36 SUCCEED and FAILED are very generic. do you want t
chengx 2017/03/21 01:37:11 This enum is updated. It has all the possible fail
272 // icon files. 371 FAILED,
273 base::FilePath icon_dir_old = icon_dir.DirName().Append( 372 // Add new items before this one, always keep this one at the end.
274 icon_dir.BaseName().value() + FILE_PATH_LITERAL("Old")); 373 END
275
276 enum FolderOperationResult {
277 SUCCESS = 0,
278 DELETE_DEST_FAILED = 1 << 0,
279 RENAME_FAILED = 1 << 1,
280 DELETE_SRC_CONTENT_FAILED = 1 << 2,
281 DELETE_SRC_DIR_FAILED = 1 << 3,
282 CREATE_SRC_FAILED = 1 << 4,
283 // This value is beyond the sum of all bit fields above and
284 // should remain last (shifted by one more than the last value)
285 END = 1 << 5
286 }; 374 };
287 375
288 // This variable records the status of three folder operations. 376 // This variable records the delete status of folder JumpListIcons.
289 uint32_t folder_operation_status = FolderOperationResult::SUCCESS; 377 uint32_t folder_delete_status = FolderDeleteResult::SUCCEED;
grt (UTC plus 2) 2017/03/20 09:15:37 FolderDeleteResult isn't an "enum class", so no ne
chengx 2017/03/21 01:37:11 Done.
290 378
291 base::ScopedClosureRunner log_operation_status_when_done(base::Bind( 379 base::ScopedClosureRunner log_operation_status_when_done(base::Bind(
292 [](uint32_t* folder_operation_status_ptr) { 380 [](uint32_t* folder_delete_status_ptr) {
293 UMA_HISTOGRAM_ENUMERATION( 381 UMA_HISTOGRAM_ENUMERATION(
294 "WinJumplist.DetailedFolderResultsDeleteUpdated", 382 "WinJumplist.DetailedFolderResultsDeleteUpdated",
295 *folder_operation_status_ptr, FolderOperationResult::END); 383 *folder_delete_status_ptr, FolderDeleteResult::END);
296 }, 384 },
297 base::Unretained(&folder_operation_status))); 385 base::Unretained(&folder_delete_status)));
298 386
299 // If deletion of |icon_dir_old| fails, do not rename |icon_dir| to 387 // If failing to delete the content in |icon_dir|, post a low-priority task
300 // |icon_dir_old|, instead, delete |icon_dir| directly to avoid bloating 388 // to try to delete it later and exit early.
301 // |icon_dir_old| by moving more things to it. 389 if (!DeleteDirectoryContentWithUpperLimit(icon_dir, true,
302 if (!base::DeleteFile(icon_dir_old, true)) { 390 kMaxIconFilesDeletedPerUpdate)) {
303 folder_operation_status |= FolderOperationResult::DELETE_DEST_FAILED; 391 folder_delete_status |= FolderDeleteResult::FAILED;
grt (UTC plus 2) 2017/03/20 09:15:36 is folder_delete_status a bitfield or just a varia
chengx 2017/03/21 01:37:10 In this CL, it should be changed to a variable. So
304 // If deletion of any item in |icon_dir| fails, exit early. If deletion of 392 BrowserThread::PostAfterStartupTask(
grt (UTC plus 2) 2017/03/20 09:15:36 PostAfterStartupTask is only needed if you're post
chengx 2017/03/21 01:37:10 Thanks for the reminder. This is something that ne
305 // all the items succeeds while only deletion of the dir fails, it is okay 393 FROM_HERE, BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE),
306 // to proceed. This skips creating the same directory and updating jumplist 394 base::Bind(&DeleteDirectory, icon_dir, false));
307 // icons to avoid bloating the JumplistIcons folder.
308 if (!base::DeleteFile(icon_dir, true)) {
309 if (!::PathIsDirectoryEmpty(icon_dir.value().c_str())) {
310 folder_operation_status |=
311 FolderOperationResult::DELETE_SRC_CONTENT_FAILED;
312 return;
313 }
314 folder_operation_status |= FolderOperationResult::DELETE_SRC_DIR_FAILED;
315 }
316 } else if (!RenameDirectory(icon_dir, icon_dir_old)) {
317 // If RenameDirectory() fails, delete |icon_dir| to avoid file accumulation
318 // in this directory, which can eventually lead the folder to be huge.
319 folder_operation_status |= FolderOperationResult::RENAME_FAILED;
320 // If deletion of any item in |icon_dir| fails, exit early. If deletion of
321 // all the items succeeds while only deletion of the dir fails, it is okay
322 // to proceed. This skips creating the same directory and updating jumplist
323 // icons to avoid bloating the JumplistIcons folder.
324 if (!base::DeleteFile(icon_dir, true)) {
325 if (!::PathIsDirectoryEmpty(icon_dir.value().c_str())) {
326 folder_operation_status |=
327 FolderOperationResult::DELETE_SRC_CONTENT_FAILED;
328 return;
329 }
330 folder_operation_status |= FolderOperationResult::DELETE_SRC_DIR_FAILED;
331 }
332 }
333
334 // If CreateDirectory() fails, exit early.
335 if (!base::CreateDirectory(icon_dir)) {
336 folder_operation_status |= FolderOperationResult::CREATE_SRC_FAILED;
337 return; 395 return;
338 } 396 }
339 397
340 // Create temporary icon files for shortcuts in the "Most Visited" category. 398 // Create temporary icon files for shortcuts in the "Most Visited" category.
341 CreateIconFiles(icon_dir, local_most_visited_pages); 399 CreateIconFiles(icon_dir, local_most_visited_pages);
342 400
343 // Create temporary icon files for shortcuts in the "Recently Closed" 401 // Create temporary icon files for shortcuts in the "Recently Closed"
344 // category. 402 // category.
345 CreateIconFiles(icon_dir, local_recently_closed_pages); 403 CreateIconFiles(icon_dir, local_recently_closed_pages);
346 404
347 // We finished collecting all resources needed for updating an application 405 // We finished collecting all resources needed for updating an application
348 // JumpList. So, create a new JumpList and replace the current JumpList 406 // JumpList. So, create a new JumpList and replace the current JumpList
349 // with it. 407 // with it.
350 UpdateJumpList(app_id.c_str(), local_most_visited_pages, 408 UpdateJumpList(app_id.c_str(), local_most_visited_pages,
351 local_recently_closed_pages, incognito_availability); 409 local_recently_closed_pages, incognito_availability);
410
411 // Post a low-priority task to delete the content in |icon_dir_old| if there
412 // is any.
413 base::FilePath icon_dir_old = icon_dir.DirName().Append(
414 icon_dir.BaseName().value() + FILE_PATH_LITERAL("Old"));
415
416 if (::PathFileExists(icon_dir_old.value().c_str())) {
417 BrowserThread::PostAfterStartupTask(
grt (UTC plus 2) 2017/03/20 09:15:37 comments above apply here as well. does it suffice
chengx 2017/03/21 01:37:10 Thanks for pointing to these APIs and traits that
418 FROM_HERE, BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE),
419 base::Bind(&DeleteDirectory, icon_dir_old, true));
420 }
352 } 421 }
353 422
354 } // namespace 423 } // namespace
355 424
356 JumpList::JumpListData::JumpListData() {} 425 JumpList::JumpListData::JumpListData() {}
357 426
358 JumpList::JumpListData::~JumpListData() {} 427 JumpList::JumpListData::~JumpListData() {}
359 428
360 JumpList::JumpList(Profile* profile) 429 JumpList::JumpList(Profile* profile)
361 : RefcountedKeyedService(content::BrowserThread::GetTaskRunnerForThread( 430 : RefcountedKeyedService(content::BrowserThread::GetTaskRunnerForThread(
(...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after
671 void JumpList::TopSitesLoaded(history::TopSites* top_sites) { 740 void JumpList::TopSitesLoaded(history::TopSites* top_sites) {
672 } 741 }
673 742
674 void JumpList::TopSitesChanged(history::TopSites* top_sites, 743 void JumpList::TopSitesChanged(history::TopSites* top_sites,
675 ChangeReason change_reason) { 744 ChangeReason change_reason) {
676 top_sites->GetMostVisitedURLs( 745 top_sites->GetMostVisitedURLs(
677 base::Bind(&JumpList::OnMostVisitedURLsAvailable, 746 base::Bind(&JumpList::OnMostVisitedURLsAvailable,
678 weak_ptr_factory_.GetWeakPtr()), 747 weak_ptr_factory_.GetWeakPtr()),
679 false); 748 false);
680 } 749 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698