Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 52 using JumpListData = JumpList::JumpListData; | 52 using JumpListData = JumpList::JumpListData; |
| 53 | 53 |
| 54 namespace { | 54 namespace { |
| 55 | 55 |
| 56 // Delay jumplist updates to allow collapsing of redundant update requests. | 56 // Delay jumplist updates to allow collapsing of redundant update requests. |
| 57 const int kDelayForJumplistUpdateInMS = 3500; | 57 const int kDelayForJumplistUpdateInMS = 3500; |
| 58 | 58 |
| 59 // JumpList folder's move operation status. | 59 // JumpList folder's move operation status. |
| 60 enum MoveOperationResult { | 60 enum MoveOperationResult { |
| 61 SUCCESS = 0, | 61 SUCCESS = 0, |
| 62 MAX_PATH_CHECK_FAILED = 1 << 0, | 62 MOVE_FILE_EX_FAILED = 1 << 0, |
| 63 MOVE_FILE_EX_FAILED = 1 << 1, | 63 COPY_FILE_FAILED = 1 << 1, |
| 64 COPY_AND_DELETE_DIR_FAILED = 1 << 2, | 64 DELETE_FILE_RECURSIVE_FAILED = 1 << 2, |
| 65 DELETE_FAILED = 1 << 3, | 65 DELETE_SOURCE_FOLDER_FAILED = 1 << 3, |
| 66 END = 1 << 4 | 66 DELETE_TARGET_FOLDER_FAILED = 1 << 4, |
| 67 END = 1 << 5 | |
| 67 }; | 68 }; |
| 68 | 69 |
| 69 // Append the common switches to each shell link. | 70 // Append the common switches to each shell link. |
| 70 void AppendCommonSwitches(ShellLinkItem* shell_link) { | 71 void AppendCommonSwitches(ShellLinkItem* shell_link) { |
| 71 const char* kSwitchNames[] = { switches::kUserDataDir }; | 72 const char* kSwitchNames[] = { switches::kUserDataDir }; |
| 72 const base::CommandLine& command_line = | 73 const base::CommandLine& command_line = |
| 73 *base::CommandLine::ForCurrentProcess(); | 74 *base::CommandLine::ForCurrentProcess(); |
| 74 shell_link->GetCommandLine()->CopySwitchesFrom(command_line, | 75 shell_link->GetCommandLine()->CopySwitchesFrom(command_line, |
| 75 kSwitchNames, | 76 kSwitchNames, |
| 76 arraysize(kSwitchNames)); | 77 arraysize(kSwitchNames)); |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 218 | 219 |
| 219 // Commit this transaction and send the updated JumpList to Windows. | 220 // Commit this transaction and send the updated JumpList to Windows. |
| 220 if (!jumplist_updater.CommitUpdate()) | 221 if (!jumplist_updater.CommitUpdate()) |
| 221 return false; | 222 return false; |
| 222 | 223 |
| 223 return true; | 224 return true; |
| 224 } | 225 } |
| 225 | 226 |
| 226 // A wrapper function for recording UMA histogram. | 227 // A wrapper function for recording UMA histogram. |
| 227 void RecordFolderMoveResult(int move_operation_status) { | 228 void RecordFolderMoveResult(int move_operation_status) { |
| 228 UMA_HISTOGRAM_ENUMERATION("WinJumplist.FolderMoveResults", | 229 UMA_HISTOGRAM_ENUMERATION("WinJumplist.DetailedFolderMoveResults", |
| 229 move_operation_status, MoveOperationResult::END); | 230 move_operation_status, MoveOperationResult::END); |
| 230 } | 231 } |
| 231 | 232 |
| 233 // This function is mostly copied from //base for UMA debugging purpose. | |
|
gab
2016/12/09 19:12:12
// This function is an exact copy of the one in //
chengx
2016/12/09 21:36:30
Yes, you are right. It is an exact copy. I copy it
| |
| 234 // Deletes all files and directories in a path. | |
| 235 // Returns false on the first failure it encounters. | |
| 236 bool DeleteFileRecursive(const base::FilePath& path, | |
| 237 const base::FilePath::StringType& pattern, | |
| 238 bool recursive) { | |
| 239 base::FileEnumerator traversal( | |
| 240 path, false, | |
| 241 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES, pattern); | |
| 242 for (base::FilePath current = traversal.Next(); !current.empty(); | |
| 243 current = traversal.Next()) { | |
| 244 // Try to clear the read-only bit if we find it. | |
| 245 base::FileEnumerator::FileInfo info = traversal.GetInfo(); | |
| 246 if ((info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) && | |
| 247 (recursive || !info.IsDirectory())) { | |
| 248 SetFileAttributes( | |
| 249 current.value().c_str(), | |
| 250 info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); | |
| 251 } | |
| 252 | |
| 253 if (info.IsDirectory()) { | |
| 254 if (recursive && (!DeleteFileRecursive(current, pattern, true) || | |
| 255 !RemoveDirectory(current.value().c_str()))) | |
| 256 return false; | |
| 257 } else if (!::DeleteFile(current.value().c_str())) { | |
| 258 return false; | |
| 259 } | |
| 260 } | |
| 261 return true; | |
| 262 } | |
| 263 | |
| 264 // This function is mostly copied from //base for UMA debugging purpose. | |
|
gab
2016/12/09 19:12:12
// This function is a copy of the one in //base au
chengx
2016/12/09 21:36:31
Done.
| |
| 265 // Deletes all files and directories in a path. | |
| 266 // Returns false on the first failure it encounters. | |
| 267 bool DeleteFileTemp(const base::FilePath& path, | |
|
gab
2016/12/09 19:12:12
naming this "DeleteFile" is fine IMO, it won't be
chengx
2016/12/09 21:36:30
Done.
| |
| 268 bool recursive, | |
| 269 int* move_operation_status) { | |
| 270 base::ThreadRestrictions::AssertIOAllowed(); | |
| 271 | |
| 272 if (path.empty()) | |
| 273 return true; | |
| 274 | |
| 275 if (path.value().length() >= MAX_PATH) | |
| 276 return false; | |
| 277 | |
| 278 // Handle any path with wildcards. | |
| 279 if (path.BaseName().value().find_first_of(L"*?") != | |
| 280 base::FilePath::StringType::npos) { | |
| 281 return DeleteFileRecursive(path.DirName(), path.BaseName().value(), | |
|
gab
2016/12/09 19:12:12
Not worried about this failing?
chengx
2016/12/09 21:36:31
I don't think it will be hit in my case. But I add
| |
| 282 recursive); | |
| 283 } | |
| 284 DWORD attr = GetFileAttributes(path.value().c_str()); | |
| 285 // We're done if we can't find the path. | |
| 286 if (attr == INVALID_FILE_ATTRIBUTES) | |
| 287 return true; | |
| 288 // We may need to clear the read-only bit. | |
| 289 if ((attr & FILE_ATTRIBUTE_READONLY) && | |
| 290 !SetFileAttributes(path.value().c_str(), | |
|
gab
2016/12/09 19:12:12
Since we now have methods in file scope, some in b
chengx
2016/12/09 21:36:31
Done.
| |
| 291 attr & ~FILE_ATTRIBUTE_READONLY)) { | |
| 292 return false; | |
|
gab
2016/12/09 19:12:12
Not worried about this failing?
chengx
2016/12/09 21:36:30
Just added UMA for this. Thanks for reminder.
| |
| 293 } | |
| 294 // Directories are handled differently if they're recursive. | |
| 295 if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) | |
| 296 return !!::DeleteFile(path.value().c_str()); | |
|
gab
2016/12/09 19:12:12
#include <windows.h> for ::DeleteFile() and other
chengx
2016/12/09 21:36:31
Done.
| |
| 297 // Handle a simple, single file delete. | |
| 298 if (!recursive || DeleteFileRecursive(path, L"*", true)) { | |
| 299 bool ret = !!RemoveDirectory(path.value().c_str()); | |
| 300 if (!ret) | |
| 301 *move_operation_status |= | |
| 302 MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED; | |
| 303 return ret; | |
| 304 } | |
| 305 | |
| 306 *move_operation_status |= MoveOperationResult::DELETE_FILE_RECURSIVE_FAILED; | |
| 307 *move_operation_status |= MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED; | |
| 308 return false; | |
| 309 } | |
| 310 | |
| 232 // This function is a temporary fork of Move() and MoveUnsafe() in | 311 // This function is a temporary fork of Move() and MoveUnsafe() in |
| 233 // base/files/file_util.h, where UMA functions are added to gather user data. | 312 // base/files/file_util.h, where UMA functions are added to gather user data. |
| 234 // As //base is highly mature, we tend not to add this temporary function | 313 // As //base is highly mature, we tend not to add this temporary function |
| 235 // in it. This change will be reverted after user data gathered. | 314 // in it. This change will be reverted after user data gathered. |
| 236 bool MoveDebugWithUMA(const base::FilePath& from_path, | 315 bool MoveDebugWithUMA(const base::FilePath& from_path, |
| 237 const base::FilePath& to_path, | 316 const base::FilePath& to_path, |
| 238 int folder_delete_fail) { | 317 int folder_delete_fail) { |
| 239 if (from_path.ReferencesParent() || to_path.ReferencesParent()) | 318 if (from_path.ReferencesParent() || to_path.ReferencesParent()) |
| 240 return false; | 319 return false; |
| 241 | 320 |
| 242 base::ThreadRestrictions::AssertIOAllowed(); | 321 base::ThreadRestrictions::AssertIOAllowed(); |
| 243 | 322 |
| 244 // This variable records the status of move operations. | 323 // This variable records the status of move operations. |
| 245 int move_operation_status = MoveOperationResult::SUCCESS; | 324 int move_operation_status = MoveOperationResult::SUCCESS; |
| 246 if (folder_delete_fail) | 325 if (folder_delete_fail) |
| 247 move_operation_status |= MoveOperationResult::DELETE_FAILED; | 326 move_operation_status |= MoveOperationResult::DELETE_TARGET_FOLDER_FAILED; |
| 248 | 327 |
| 249 // NOTE: I suspect we could support longer paths, but that would involve | 328 // NOTE: I suspect we could support longer paths, but that would involve |
| 250 // analyzing all our usage of files. | 329 // analyzing all our usage of files. |
| 251 if (from_path.value().length() >= MAX_PATH || | 330 if (from_path.value().length() >= MAX_PATH || |
| 252 to_path.value().length() >= MAX_PATH) { | 331 to_path.value().length() >= MAX_PATH) { |
| 253 move_operation_status |= MoveOperationResult::MAX_PATH_CHECK_FAILED; | |
| 254 RecordFolderMoveResult(move_operation_status); | |
| 255 return false; | 332 return false; |
| 256 } | 333 } |
| 257 if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(), | 334 if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(), |
| 258 MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0) { | 335 MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0) { |
| 259 RecordFolderMoveResult(move_operation_status); | 336 RecordFolderMoveResult(move_operation_status); |
| 260 return true; | 337 return true; |
| 261 } | 338 } |
| 262 | 339 |
| 263 move_operation_status |= MoveOperationResult::MOVE_FILE_EX_FAILED; | 340 move_operation_status |= MoveOperationResult::MOVE_FILE_EX_FAILED; |
| 264 | 341 |
| 265 // Keep the last error value from MoveFileEx around in case the below | 342 // Keep the last error value from MoveFileEx around in case the below |
| 266 // fails. | 343 // fails. |
| 267 bool ret = false; | 344 bool ret = false; |
| 268 DWORD last_error = ::GetLastError(); | 345 DWORD last_error = ::GetLastError(); |
| 269 | 346 |
| 270 if (base::DirectoryExists(from_path)) { | 347 if (base::DirectoryExists(from_path)) { |
| 271 // MoveFileEx fails if moving directory across volumes. We will simulate | 348 // MoveFileEx fails if moving directory across volumes. We will simulate |
| 272 // the move by using Copy and Delete. Ideally we could check whether | 349 // the move by using Copy and Delete. Ideally we could check whether |
| 273 // from_path and to_path are indeed in different volumes. | 350 // from_path and to_path are indeed in different volumes. |
| 274 ret = base::internal::CopyAndDeleteDirectory(from_path, to_path); | 351 if (base::CopyDirectory(from_path, to_path, true)) { |
| 352 if (DeleteFileTemp(from_path, true, &move_operation_status)) | |
| 353 ret = true; | |
| 354 } else { | |
| 355 move_operation_status |= MoveOperationResult::COPY_FILE_FAILED; | |
|
gab
2016/12/09 19:12:13
This is copying a directory, not a "file", right?
chengx
2016/12/09 21:36:31
Yes, you are right.
| |
| 356 move_operation_status |= | |
| 357 MoveOperationResult::DELETE_FILE_RECURSIVE_FAILED; | |
| 358 move_operation_status |= MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED; | |
| 359 } | |
| 275 } | 360 } |
| 276 | 361 |
| 277 if (!ret) { | 362 if (!ret) { |
| 278 // Leave a clue about what went wrong so that it can be (at least) picked | 363 // Leave a clue about what went wrong so that it can be (at least) picked |
| 279 // up by a PLOG entry. | 364 // up by a PLOG entry. |
| 280 ::SetLastError(last_error); | 365 ::SetLastError(last_error); |
| 281 move_operation_status |= MoveOperationResult::COPY_AND_DELETE_DIR_FAILED; | |
| 282 } | 366 } |
| 283 | 367 |
| 284 RecordFolderMoveResult(move_operation_status); | 368 RecordFolderMoveResult(move_operation_status); |
| 285 | 369 |
| 286 return ret; | 370 return ret; |
| 287 } | 371 } |
| 288 | 372 |
| 289 // Updates the jumplist, once all the data has been fetched. | 373 // Updates the jumplist, once all the data has been fetched. |
| 290 void RunUpdateOnFileThread( | 374 void RunUpdateOnFileThread( |
| 291 IncognitoModePrefs::Availability incognito_availability, | 375 IncognitoModePrefs::Availability incognito_availability, |
| (...skipping 379 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 671 void JumpList::TopSitesLoaded(history::TopSites* top_sites) { | 755 void JumpList::TopSitesLoaded(history::TopSites* top_sites) { |
| 672 } | 756 } |
| 673 | 757 |
| 674 void JumpList::TopSitesChanged(history::TopSites* top_sites, | 758 void JumpList::TopSitesChanged(history::TopSites* top_sites, |
| 675 ChangeReason change_reason) { | 759 ChangeReason change_reason) { |
| 676 top_sites->GetMostVisitedURLs( | 760 top_sites->GetMostVisitedURLs( |
| 677 base::Bind(&JumpList::OnMostVisitedURLsAvailable, | 761 base::Bind(&JumpList::OnMostVisitedURLsAvailable, |
| 678 weak_ptr_factory_.GetWeakPtr()), | 762 weak_ptr_factory_.GetWeakPtr()), |
| 679 false); | 763 false); |
| 680 } | 764 } |
| OLD | NEW |