| 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 <windows.h> | |
| 8 | |
| 9 #include "base/bind.h" | 7 #include "base/bind.h" |
| 10 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
| 11 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 12 #include "base/files/file_enumerator.h" | |
| 13 #include "base/files/file_util.h" | 10 #include "base/files/file_util.h" |
| 14 #include "base/macros.h" | 11 #include "base/macros.h" |
| 15 #include "base/metrics/histogram_macros.h" | 12 #include "base/metrics/histogram_macros.h" |
| 16 #include "base/path_service.h" | 13 #include "base/path_service.h" |
| 17 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
| 18 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
| 19 #include "base/threading/thread.h" | 16 #include "base/threading/thread.h" |
| 20 #include "base/threading/thread_restrictions.h" | |
| 21 #include "base/trace_event/trace_event.h" | 17 #include "base/trace_event/trace_event.h" |
| 22 #include "chrome/browser/chrome_notification_types.h" | 18 #include "chrome/browser/chrome_notification_types.h" |
| 23 #include "chrome/browser/favicon/favicon_service_factory.h" | 19 #include "chrome/browser/favicon/favicon_service_factory.h" |
| 24 #include "chrome/browser/history/top_sites_factory.h" | 20 #include "chrome/browser/history/top_sites_factory.h" |
| 25 #include "chrome/browser/metrics/jumplist_metrics_win.h" | 21 #include "chrome/browser/metrics/jumplist_metrics_win.h" |
| 26 #include "chrome/browser/profiles/profile.h" | 22 #include "chrome/browser/profiles/profile.h" |
| 27 #include "chrome/browser/sessions/tab_restore_service_factory.h" | 23 #include "chrome/browser/sessions/tab_restore_service_factory.h" |
| 28 #include "chrome/browser/shell_integration_win.h" | 24 #include "chrome/browser/shell_integration_win.h" |
| 29 #include "chrome/common/chrome_constants.h" | 25 #include "chrome/common/chrome_constants.h" |
| 30 #include "chrome/common/chrome_switches.h" | 26 #include "chrome/common/chrome_switches.h" |
| (...skipping 21 matching lines...) Expand all Loading... |
| 52 #include "url/gurl.h" | 48 #include "url/gurl.h" |
| 53 | 49 |
| 54 using content::BrowserThread; | 50 using content::BrowserThread; |
| 55 using JumpListData = JumpList::JumpListData; | 51 using JumpListData = JumpList::JumpListData; |
| 56 | 52 |
| 57 namespace { | 53 namespace { |
| 58 | 54 |
| 59 // Delay jumplist updates to allow collapsing of redundant update requests. | 55 // Delay jumplist updates to allow collapsing of redundant update requests. |
| 60 const int kDelayForJumplistUpdateInMS = 3500; | 56 const int kDelayForJumplistUpdateInMS = 3500; |
| 61 | 57 |
| 62 // JumpList folder's move operation status. | |
| 63 enum MoveOperationResult { | |
| 64 SUCCESS = 0, | |
| 65 MOVE_FILE_EX_FAILED = 1 << 0, | |
| 66 COPY_DIR_FAILED = 1 << 1, | |
| 67 DELETE_FILE_RECURSIVE_FAILED = 1 << 2, | |
| 68 DELETE_SOURCE_FOLDER_FAILED = 1 << 3, | |
| 69 DELETE_TARGET_FOLDER_FAILED = 1 << 4, | |
| 70 DELETE_DIR_READ_ONLY = 1 << 5, | |
| 71 END = 1 << 6 | |
| 72 }; | |
| 73 | |
| 74 // Append the common switches to each shell link. | 58 // Append the common switches to each shell link. |
| 75 void AppendCommonSwitches(ShellLinkItem* shell_link) { | 59 void AppendCommonSwitches(ShellLinkItem* shell_link) { |
| 76 const char* kSwitchNames[] = { switches::kUserDataDir }; | 60 const char* kSwitchNames[] = { switches::kUserDataDir }; |
| 77 const base::CommandLine& command_line = | 61 const base::CommandLine& command_line = |
| 78 *base::CommandLine::ForCurrentProcess(); | 62 *base::CommandLine::ForCurrentProcess(); |
| 79 shell_link->GetCommandLine()->CopySwitchesFrom(command_line, | 63 shell_link->GetCommandLine()->CopySwitchesFrom(command_line, |
| 80 kSwitchNames, | 64 kSwitchNames, |
| 81 arraysize(kSwitchNames)); | 65 arraysize(kSwitchNames)); |
| 82 } | 66 } |
| 83 | 67 |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 221 if (!UpdateTaskCategory(&jumplist_updater, incognito_availability)) | 205 if (!UpdateTaskCategory(&jumplist_updater, incognito_availability)) |
| 222 return false; | 206 return false; |
| 223 | 207 |
| 224 // Commit this transaction and send the updated JumpList to Windows. | 208 // Commit this transaction and send the updated JumpList to Windows. |
| 225 if (!jumplist_updater.CommitUpdate()) | 209 if (!jumplist_updater.CommitUpdate()) |
| 226 return false; | 210 return false; |
| 227 | 211 |
| 228 return true; | 212 return true; |
| 229 } | 213 } |
| 230 | 214 |
| 231 // A wrapper function for recording UMA histogram. | |
| 232 void RecordFolderMoveResult(int move_operation_status) { | |
| 233 UMA_HISTOGRAM_ENUMERATION("WinJumplist.DetailedFolderMoveResults", | |
| 234 move_operation_status, MoveOperationResult::END); | |
| 235 } | |
| 236 | |
| 237 // This function is an exact copy of //base for UMA debugging purpose of | |
| 238 // DeleteFile() below. | |
| 239 // Deletes all files and directories in a path. | |
| 240 // Returns false on the first failure it encounters. | |
| 241 bool DeleteFileRecursive(const base::FilePath& path, | |
| 242 const base::FilePath::StringType& pattern, | |
| 243 bool recursive) { | |
| 244 base::FileEnumerator traversal( | |
| 245 path, false, | |
| 246 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES, pattern); | |
| 247 for (base::FilePath current = traversal.Next(); !current.empty(); | |
| 248 current = traversal.Next()) { | |
| 249 // Try to clear the read-only bit if we find it. | |
| 250 base::FileEnumerator::FileInfo info = traversal.GetInfo(); | |
| 251 if ((info.find_data().dwFileAttributes & FILE_ATTRIBUTE_READONLY) && | |
| 252 (recursive || !info.IsDirectory())) { | |
| 253 ::SetFileAttributes( | |
| 254 current.value().c_str(), | |
| 255 info.find_data().dwFileAttributes & ~FILE_ATTRIBUTE_READONLY); | |
| 256 } | |
| 257 | |
| 258 if (info.IsDirectory()) { | |
| 259 if (recursive && (!DeleteFileRecursive(current, pattern, true) || | |
| 260 !::RemoveDirectory(current.value().c_str()))) | |
| 261 return false; | |
| 262 } else if (!::DeleteFile(current.value().c_str())) { | |
| 263 return false; | |
| 264 } | |
| 265 } | |
| 266 return true; | |
| 267 } | |
| 268 | |
| 269 // This function is a copy from //base for UMA debugging purpose. | |
| 270 // Deletes all files and directories in a path. | |
| 271 // Returns false on the first failure it encounters. | |
| 272 bool DeleteFile(const base::FilePath& path, | |
| 273 bool recursive, | |
| 274 int* move_operation_status) { | |
| 275 base::ThreadRestrictions::AssertIOAllowed(); | |
| 276 | |
| 277 if (path.empty()) | |
| 278 return true; | |
| 279 | |
| 280 if (path.value().length() >= MAX_PATH) | |
| 281 return false; | |
| 282 | |
| 283 // Handle any path with wildcards. | |
| 284 if (path.BaseName().value().find_first_of(L"*?") != | |
| 285 base::FilePath::StringType::npos) { | |
| 286 bool ret = | |
| 287 DeleteFileRecursive(path.DirName(), path.BaseName().value(), recursive); | |
| 288 if (!ret) { | |
| 289 *move_operation_status |= | |
| 290 MoveOperationResult::DELETE_FILE_RECURSIVE_FAILED; | |
| 291 *move_operation_status |= | |
| 292 MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED; | |
| 293 } | |
| 294 return ret; | |
| 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)) { | |
| 304 *move_operation_status |= MoveOperationResult::DELETE_DIR_READ_ONLY; | |
| 305 *move_operation_status |= MoveOperationResult::DELETE_FILE_RECURSIVE_FAILED; | |
| 306 *move_operation_status |= MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED; | |
| 307 return false; | |
| 308 } | |
| 309 // Directories are handled differently if they're recursive. | |
| 310 if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) | |
| 311 return !!::DeleteFile(path.value().c_str()); | |
| 312 // Handle a simple, single file delete. | |
| 313 if (!recursive || DeleteFileRecursive(path, L"*", true)) { | |
| 314 bool ret = !!::RemoveDirectory(path.value().c_str()); | |
| 315 if (!ret) | |
| 316 *move_operation_status |= | |
| 317 MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED; | |
| 318 return ret; | |
| 319 } | |
| 320 | |
| 321 *move_operation_status |= MoveOperationResult::DELETE_FILE_RECURSIVE_FAILED; | |
| 322 *move_operation_status |= MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED; | |
| 323 return false; | |
| 324 } | |
| 325 | |
| 326 // This function is mostly copied from //base. | |
| 327 // It has some issue when the dest dir string contains the src dir string. | |
| 328 // Temporary fix by replacing the old logic with IsParent() call. | |
| 329 bool CopyDirectoryTemp(const base::FilePath& from_path, | |
| 330 const base::FilePath& to_path, | |
| 331 bool recursive) { | |
| 332 // NOTE(maruel): Previous version of this function used to call | |
| 333 // SHFileOperation(). This used to copy the file attributes and extended | |
| 334 // attributes, OLE structured storage, NTFS file system alternate data | |
| 335 // streams, SECURITY_DESCRIPTOR. In practice, this is not what we want, we | |
| 336 // want the containing directory to propagate its SECURITY_DESCRIPTOR. | |
| 337 base::ThreadRestrictions::AssertIOAllowed(); | |
| 338 | |
| 339 // NOTE: I suspect we could support longer paths, but that would involve | |
| 340 // analyzing all our usage of files. | |
| 341 if (from_path.value().length() >= MAX_PATH || | |
| 342 to_path.value().length() >= MAX_PATH) { | |
| 343 return false; | |
| 344 } | |
| 345 | |
| 346 // This function does not properly handle destinations within the source. | |
| 347 base::FilePath real_to_path = to_path; | |
| 348 if (base::PathExists(real_to_path)) { | |
| 349 real_to_path = base::MakeAbsoluteFilePath(real_to_path); | |
| 350 if (real_to_path.empty()) | |
| 351 return false; | |
| 352 } else { | |
| 353 real_to_path = base::MakeAbsoluteFilePath(real_to_path.DirName()); | |
| 354 if (real_to_path.empty()) | |
| 355 return false; | |
| 356 } | |
| 357 base::FilePath real_from_path = base::MakeAbsoluteFilePath(from_path); | |
| 358 if (real_from_path.empty()) | |
| 359 return false; | |
| 360 | |
| 361 // Originally this CopyDirectory function returns false when to_path string | |
| 362 // contains from_path string. While this can be that the to_path is within | |
| 363 // the from_path, e.g., parent-child relationship in terms of folder, it | |
| 364 // also can be the two paths are in the same folder, just that one name | |
| 365 // contains | |
| 366 // the other, e.g. C:\\JumpListIcon and C:\\JumpListIconOld. | |
| 367 // We make this function not return false in the latter situation by calling | |
| 368 // IsParent() function. | |
| 369 if (real_to_path.value().size() >= real_from_path.value().size() && | |
| 370 real_from_path.IsParent(real_to_path)) { | |
| 371 return false; | |
| 372 } | |
| 373 | |
| 374 int traverse_type = base::FileEnumerator::FILES; | |
| 375 if (recursive) | |
| 376 traverse_type |= base::FileEnumerator::DIRECTORIES; | |
| 377 base::FileEnumerator traversal(from_path, recursive, traverse_type); | |
| 378 | |
| 379 if (!base::PathExists(from_path)) { | |
| 380 DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: " | |
| 381 << from_path.value().c_str(); | |
| 382 return false; | |
| 383 } | |
| 384 // TODO(maruel): This is not necessary anymore. | |
| 385 DCHECK(recursive || base::DirectoryExists(from_path)); | |
| 386 | |
| 387 base::FilePath current = from_path; | |
| 388 bool from_is_dir = base::DirectoryExists(from_path); | |
| 389 bool success = true; | |
| 390 base::FilePath from_path_base = from_path; | |
| 391 if (recursive && base::DirectoryExists(to_path)) { | |
| 392 // If the destination already exists and is a directory, then the | |
| 393 // top level of source needs to be copied. | |
| 394 from_path_base = from_path.DirName(); | |
| 395 } | |
| 396 | |
| 397 while (success && !current.empty()) { | |
| 398 // current is the source path, including from_path, so append | |
| 399 // the suffix after from_path to to_path to create the target_path. | |
| 400 base::FilePath target_path(to_path); | |
| 401 if (from_path_base != current) { | |
| 402 if (!from_path_base.AppendRelativePath(current, &target_path)) { | |
| 403 success = false; | |
| 404 break; | |
| 405 } | |
| 406 } | |
| 407 | |
| 408 if (from_is_dir) { | |
| 409 if (!base::DirectoryExists(target_path) && | |
| 410 !::CreateDirectory(target_path.value().c_str(), NULL)) { | |
| 411 DLOG(ERROR) << "CopyDirectory() couldn't create directory: " | |
| 412 << target_path.value().c_str(); | |
| 413 success = false; | |
| 414 } | |
| 415 } else if (!base::CopyFile(current, target_path)) { | |
| 416 DLOG(ERROR) << "CopyDirectory() couldn't create file: " | |
| 417 << target_path.value().c_str(); | |
| 418 success = false; | |
| 419 } | |
| 420 | |
| 421 current = traversal.Next(); | |
| 422 if (!current.empty()) | |
| 423 from_is_dir = traversal.GetInfo().IsDirectory(); | |
| 424 } | |
| 425 | |
| 426 return success; | |
| 427 } | |
| 428 | |
| 429 // This function is a temporary fork of Move() and MoveUnsafe() in | |
| 430 // base/files/file_util.h, where UMA functions are added to gather user data. | |
| 431 // As //base is highly mature, we tend not to add this temporary function | |
| 432 // in it. This change will be reverted after user data gathered. | |
| 433 bool MoveDebugWithUMA(const base::FilePath& from_path, | |
| 434 const base::FilePath& to_path, | |
| 435 int folder_delete_fail) { | |
| 436 if (from_path.ReferencesParent() || to_path.ReferencesParent()) | |
| 437 return false; | |
| 438 | |
| 439 base::ThreadRestrictions::AssertIOAllowed(); | |
| 440 | |
| 441 // This variable records the status of move operations. | |
| 442 int move_operation_status = MoveOperationResult::SUCCESS; | |
| 443 if (folder_delete_fail) | |
| 444 move_operation_status |= MoveOperationResult::DELETE_TARGET_FOLDER_FAILED; | |
| 445 | |
| 446 // NOTE: I suspect we could support longer paths, but that would involve | |
| 447 // analyzing all our usage of files. | |
| 448 if (from_path.value().length() >= MAX_PATH || | |
| 449 to_path.value().length() >= MAX_PATH) { | |
| 450 return false; | |
| 451 } | |
| 452 if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(), | |
| 453 MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0) { | |
| 454 RecordFolderMoveResult(move_operation_status); | |
| 455 return true; | |
| 456 } | |
| 457 | |
| 458 move_operation_status |= MoveOperationResult::MOVE_FILE_EX_FAILED; | |
| 459 | |
| 460 // Keep the last error value from MoveFileEx around in case the below | |
| 461 // fails. | |
| 462 bool ret = false; | |
| 463 DWORD last_error = ::GetLastError(); | |
| 464 | |
| 465 if (base::DirectoryExists(from_path)) { | |
| 466 // MoveFileEx fails if moving directory across volumes. We will simulate | |
| 467 // the move by using Copy and Delete. Ideally we could check whether | |
| 468 // from_path and to_path are indeed in different volumes. | |
| 469 if (CopyDirectoryTemp(from_path, to_path, true)) { | |
| 470 if (DeleteFile(from_path, true, &move_operation_status)) | |
| 471 ret = true; | |
| 472 } else { | |
| 473 move_operation_status |= MoveOperationResult::COPY_DIR_FAILED; | |
| 474 move_operation_status |= | |
| 475 MoveOperationResult::DELETE_FILE_RECURSIVE_FAILED; | |
| 476 move_operation_status |= MoveOperationResult::DELETE_SOURCE_FOLDER_FAILED; | |
| 477 } | |
| 478 } | |
| 479 | |
| 480 if (!ret) { | |
| 481 // Leave a clue about what went wrong so that it can be (at least) picked | |
| 482 // up by a PLOG entry. | |
| 483 ::SetLastError(last_error); | |
| 484 } | |
| 485 | |
| 486 RecordFolderMoveResult(move_operation_status); | |
| 487 | |
| 488 return ret; | |
| 489 } | |
| 490 | |
| 491 // Updates the jumplist, once all the data has been fetched. | 215 // Updates the jumplist, once all the data has been fetched. |
| 492 void RunUpdateOnFileThread( | 216 void RunUpdateOnFileThread( |
| 493 IncognitoModePrefs::Availability incognito_availability, | 217 IncognitoModePrefs::Availability incognito_availability, |
| 494 const std::wstring& app_id, | 218 const std::wstring& app_id, |
| 495 const base::FilePath& icon_dir, | 219 const base::FilePath& icon_dir, |
| 496 base::RefCountedData<JumpListData>* ref_counted_data) { | 220 base::RefCountedData<JumpListData>* ref_counted_data) { |
| 497 JumpListData* data = &ref_counted_data->data; | 221 JumpListData* data = &ref_counted_data->data; |
| 498 ShellLinkItemList local_most_visited_pages; | 222 ShellLinkItemList local_most_visited_pages; |
| 499 ShellLinkItemList local_recently_closed_pages; | 223 ShellLinkItemList local_recently_closed_pages; |
| 500 | 224 |
| 501 { | 225 { |
| 502 base::AutoLock auto_lock(data->list_lock_); | 226 base::AutoLock auto_lock(data->list_lock_); |
| 503 // Make sure we are not out of date: if icon_urls_ is not empty, then | 227 // Make sure we are not out of date: if icon_urls_ is not empty, then |
| 504 // another notification has been received since we processed this one | 228 // another notification has been received since we processed this one |
| 505 if (!data->icon_urls_.empty()) | 229 if (!data->icon_urls_.empty()) |
| 506 return; | 230 return; |
| 507 | 231 |
| 508 // Make local copies of lists so we can release the lock. | 232 // Make local copies of lists so we can release the lock. |
| 509 local_most_visited_pages = data->most_visited_pages_; | 233 local_most_visited_pages = data->most_visited_pages_; |
| 510 local_recently_closed_pages = data->recently_closed_pages_; | 234 local_recently_closed_pages = data->recently_closed_pages_; |
| 511 } | 235 } |
| 512 | 236 |
| 513 // Delete the directory which contains old icon files, rename the current | 237 // Delete the directory which contains old icon files, rename the current |
| 514 // icon directory, and create a new directory which contains new JumpList | 238 // icon directory, and create a new directory which contains new JumpList |
| 515 // icon files. | 239 // icon files. |
| 516 base::FilePath icon_dir_old(icon_dir.value() + L"Old"); | 240 base::FilePath icon_dir_old(icon_dir.value() + L"Old"); |
| 517 | 241 |
| 518 enum FolderOperationResult { | 242 enum FolderOperationResult { |
| 519 SUCCESS = 0, | 243 SUCCESS = 0, |
| 520 DELETE_FAILED = 1 << 0, | 244 DELETE_DEST_FAILED = 1 << 0, |
| 521 MOVE_FAILED = 1 << 1, | 245 MOVE_FAILED = 1 << 1, |
| 522 CREATE_DIR_FAILED = 1 << 2, | 246 DELETE_SRC_FAILED = 1 << 2, |
| 247 CREATE_SRC_FAILED = 1 << 3, |
| 523 // This value is beyond the sum of all bit fields above and | 248 // This value is beyond the sum of all bit fields above and |
| 524 // should remain last (shifted by one more than the last value) | 249 // should remain last (shifted by one more than the last value) |
| 525 END = 1 << 3 | 250 END = 1 << 4 |
| 526 }; | 251 }; |
| 527 | 252 |
| 528 // This variable records the status of three folder operations. | 253 // This variable records the status of three folder operations. |
| 529 int folder_operation_status = FolderOperationResult::SUCCESS; | 254 int folder_operation_status = FolderOperationResult::SUCCESS; |
| 530 | 255 |
| 531 if (base::PathExists(icon_dir_old) && !base::DeleteFile(icon_dir_old, true)) | 256 if (base::PathExists(icon_dir_old) && !base::DeleteFile(icon_dir_old, true)) |
| 532 folder_operation_status |= FolderOperationResult::DELETE_FAILED; | 257 folder_operation_status |= FolderOperationResult::DELETE_DEST_FAILED; |
| 533 if (!MoveDebugWithUMA(icon_dir, icon_dir_old, folder_operation_status)) | 258 if (!base::Move(icon_dir, icon_dir_old)) { |
| 534 folder_operation_status |= FolderOperationResult::MOVE_FAILED; | 259 folder_operation_status |= FolderOperationResult::MOVE_FAILED; |
| 260 // If Move() fails, we force to delete |icon_dir| to avoide file |
| 261 // accumulation |
| 262 // in this directory, which can eventually lead the folder to be huge. |
| 263 if (!base::DeleteFile(icon_dir, true)) |
| 264 folder_operation_status |= FolderOperationResult::DELETE_SRC_FAILED; |
| 265 } |
| 535 if (!base::CreateDirectory(icon_dir)) | 266 if (!base::CreateDirectory(icon_dir)) |
| 536 folder_operation_status |= FolderOperationResult::CREATE_DIR_FAILED; | 267 folder_operation_status |= FolderOperationResult::CREATE_SRC_FAILED; |
| 537 | 268 |
| 538 UMA_HISTOGRAM_ENUMERATION("WinJumplist.FolderResults", | 269 UMA_HISTOGRAM_ENUMERATION("WinJumplist.DetailedFolderResults", |
| 539 folder_operation_status, | 270 folder_operation_status, |
| 540 FolderOperationResult::END); | 271 FolderOperationResult::END); |
| 541 | 272 |
| 542 // Create temporary icon files for shortcuts in the "Most Visited" category. | 273 // Create temporary icon files for shortcuts in the "Most Visited" category. |
| 543 CreateIconFiles(icon_dir, local_most_visited_pages); | 274 CreateIconFiles(icon_dir, local_most_visited_pages); |
| 544 | 275 |
| 545 // Create temporary icon files for shortcuts in the "Recently Closed" | 276 // Create temporary icon files for shortcuts in the "Recently Closed" |
| 546 // category. | 277 // category. |
| 547 CreateIconFiles(icon_dir, local_recently_closed_pages); | 278 CreateIconFiles(icon_dir, local_recently_closed_pages); |
| 548 | 279 |
| (...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 873 void JumpList::TopSitesLoaded(history::TopSites* top_sites) { | 604 void JumpList::TopSitesLoaded(history::TopSites* top_sites) { |
| 874 } | 605 } |
| 875 | 606 |
| 876 void JumpList::TopSitesChanged(history::TopSites* top_sites, | 607 void JumpList::TopSitesChanged(history::TopSites* top_sites, |
| 877 ChangeReason change_reason) { | 608 ChangeReason change_reason) { |
| 878 top_sites->GetMostVisitedURLs( | 609 top_sites->GetMostVisitedURLs( |
| 879 base::Bind(&JumpList::OnMostVisitedURLsAvailable, | 610 base::Bind(&JumpList::OnMostVisitedURLsAvailable, |
| 880 weak_ptr_factory_.GetWeakPtr()), | 611 weak_ptr_factory_.GetWeakPtr()), |
| 881 false); | 612 false); |
| 882 } | 613 } |
| OLD | NEW |