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/base_paths.h" | 7 #include "base/base_paths.h" |
| 8 #include "base/bind.h" | 8 #include "base/bind.h" |
| 9 #include "base/bind_helpers.h" | 9 #include "base/bind_helpers.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 31 #include "chrome/browser/shell_integration_win.h" | 31 #include "chrome/browser/shell_integration_win.h" |
| 32 #include "chrome/browser/win/jumplist_file_util.h" | 32 #include "chrome/browser/win/jumplist_file_util.h" |
| 33 #include "chrome/browser/win/jumplist_update_util.h" | 33 #include "chrome/browser/win/jumplist_update_util.h" |
| 34 #include "chrome/common/chrome_constants.h" | 34 #include "chrome/common/chrome_constants.h" |
| 35 #include "chrome/common/chrome_icon_resources_win.h" | 35 #include "chrome/common/chrome_icon_resources_win.h" |
| 36 #include "chrome/common/chrome_switches.h" | 36 #include "chrome/common/chrome_switches.h" |
| 37 #include "chrome/common/pref_names.h" | 37 #include "chrome/common/pref_names.h" |
| 38 #include "chrome/grit/generated_resources.h" | 38 #include "chrome/grit/generated_resources.h" |
| 39 #include "chrome/install_static/install_util.h" | 39 #include "chrome/install_static/install_util.h" |
| 40 #include "components/favicon/core/favicon_service.h" | 40 #include "components/favicon/core/favicon_service.h" |
| 41 #include "components/history/core/browser/history_service.h" | |
| 41 #include "components/history/core/browser/top_sites.h" | 42 #include "components/history/core/browser/top_sites.h" |
| 42 #include "components/prefs/pref_change_registrar.h" | 43 #include "components/prefs/pref_change_registrar.h" |
| 43 #include "components/sessions/core/session_types.h" | 44 #include "components/sessions/core/session_types.h" |
| 44 #include "components/strings/grit/components_strings.h" | 45 #include "components/strings/grit/components_strings.h" |
| 46 #include "content/public/browser/browser_thread.h" | |
|
grt (UTC plus 2)
2017/06/12 20:43:39
is this now unused?
chengx
2017/06/12 22:05:30
Done. Deleted.
| |
| 45 #include "ui/base/l10n/l10n_util.h" | 47 #include "ui/base/l10n/l10n_util.h" |
| 46 #include "ui/gfx/codec/png_codec.h" | 48 #include "ui/gfx/codec/png_codec.h" |
| 47 #include "ui/gfx/favicon_size.h" | 49 #include "ui/gfx/favicon_size.h" |
| 48 #include "ui/gfx/icon_util.h" | 50 #include "ui/gfx/icon_util.h" |
| 49 #include "ui/gfx/image/image.h" | 51 #include "ui/gfx/image/image.h" |
| 50 #include "ui/gfx/image/image_family.h" | 52 #include "ui/gfx/image/image_family.h" |
| 51 #include "ui/gfx/image/image_skia.h" | 53 #include "ui/gfx/image/image_skia.h" |
| 52 #include "ui/gfx/image/image_skia_rep.h" | 54 #include "ui/gfx/image/image_skia_rep.h" |
| 53 #include "url/gurl.h" | 55 #include "url/gurl.h" |
| 54 | 56 |
| 55 using content::BrowserThread; | 57 using content::BrowserThread; |
|
grt (UTC plus 2)
2017/06/12 20:43:39
unused
chengx
2017/06/12 22:05:30
Done.
| |
| 56 using JumpListData = JumpList::JumpListData; | |
| 57 | 58 |
| 58 namespace { | 59 namespace { |
| 59 | 60 |
| 60 // The default maximum number of items to display in JumpList is 10. | 61 // The default maximum number of items to display in JumpList is 10. |
| 61 // https://msdn.microsoft.com/library/windows/desktop/dd378398.aspx | 62 // https://msdn.microsoft.com/library/windows/desktop/dd378398.aspx |
| 62 // The "Most visited" and "Recently closed" category titles always take 2 slots. | 63 // The "Most visited" and "Recently closed" category titles always take 2 slots. |
| 63 // For the remaining 8 slots, we allocate 5 slots to "most-visited" items and 3 | 64 // For the remaining 8 slots, we allocate 5 slots to "most-visited" items and 3 |
| 64 // slots to "recently-closed" items, respectively. | 65 // slots to "recently-closed" items, respectively. |
| 65 constexpr size_t kMostVisitedItems = 5; | 66 constexpr size_t kMostVisitedItems = 5; |
| 66 constexpr size_t kRecentlyClosedItems = 3; | 67 constexpr size_t kRecentlyClosedItems = 3; |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 189 base::FilePath GenerateJumplistIconDirName( | 190 base::FilePath GenerateJumplistIconDirName( |
| 190 const base::FilePath& profile_dir, | 191 const base::FilePath& profile_dir, |
| 191 const base::FilePath::StringPieceType& suffix) { | 192 const base::FilePath::StringPieceType& suffix) { |
| 192 base::FilePath::StringType dir_name(chrome::kJumpListIconDirname); | 193 base::FilePath::StringType dir_name(chrome::kJumpListIconDirname); |
| 193 suffix.AppendToString(&dir_name); | 194 suffix.AppendToString(&dir_name); |
| 194 return profile_dir.Append(dir_name); | 195 return profile_dir.Append(dir_name); |
| 195 } | 196 } |
| 196 | 197 |
| 197 } // namespace | 198 } // namespace |
| 198 | 199 |
| 199 JumpList::JumpListData::JumpListData() {} | 200 JumpList::UpdateResults::UpdateResults() {} |
| 200 | 201 |
| 201 JumpList::JumpListData::~JumpListData() {} | 202 JumpList::UpdateResults::~UpdateResults() {} |
| 202 | 203 |
| 203 JumpList::JumpList(Profile* profile) | 204 JumpList::JumpList(Profile* profile) |
| 204 : RefcountedKeyedService(content::BrowserThread::GetTaskRunnerForThread( | 205 : profile_(profile), |
| 205 content::BrowserThread::UI)), | |
| 206 profile_(profile), | |
| 207 jumplist_data_(new base::RefCountedData<JumpListData>), | |
| 208 task_id_(base::CancelableTaskTracker::kBadTaskId), | |
| 209 update_jumplist_task_runner_(base::CreateCOMSTATaskRunnerWithTraits( | 206 update_jumplist_task_runner_(base::CreateCOMSTATaskRunnerWithTraits( |
| 210 {base::MayBlock(), base::TaskPriority::USER_VISIBLE, | 207 {base::MayBlock(), base::TaskPriority::USER_VISIBLE, |
| 211 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})), | 208 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})), |
| 212 delete_jumplisticons_task_runner_( | 209 delete_jumplisticons_task_runner_( |
| 213 base::CreateSequencedTaskRunnerWithTraits( | 210 base::CreateSequencedTaskRunnerWithTraits( |
| 214 {base::MayBlock(), base::TaskPriority::BACKGROUND, | 211 {base::MayBlock(), base::TaskPriority::BACKGROUND, |
| 215 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})), | 212 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})), |
| 216 weak_ptr_factory_(this) { | 213 weak_ptr_factory_(this) { |
| 217 DCHECK(Enabled()); | 214 DCHECK(Enabled()); |
| 218 // To update JumpList when a tab is added or removed, we add this object to | 215 // To update JumpList when a tab is added or removed, we add this object to |
| 219 // the observer list of the TabRestoreService class. | 216 // the observer list of the TabRestoreService class. |
| 220 // When we add this object to the observer list, we save the pointer to this | 217 // When we add this object to the observer list, we save the pointer to this |
| 221 // TabRestoreService object. This pointer is used when we remove this object | 218 // TabRestoreService object. This pointer is used when we remove this object |
| 222 // from the observer list. | 219 // from the observer list. |
| 223 sessions::TabRestoreService* tab_restore_service = | 220 sessions::TabRestoreService* tab_restore_service = |
| 224 TabRestoreServiceFactory::GetForProfile(profile_); | 221 TabRestoreServiceFactory::GetForProfile(profile_); |
| 225 if (!tab_restore_service) | 222 if (!tab_restore_service) |
| 226 return; | 223 return; |
| 227 | 224 |
| 228 app_id_ = | 225 app_id_ = |
| 229 shell_integration::win::GetChromiumModelIdForProfile(profile_->GetPath()); | 226 shell_integration::win::GetChromiumModelIdForProfile(profile_->GetPath()); |
| 230 | 227 |
| 228 // Register as TopSitesObserver so that we can update ourselves when the | |
| 229 // TopSites changes. TopSites updates itself after a delay. This is especially | |
| 230 // noticable when your profile is empty. | |
| 231 scoped_refptr<history::TopSites> top_sites = | 231 scoped_refptr<history::TopSites> top_sites = |
| 232 TopSitesFactory::GetForProfile(profile_); | 232 TopSitesFactory::GetForProfile(profile_); |
| 233 if (top_sites) { | 233 if (top_sites) |
| 234 // Register as TopSitesObserver so that we can update ourselves when the | |
| 235 // TopSites changes. TopSites updates itself after a delay. This is | |
| 236 // especially noticable when your profile is empty. | |
| 237 top_sites->AddObserver(this); | 234 top_sites->AddObserver(this); |
| 238 } | 235 |
| 236 // Register as TabRestoreServiceObserver so that we can update ourselves when | |
| 237 // recently closed tabs have changes. | |
| 239 tab_restore_service->AddObserver(this); | 238 tab_restore_service->AddObserver(this); |
| 239 | |
| 240 // kIncognitoModeAvailability is monitored for changes on Incognito mode. | |
| 240 pref_change_registrar_.reset(new PrefChangeRegistrar); | 241 pref_change_registrar_.reset(new PrefChangeRegistrar); |
| 241 pref_change_registrar_->Init(profile_->GetPrefs()); | 242 pref_change_registrar_->Init(profile_->GetPrefs()); |
| 243 // base::Unretained is safe since |this| is guaranteed to outlive | |
| 244 // pref_change_registrar_. | |
| 242 pref_change_registrar_->Add( | 245 pref_change_registrar_->Add( |
| 243 prefs::kIncognitoModeAvailability, | 246 prefs::kIncognitoModeAvailability, |
| 244 base::Bind(&JumpList::OnIncognitoAvailabilityChanged, this)); | 247 base::Bind(&JumpList::OnIncognitoAvailabilityChanged, |
| 248 base::Unretained(this))); | |
| 245 } | 249 } |
| 246 | 250 |
| 247 JumpList::~JumpList() { | 251 JumpList::~JumpList() { |
| 248 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 252 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 249 Terminate(); | 253 Terminate(); |
| 250 } | 254 } |
| 251 | 255 |
| 252 // static | 256 // static |
| 253 bool JumpList::Enabled() { | 257 bool JumpList::Enabled() { |
| 254 return JumpListUpdater::IsEnabled(); | 258 return JumpListUpdater::IsEnabled(); |
| 255 } | 259 } |
| 256 | 260 |
| 257 void JumpList::CancelPendingUpdate() { | 261 void JumpList::CancelPendingUpdate() { |
| 258 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 262 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 263 | |
| 264 // Cancel a pending most-visited URL fetch by invalidating the weak pointer. | |
| 265 weak_ptr_factory_.InvalidateWeakPtrs(); | |
| 266 | |
| 267 // Cancel a pending favicon loading by invalidating its task id. | |
| 259 if (task_id_ != base::CancelableTaskTracker::kBadTaskId) { | 268 if (task_id_ != base::CancelableTaskTracker::kBadTaskId) { |
| 260 cancelable_task_tracker_.TryCancel(task_id_); | 269 cancelable_task_tracker_.TryCancel(task_id_); |
| 261 task_id_ = base::CancelableTaskTracker::kBadTaskId; | 270 task_id_ = base::CancelableTaskTracker::kBadTaskId; |
| 262 } | 271 } |
| 263 } | 272 } |
| 264 | 273 |
| 265 void JumpList::Terminate() { | 274 void JumpList::Terminate() { |
| 266 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 275 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 267 timer_most_visited_.Stop(); | 276 timer_.Stop(); |
| 268 timer_recently_closed_.Stop(); | |
| 269 CancelPendingUpdate(); | 277 CancelPendingUpdate(); |
| 278 update_in_progress_ = false; | |
| 270 if (profile_) { | 279 if (profile_) { |
| 271 sessions::TabRestoreService* tab_restore_service = | 280 sessions::TabRestoreService* tab_restore_service = |
| 272 TabRestoreServiceFactory::GetForProfile(profile_); | 281 TabRestoreServiceFactory::GetForProfile(profile_); |
| 273 if (tab_restore_service) | 282 if (tab_restore_service) |
| 274 tab_restore_service->RemoveObserver(this); | 283 tab_restore_service->RemoveObserver(this); |
| 275 scoped_refptr<history::TopSites> top_sites = | 284 scoped_refptr<history::TopSites> top_sites = |
| 276 TopSitesFactory::GetForProfile(profile_); | 285 TopSitesFactory::GetForProfile(profile_); |
| 277 if (top_sites) | 286 if (top_sites) |
| 278 top_sites->RemoveObserver(this); | 287 top_sites->RemoveObserver(this); |
| 279 pref_change_registrar_.reset(); | 288 pref_change_registrar_.reset(); |
| 280 } | 289 } |
| 281 profile_ = NULL; | 290 profile_ = nullptr; |
| 282 } | 291 } |
| 283 | 292 |
| 284 void JumpList::ShutdownOnUIThread() { | 293 void JumpList::Shutdown() { |
| 285 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 294 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 286 Terminate(); | 295 Terminate(); |
| 287 } | 296 } |
| 288 | 297 |
| 289 void JumpList::OnMostVisitedURLsAvailable( | 298 void JumpList::OnMostVisitedURLsAvailable( |
| 290 const history::MostVisitedURLList& urls) { | 299 const history::MostVisitedURLList& urls) { |
| 291 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 300 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 292 | 301 |
| 293 { | 302 top_sites_has_pending_notification_ = false; |
| 294 JumpListData* data = &jumplist_data_->data; | |
| 295 base::AutoLock auto_lock(data->list_lock_); | |
| 296 | 303 |
| 297 // There is no need to update the JumpList if the top most visited sites in | 304 // There is no need to update the JumpList if the top most visited sites in |
| 298 // display have not changed. | 305 // display have not changed. |
| 299 if (MostVisitedItemsUnchanged(data->most_visited_pages_, urls, | 306 if (MostVisitedItemsUnchanged(most_visited_pages_, urls, kMostVisitedItems)) |
| 300 kMostVisitedItems)) { | 307 return; |
| 301 return; | |
| 302 } | |
| 303 | 308 |
| 304 data->most_visited_pages_.clear(); | 309 most_visited_pages_.clear(); |
| 305 | 310 |
| 306 for (size_t i = 0; i < urls.size() && i < kMostVisitedItems; i++) { | 311 const size_t num_items = std::min(urls.size(), kMostVisitedItems); |
| 307 const history::MostVisitedURL& url = urls[i]; | 312 for (size_t i = 0; i < num_items; ++i) { |
| 308 scoped_refptr<ShellLinkItem> link = CreateShellLink(); | 313 const history::MostVisitedURL& url = urls[i]; |
| 309 std::string url_string = url.url.spec(); | 314 scoped_refptr<ShellLinkItem> link = CreateShellLink(); |
| 310 base::string16 url_string_wide = base::UTF8ToUTF16(url_string); | 315 std::string url_string = url.url.spec(); |
| 311 link->GetCommandLine()->AppendArgNative(url_string_wide); | 316 base::string16 url_string_wide = base::UTF8ToUTF16(url_string); |
| 312 link->GetCommandLine()->AppendSwitchASCII( | 317 link->GetCommandLine()->AppendArgNative(url_string_wide); |
| 313 switches::kWinJumplistAction, jumplist::kMostVisitedCategory); | 318 link->GetCommandLine()->AppendSwitchASCII(switches::kWinJumplistAction, |
| 314 link->set_title(!url.title.empty() ? url.title : url_string_wide); | 319 jumplist::kMostVisitedCategory); |
| 315 link->set_url(url_string); | 320 link->set_title(!url.title.empty() ? url.title : url_string_wide); |
| 316 data->most_visited_pages_.push_back(link); | 321 link->set_url(url_string); |
| 317 data->icon_urls_.push_back(std::make_pair(url_string, link)); | 322 most_visited_pages_.push_back(link); |
| 318 } | 323 icon_urls_.emplace_back(std::move(url_string), std::move(link)); |
| 319 data->most_visited_pages_have_updates_ = true; | |
| 320 } | 324 } |
| 321 | 325 |
| 326 most_visited_should_update_ = true; | |
| 327 | |
| 322 // Send a query that retrieves the first favicon. | 328 // Send a query that retrieves the first favicon. |
| 323 StartLoadingFavicon(); | 329 StartLoadingFavicon(); |
| 324 } | 330 } |
| 325 | 331 |
| 326 void JumpList::TabRestoreServiceChanged(sessions::TabRestoreService* service) { | 332 void JumpList::TabRestoreServiceChanged(sessions::TabRestoreService* service) { |
| 327 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 333 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 328 | 334 |
| 329 // if we have a pending favicon request, cancel it here (it is out of date). | 335 tab_restore_has_pending_notification_ = true; |
| 336 | |
| 337 // Postpone handling this notification until a pending update completes. | |
| 338 if (update_in_progress_) | |
| 339 return; | |
| 340 | |
| 341 // if we have a pending favicon request, cancel it here as it's out of date. | |
| 330 CancelPendingUpdate(); | 342 CancelPendingUpdate(); |
| 331 | 343 |
| 332 // Initialize the one-shot timer to update the the "Recently Closed" category | 344 // Initialize the one-shot timer to update the JumpList in a while. |
| 333 // in a while. If there is already a request queued then cancel it and post | 345 InitializeTimerForUpdate(); |
| 334 // the new request. This ensures that JumpList update of the "Recently Closed" | |
| 335 // category won't happen until there has been a brief quiet period, thus | |
| 336 // avoiding update storms. | |
| 337 if (timer_recently_closed_.IsRunning()) { | |
| 338 timer_recently_closed_.Reset(); | |
| 339 } else { | |
| 340 timer_recently_closed_.Start( | |
| 341 FROM_HERE, kDelayForJumplistUpdate, | |
| 342 base::Bind(&JumpList::DeferredTabRestoreServiceChanged, | |
| 343 base::Unretained(this))); | |
| 344 } | |
| 345 } | 346 } |
| 346 | 347 |
| 347 void JumpList::TabRestoreServiceDestroyed( | 348 void JumpList::TabRestoreServiceDestroyed( |
| 348 sessions::TabRestoreService* service) {} | 349 sessions::TabRestoreService* service) {} |
| 349 | 350 |
| 350 bool JumpList::AddTab(const sessions::TabRestoreService::Tab& tab, | 351 bool JumpList::AddTab(const sessions::TabRestoreService::Tab& tab, |
| 351 size_t max_items, | 352 size_t max_items) { |
| 352 JumpListData* data) { | |
| 353 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 353 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 354 data->list_lock_.AssertAcquired(); | |
| 355 | 354 |
| 356 // This code adds the URL and the title strings of the given tab to |data|. | 355 // This code adds the URL and the title strings of the given tab to the |
| 357 if (data->recently_closed_pages_.size() >= max_items) | 356 // JumpList variables. |
| 357 if (recently_closed_pages_.size() >= max_items) | |
| 358 return false; | 358 return false; |
| 359 | 359 |
| 360 scoped_refptr<ShellLinkItem> link = CreateShellLink(); | 360 scoped_refptr<ShellLinkItem> link = CreateShellLink(); |
| 361 const sessions::SerializedNavigationEntry& current_navigation = | 361 const sessions::SerializedNavigationEntry& current_navigation = |
| 362 tab.navigations.at(tab.current_navigation_index); | 362 tab.navigations.at(tab.current_navigation_index); |
| 363 std::string url = current_navigation.virtual_url().spec(); | 363 std::string url = current_navigation.virtual_url().spec(); |
| 364 link->GetCommandLine()->AppendArgNative(base::UTF8ToUTF16(url)); | 364 link->GetCommandLine()->AppendArgNative(base::UTF8ToUTF16(url)); |
| 365 link->GetCommandLine()->AppendSwitchASCII(switches::kWinJumplistAction, | 365 link->GetCommandLine()->AppendSwitchASCII(switches::kWinJumplistAction, |
| 366 jumplist::kRecentlyClosedCategory); | 366 jumplist::kRecentlyClosedCategory); |
| 367 link->set_title(current_navigation.title()); | 367 link->set_title(current_navigation.title()); |
| 368 link->set_url(url); | 368 link->set_url(url); |
| 369 data->recently_closed_pages_.push_back(link); | 369 recently_closed_pages_.push_back(link); |
| 370 data->icon_urls_.push_back(std::make_pair(std::move(url), std::move(link))); | 370 icon_urls_.emplace_back(std::move(url), std::move(link)); |
| 371 | 371 |
| 372 return true; | 372 return true; |
| 373 } | 373 } |
| 374 | 374 |
| 375 void JumpList::AddWindow(const sessions::TabRestoreService::Window& window, | 375 void JumpList::AddWindow(const sessions::TabRestoreService::Window& window, |
| 376 size_t max_items, | 376 size_t max_items) { |
| 377 JumpListData* data) { | |
| 378 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 377 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 379 data->list_lock_.AssertAcquired(); | |
| 380 | |
| 381 // This code enumerates all the tabs in the given window object and add their | |
| 382 // URLs and titles to |data|. | |
| 383 DCHECK(!window.tabs.empty()); | 378 DCHECK(!window.tabs.empty()); |
| 384 | 379 |
| 385 for (const auto& tab : window.tabs) { | 380 for (const auto& tab : window.tabs) { |
| 386 if (!AddTab(*tab, max_items, data)) | 381 if (!AddTab(*tab, max_items)) |
| 387 return; | 382 return; |
| 388 } | 383 } |
| 389 } | 384 } |
| 390 | 385 |
| 391 void JumpList::StartLoadingFavicon() { | 386 void JumpList::StartLoadingFavicon() { |
| 392 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 387 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 393 | 388 |
| 394 base::ElapsedTimer timer; | 389 if (icon_urls_.empty()) { |
| 395 | |
| 396 GURL url; | |
| 397 bool waiting_for_icons = true; | |
| 398 { | |
| 399 JumpListData* data = &jumplist_data_->data; | |
| 400 base::AutoLock auto_lock(data->list_lock_); | |
| 401 waiting_for_icons = !data->icon_urls_.empty(); | |
| 402 if (waiting_for_icons) { | |
| 403 // Ask FaviconService if it has a favicon of a URL. | |
| 404 // When FaviconService has one, it will call OnFaviconDataAvailable(). | |
| 405 url = GURL(data->icon_urls_.front().first); | |
| 406 } | |
| 407 } | |
| 408 | |
| 409 if (!waiting_for_icons) { | |
| 410 // No more favicons are needed by the application JumpList. Schedule a | 390 // No more favicons are needed by the application JumpList. Schedule a |
| 411 // RunUpdateJumpList call. | 391 // RunUpdateJumpList call. |
| 412 PostRunUpdate(); | 392 PostRunUpdate(); |
| 413 return; | 393 return; |
| 414 } | 394 } |
| 415 | 395 |
| 396 base::ElapsedTimer timer; | |
| 397 | |
| 398 // Ask FaviconService if it has a favicon of a URL. | |
| 399 // When FaviconService has one, it will call OnFaviconDataAvailable(). | |
| 416 favicon::FaviconService* favicon_service = | 400 favicon::FaviconService* favicon_service = |
| 417 FaviconServiceFactory::GetForProfile(profile_, | 401 FaviconServiceFactory::GetForProfile(profile_, |
| 418 ServiceAccessType::EXPLICIT_ACCESS); | 402 ServiceAccessType::EXPLICIT_ACCESS); |
| 403 // base::Unretained is safe since |this| is guaranteed to outlive | |
| 404 // cancelable_task_tracker_. | |
| 419 task_id_ = favicon_service->GetFaviconImageForPageURL( | 405 task_id_ = favicon_service->GetFaviconImageForPageURL( |
| 420 url, | 406 GURL(icon_urls_.front().first), |
| 421 base::Bind(&JumpList::OnFaviconDataAvailable, base::Unretained(this)), | 407 base::Bind(&JumpList::OnFaviconDataAvailable, base::Unretained(this)), |
| 422 &cancelable_task_tracker_); | 408 &cancelable_task_tracker_); |
| 423 | 409 |
| 424 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/717236 | 410 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/717236 |
| 425 UMA_HISTOGRAM_TIMES("WinJumplist.StartLoadingFaviconDuration", | 411 UMA_HISTOGRAM_TIMES("WinJumplist.StartLoadingFaviconDuration", |
| 426 timer.Elapsed()); | 412 timer.Elapsed()); |
| 427 } | 413 } |
| 428 | 414 |
| 429 void JumpList::OnFaviconDataAvailable( | 415 void JumpList::OnFaviconDataAvailable( |
| 430 const favicon_base::FaviconImageResult& image_result) { | 416 const favicon_base::FaviconImageResult& image_result) { |
| 431 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 417 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 432 | 418 |
| 433 base::ElapsedTimer timer; | 419 base::ElapsedTimer timer; |
| 434 | 420 |
| 435 // If there is currently a favicon request in progress, it is now outdated, | 421 // If there is currently a favicon request in progress, it is now outdated, |
| 436 // as we have received another, so nullify the handle from the old request. | 422 // as we have received another, so nullify the handle from the old request. |
| 437 task_id_ = base::CancelableTaskTracker::kBadTaskId; | 423 task_id_ = base::CancelableTaskTracker::kBadTaskId; |
| 438 // Lock the list to set icon data and pop the url. | 424 |
| 439 { | 425 // Attach the received data to the ShellLinkItem object. This data will be |
| 440 JumpListData* data = &jumplist_data_->data; | 426 // decoded by the RunUpdateJumpList method. |
| 441 base::AutoLock auto_lock(data->list_lock_); | 427 if (!icon_urls_.empty()) { |
| 442 // Attach the received data to the ShellLinkItem object. | 428 if (!image_result.image.IsEmpty() && icon_urls_.front().second.get()) { |
| 443 // This data will be decoded by the RunUpdateJumpList | |
| 444 // method. | |
| 445 if (!image_result.image.IsEmpty() && !data->icon_urls_.empty() && | |
| 446 data->icon_urls_.front().second.get()) { | |
| 447 gfx::ImageSkia image_skia = image_result.image.AsImageSkia(); | 429 gfx::ImageSkia image_skia = image_result.image.AsImageSkia(); |
| 448 image_skia.EnsureRepsForSupportedScales(); | 430 image_skia.EnsureRepsForSupportedScales(); |
| 449 std::unique_ptr<gfx::ImageSkia> deep_copy(image_skia.DeepCopy()); | 431 std::unique_ptr<gfx::ImageSkia> deep_copy(image_skia.DeepCopy()); |
| 450 data->icon_urls_.front().second->set_icon_image(*deep_copy); | 432 icon_urls_.front().second->set_icon_image(*deep_copy); |
| 451 } | 433 } |
| 452 | 434 icon_urls_.pop_front(); |
| 453 if (!data->icon_urls_.empty()) | |
| 454 data->icon_urls_.pop_front(); | |
| 455 } | 435 } |
| 456 | 436 |
| 457 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/717236 | 437 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/717236 |
| 458 UMA_HISTOGRAM_TIMES("WinJumplist.OnFaviconDataAvailableDuration", | 438 UMA_HISTOGRAM_TIMES("WinJumplist.OnFaviconDataAvailableDuration", |
| 459 timer.Elapsed()); | 439 timer.Elapsed()); |
| 460 | 440 |
| 461 // Check whether we need to load more favicons. | 441 // Check whether we need to load more favicons. |
| 462 StartLoadingFavicon(); | 442 StartLoadingFavicon(); |
| 463 } | 443 } |
| 464 | 444 |
| 465 void JumpList::OnIncognitoAvailabilityChanged() { | 445 void JumpList::OnIncognitoAvailabilityChanged() { |
| 466 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 446 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 467 | 447 |
| 468 bool waiting_for_icons = true; | 448 if (icon_urls_.empty()) |
| 469 { | |
| 470 JumpListData* data = &jumplist_data_->data; | |
| 471 base::AutoLock auto_lock(data->list_lock_); | |
| 472 waiting_for_icons = !data->icon_urls_.empty(); | |
| 473 } | |
| 474 | |
| 475 // Since neither the "Most Visited" category nor the "Recently Closed" | |
| 476 // category changes, mark the flags so that icon files for those categories | |
| 477 // won't be updated later on. | |
| 478 if (!waiting_for_icons) | |
| 479 PostRunUpdate(); | 449 PostRunUpdate(); |
| 480 } | 450 } |
| 481 | 451 |
| 482 void JumpList::PostRunUpdate() { | 452 void JumpList::PostRunUpdate() { |
| 483 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 453 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 484 | 454 |
| 485 TRACE_EVENT0("browser", "JumpList::PostRunUpdate"); | 455 TRACE_EVENT0("browser", "JumpList::PostRunUpdate"); |
| 486 if (!profile_) | 456 |
| 487 return; | 457 update_in_progress_ = true; |
| 488 | 458 |
| 489 base::FilePath profile_dir = profile_->GetPath(); | 459 base::FilePath profile_dir = profile_->GetPath(); |
| 490 | 460 |
| 491 // Check if incognito windows (or normal windows) are disabled by policy. | 461 // Check if incognito windows (or normal windows) are disabled by policy. |
| 492 IncognitoModePrefs::Availability incognito_availability = | 462 IncognitoModePrefs::Availability incognito_availability = |
| 493 IncognitoModePrefs::GetAvailability(profile_->GetPrefs()); | 463 IncognitoModePrefs::GetAvailability(profile_->GetPrefs()); |
| 494 | 464 |
| 495 // Post a task to update the JumpList, which consists of 1) delete old icons, | 465 // Make local copies of JumpList member variables and use them for an update. |
| 496 // 2) create new icons, 3) notify the OS. | 466 ShellLinkItemList local_most_visited_pages = most_visited_pages_; |
| 497 update_jumplist_task_runner_->PostTask( | 467 ShellLinkItemList local_recently_closed_pages = recently_closed_pages_; |
| 498 FROM_HERE, | |
| 499 base::Bind(&JumpList::RunUpdateJumpList, this, incognito_availability, | |
| 500 app_id_, profile_dir, base::RetainedRef(jumplist_data_))); | |
| 501 | 468 |
| 502 // Post a task to delete JumpListIcons folder as it's no longer needed. | 469 bool most_visited_should_update = most_visited_should_update_; |
| 503 // Now we have JumpListIconsMostVisited folder and JumpListIconsRecentClosed | 470 bool recently_closed_should_update = recently_closed_should_update_; |
| 504 // folder instead. | |
| 505 base::FilePath icon_dir = | |
| 506 GenerateJumplistIconDirName(profile_dir, FILE_PATH_LITERAL("")); | |
| 507 | 471 |
| 508 delete_jumplisticons_task_runner_->PostTask( | 472 auto update_results = base::MakeUnique<UpdateResults>(); |
| 509 FROM_HERE, | 473 update_results->most_visited_icons_in_update_ = most_visited_icons_; |
| 510 base::Bind(&DeleteDirectory, std::move(icon_dir), kFileDeleteLimit)); | 474 update_results->recently_closed_icons_in_update = recently_closed_icons_; |
| 511 | 475 |
| 512 // Post a task to delete JumpListIconsOld folder as it's no longer needed. | 476 // Post a task to update the JumpList, which consists of 1) create new icons, |
| 513 base::FilePath icon_dir_old = | 477 // 2) delete old icons, 3) notify the OS. |
| 514 GenerateJumplistIconDirName(profile_dir, FILE_PATH_LITERAL("Old")); | 478 if (!update_jumplist_task_runner_->PostTaskAndReply( |
| 515 | 479 FROM_HERE, |
| 516 delete_jumplisticons_task_runner_->PostTask( | 480 base::Bind(&JumpList::RunUpdateJumpList, app_id_, profile_dir, |
| 517 FROM_HERE, | 481 local_most_visited_pages, local_recently_closed_pages, |
| 518 base::Bind(&DeleteDirectory, std::move(icon_dir_old), kFileDeleteLimit)); | 482 most_visited_should_update, recently_closed_should_update, |
| 483 incognito_availability, update_results.get()), | |
| 484 base::Bind(&JumpList::OnRunUpdateCompletion, | |
| 485 weak_ptr_factory_.GetWeakPtr(), | |
| 486 base::Passed(std::move(update_results))))) { | |
| 487 OnRunUpdateCompletion(base::MakeUnique<UpdateResults>()); | |
| 488 } | |
| 519 } | 489 } |
| 520 | 490 |
| 521 void JumpList::TopSitesLoaded(history::TopSites* top_sites) { | 491 void JumpList::TopSitesLoaded(history::TopSites* top_sites) { |
| 522 } | 492 } |
| 523 | 493 |
| 524 void JumpList::TopSitesChanged(history::TopSites* top_sites, | 494 void JumpList::TopSitesChanged(history::TopSites* top_sites, |
| 525 ChangeReason change_reason) { | 495 ChangeReason change_reason) { |
| 526 // If we have a pending favicon request, cancel it here (it is out of date). | 496 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 497 | |
| 498 top_sites_has_pending_notification_ = true; | |
| 499 | |
| 500 // Postpone handling this notification until a pending update completes. | |
| 501 if (update_in_progress_) | |
| 502 return; | |
| 503 | |
| 504 // If we have a pending favicon request, cancel it here as it's out of date. | |
| 527 CancelPendingUpdate(); | 505 CancelPendingUpdate(); |
| 528 | 506 |
| 529 // Initialize the one-shot timer to update the the "Most visited" category in | 507 // Initialize the one-shot timer to update the JumpList in a while. |
| 530 // a while. If there is already a request queued then cancel it and post the | 508 InitializeTimerForUpdate(); |
| 531 // new request. This ensures that JumpList update of the "Most visited" | 509 } |
| 532 // category won't happen until there has been a brief quiet period, thus | 510 |
| 533 // avoiding update storms. | 511 void JumpList::InitializeTimerForUpdate() { |
| 534 if (timer_most_visited_.IsRunning()) { | 512 if (timer_.IsRunning()) { |
| 535 timer_most_visited_.Reset(); | 513 timer_.Reset(); |
| 536 } else { | 514 } else { |
| 537 timer_most_visited_.Start( | 515 // base::Unretained is safe since |this| is guaranteed to outlive timer_. |
| 538 FROM_HERE, kDelayForJumplistUpdate, | 516 timer_.Start(FROM_HERE, kDelayForJumplistUpdate, |
| 539 base::Bind(&JumpList::DeferredTopSitesChanged, base::Unretained(this))); | 517 base::Bind(&JumpList::OnDelayTimer, base::Unretained(this))); |
| 540 } | 518 } |
| 541 } | 519 } |
| 542 | 520 |
| 543 void JumpList::DeferredTopSitesChanged() { | 521 void JumpList::OnDelayTimer() { |
| 544 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 522 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 545 | 523 |
| 546 if (updates_to_skip_ > 0) { | 524 if (updates_to_skip_ > 0) { |
| 547 --updates_to_skip_; | 525 --updates_to_skip_; |
| 548 return; | 526 return; |
| 549 } | 527 } |
| 550 | 528 |
| 529 // Retrieve the recently closed URLs synchronously. | |
| 530 if (tab_restore_has_pending_notification_) { | |
| 531 tab_restore_has_pending_notification_ = false; | |
| 532 ProcessTabRestoreServiceNotification(); | |
| 533 } | |
| 534 | |
| 535 // If TopSites has updates, retrieve the URLs asynchronously, and on its | |
| 536 // completion, trigger favicon loading. | |
| 537 // Otherwise, call StartLoadingFavicon directly to start favicon loading. | |
| 538 if (top_sites_has_pending_notification_) | |
| 539 ProcessTopSitesNotification(); | |
| 540 else | |
| 541 StartLoadingFavicon(); | |
| 542 } | |
| 543 | |
| 544 void JumpList::ProcessTopSitesNotification() { | |
| 545 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | |
| 546 | |
| 551 // Opening the first tab in one session triggers a TopSite history sync. | 547 // Opening the first tab in one session triggers a TopSite history sync. |
| 552 // Delay this sync till the first tab is closed to allow the "recently closed" | 548 // Delay this sync till the first tab is closed to allow the "recently closed" |
| 553 // category from last session to stay longer. | 549 // category from last session to stay longer. |
| 554 if (!has_tab_closed_) | 550 if (!has_tab_closed_) |
| 555 return; | 551 return; |
| 556 | 552 |
| 557 scoped_refptr<history::TopSites> top_sites = | 553 scoped_refptr<history::TopSites> top_sites = |
| 558 TopSitesFactory::GetForProfile(profile_); | 554 TopSitesFactory::GetForProfile(profile_); |
| 559 if (top_sites) { | 555 if (top_sites) { |
| 560 top_sites->GetMostVisitedURLs( | 556 top_sites->GetMostVisitedURLs( |
| 561 base::Bind(&JumpList::OnMostVisitedURLsAvailable, | 557 base::Bind(&JumpList::OnMostVisitedURLsAvailable, |
| 562 weak_ptr_factory_.GetWeakPtr()), | 558 weak_ptr_factory_.GetWeakPtr()), |
| 563 false); | 559 false); |
| 564 } | 560 } |
| 565 } | 561 } |
| 566 | 562 |
| 567 void JumpList::DeferredTabRestoreServiceChanged() { | 563 void JumpList::ProcessTabRestoreServiceNotification() { |
| 568 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 564 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 569 | 565 |
| 570 if (updates_to_skip_ > 0) { | |
| 571 --updates_to_skip_; | |
| 572 return; | |
| 573 } | |
| 574 | |
| 575 // Force a TopSite history sync when closing a first tab in one session. | |
| 576 if (!has_tab_closed_) { | |
| 577 has_tab_closed_ = true; | |
| 578 scoped_refptr<history::TopSites> top_sites = | |
| 579 TopSitesFactory::GetForProfile(profile_); | |
| 580 if (top_sites) | |
| 581 top_sites->SyncWithHistory(); | |
| 582 } | |
| 583 | |
| 584 // Create a list of ShellLinkItems from the "Recently Closed" pages. | 566 // Create a list of ShellLinkItems from the "Recently Closed" pages. |
| 585 // As noted above, we create a ShellLinkItem objects with the following | 567 // As noted above, we create a ShellLinkItem objects with the following |
| 586 // parameters. | 568 // parameters. |
| 587 // * arguments | 569 // * arguments |
| 588 // The last URL of the tab object. | 570 // The last URL of the tab object. |
| 589 // * title | 571 // * title |
| 590 // The title of the last URL. | 572 // The title of the last URL. |
| 591 // * icon | 573 // * icon |
| 592 // An empty string. This value is to be updated in OnFaviconDataAvailable(). | 574 // An empty string. This value is to be updated in OnFaviconDataAvailable(). |
| 593 | 575 |
| 594 sessions::TabRestoreService* tab_restore_service = | 576 sessions::TabRestoreService* tab_restore_service = |
| 595 TabRestoreServiceFactory::GetForProfile(profile_); | 577 TabRestoreServiceFactory::GetForProfile(profile_); |
| 596 | 578 |
| 597 { | 579 recently_closed_pages_.clear(); |
| 598 JumpListData* data = &jumplist_data_->data; | |
| 599 base::AutoLock auto_lock(data->list_lock_); | |
| 600 data->recently_closed_pages_.clear(); | |
| 601 | 580 |
| 602 for (const auto& entry : tab_restore_service->entries()) { | 581 for (const auto& entry : tab_restore_service->entries()) { |
| 603 if (data->recently_closed_pages_.size() >= kRecentlyClosedItems) | 582 if (recently_closed_pages_.size() >= kRecentlyClosedItems) |
| 583 break; | |
| 584 switch (entry->type) { | |
| 585 case sessions::TabRestoreService::TAB: | |
| 586 AddTab(static_cast<const sessions::TabRestoreService::Tab&>(*entry), | |
| 587 kRecentlyClosedItems); | |
| 604 break; | 588 break; |
| 605 switch (entry->type) { | 589 case sessions::TabRestoreService::WINDOW: |
| 606 case sessions::TabRestoreService::TAB: | 590 AddWindow( |
| 607 AddTab(static_cast<const sessions::TabRestoreService::Tab&>(*entry), | 591 static_cast<const sessions::TabRestoreService::Window&>(*entry), |
| 608 kRecentlyClosedItems, data); | 592 kRecentlyClosedItems); |
| 609 break; | 593 break; |
| 610 case sessions::TabRestoreService::WINDOW: | |
| 611 AddWindow( | |
| 612 static_cast<const sessions::TabRestoreService::Window&>(*entry), | |
| 613 kRecentlyClosedItems, data); | |
| 614 break; | |
| 615 } | |
| 616 } | 594 } |
| 617 | |
| 618 data->recently_closed_pages_have_updates_ = true; | |
| 619 } | 595 } |
| 620 | 596 |
| 621 // Send a query that retrieves the first favicon. | 597 recently_closed_should_update_ = true; |
| 622 StartLoadingFavicon(); | 598 |
| 599 // Force a TopSite history sync when closing a first tab in one session. | |
| 600 if (!has_tab_closed_) { | |
| 601 has_tab_closed_ = true; | |
| 602 scoped_refptr<history::TopSites> top_sites = | |
| 603 TopSitesFactory::GetForProfile(profile_); | |
| 604 if (top_sites) | |
| 605 top_sites->SyncWithHistory(); | |
| 606 } | |
| 623 } | 607 } |
| 624 | 608 |
| 609 // static | |
| 625 void JumpList::DeleteIconFiles(const base::FilePath& icon_dir, | 610 void JumpList::DeleteIconFiles(const base::FilePath& icon_dir, |
| 626 JumpListCategory category) { | 611 URLIconCache* icon_cache) { |
| 627 base::flat_map<std::string, base::FilePath>* source_map = nullptr; | |
| 628 switch (category) { | |
| 629 case JumpListCategory::kMostVisited: | |
| 630 source_map = &most_visited_icons_; | |
| 631 break; | |
| 632 case JumpListCategory::kRecentlyClosed: | |
| 633 source_map = &recently_closed_icons_; | |
| 634 break; | |
| 635 } | |
| 636 | |
| 637 // Put all cached icon file paths into a set. | 612 // Put all cached icon file paths into a set. |
| 638 base::flat_set<base::FilePath> cached_files; | 613 base::flat_set<base::FilePath> cached_files; |
| 639 cached_files.reserve(source_map->size()); | 614 cached_files.reserve(icon_cache->size()); |
| 640 | 615 |
| 641 for (const auto& url_path_pair : *source_map) | 616 for (const auto& url_path_pair : *icon_cache) |
| 642 cached_files.insert(url_path_pair.second); | 617 cached_files.insert(url_path_pair.second); |
| 643 | 618 |
| 644 DeleteNonCachedFiles(icon_dir, cached_files); | 619 DeleteNonCachedFiles(icon_dir, cached_files); |
| 645 } | 620 } |
| 646 | 621 |
| 622 // static | |
| 647 int JumpList::CreateIconFiles(const base::FilePath& icon_dir, | 623 int JumpList::CreateIconFiles(const base::FilePath& icon_dir, |
| 648 const ShellLinkItemList& item_list, | 624 const ShellLinkItemList& item_list, |
| 649 size_t max_items, | 625 size_t max_items, |
| 650 JumpListCategory category) { | 626 URLIconCache* icon_cache) { |
| 651 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/40407. | 627 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/40407. |
| 652 SCOPED_UMA_HISTOGRAM_TIMER("WinJumplist.CreateIconFilesDuration"); | 628 SCOPED_UMA_HISTOGRAM_TIMER("WinJumplist.CreateIconFilesDuration"); |
| 653 | 629 |
| 654 int icons_created = 0; | 630 int icons_created = 0; |
| 655 | 631 |
| 656 // Reuse icons for urls that were already present in the jumplist for this | 632 // Reuse icons for urls that already present in the current JumpList. |
| 657 // category. | 633 URLIconCache updated_map; |
| 658 | |
| 659 base::flat_map<std::string, base::FilePath>* source_map = nullptr; | |
| 660 switch (category) { | |
| 661 case JumpListCategory::kMostVisited: | |
| 662 source_map = &most_visited_icons_; | |
| 663 break; | |
| 664 case JumpListCategory::kRecentlyClosed: | |
| 665 source_map = &recently_closed_icons_; | |
| 666 break; | |
| 667 } | |
| 668 | |
| 669 base::flat_map<std::string, base::FilePath> updated_map; | |
| 670 | |
| 671 for (ShellLinkItemList::const_iterator iter = item_list.begin(); | 634 for (ShellLinkItemList::const_iterator iter = item_list.begin(); |
| 672 iter != item_list.end() && max_items > 0; ++iter, --max_items) { | 635 iter != item_list.end() && max_items > 0; ++iter, --max_items) { |
| 673 ShellLinkItem* item = iter->get(); | 636 ShellLinkItem* item = iter->get(); |
| 674 auto cache_iter = source_map->find(item->url()); | 637 auto cache_iter = icon_cache->find(item->url()); |
| 675 if (cache_iter != source_map->end()) { | 638 if (cache_iter != icon_cache->end()) { |
| 676 item->set_icon(cache_iter->second.value(), 0); | 639 item->set_icon(cache_iter->second.value(), 0); |
| 677 updated_map[item->url()] = cache_iter->second; | 640 updated_map[item->url()] = cache_iter->second; |
| 678 } else { | 641 } else { |
| 679 base::FilePath icon_path; | 642 base::FilePath icon_path; |
| 680 if (CreateIconFile(item->icon_image(), icon_dir, &icon_path)) { | 643 if (CreateIconFile(item->icon_image(), icon_dir, &icon_path)) { |
| 681 ++icons_created; | 644 ++icons_created; |
| 682 item->set_icon(icon_path.value(), 0); | 645 item->set_icon(icon_path.value(), 0); |
| 683 updated_map[item->url()] = icon_path; | 646 updated_map[item->url()] = icon_path; |
| 684 } | 647 } |
| 685 } | 648 } |
| 686 } | 649 } |
| 687 source_map->swap(updated_map); | 650 icon_cache->swap(updated_map); |
| 688 | 651 |
| 689 return icons_created; | 652 return icons_created; |
| 690 } | 653 } |
| 691 | 654 |
| 655 // static | |
| 692 int JumpList::UpdateIconFiles(const base::FilePath& icon_dir, | 656 int JumpList::UpdateIconFiles(const base::FilePath& icon_dir, |
| 693 const ShellLinkItemList& page_list, | 657 const ShellLinkItemList& page_list, |
| 694 size_t slot_limit, | 658 size_t slot_limit, |
| 695 JumpListCategory category) { | 659 URLIconCache* icon_cache) { |
| 696 int icons_created = 0; | 660 int icons_created = 0; |
| 697 | 661 |
| 698 // Maximum number of icon files that each JumpList icon folder may hold, which | |
| 699 // is set to 2 times the normal amount. | |
| 700 size_t icon_limit = | |
| 701 2 * ((category == JumpListCategory::kMostVisited) ? kMostVisitedItems | |
| 702 : kRecentlyClosedItems); | |
| 703 | |
| 704 // Clear the JumpList icon folder at |icon_dir| and the cache when | 662 // Clear the JumpList icon folder at |icon_dir| and the cache when |
| 705 // 1) "Most visited" category updates for the 1st time after Chrome is | 663 // 1) |icon_cache| is empty. This happens when "Most visited" or "Recently |
| 706 // launched. This actually happens right after Chrome is launched. | 664 // closed" category updates for the 1st time after Chrome is launched. |
| 707 // 2) "Recently closed" category updates for the 1st time after Chrome is | 665 // 2) The number of icons in |icon_dir| has exceeded the limit. |
| 708 // launched. | 666 if (icon_cache->empty() || FilesExceedLimitInDir(icon_dir, slot_limit * 2)) { |
| 709 // 3) The number of icons in |icon_dir| has exceeded the limit. | |
| 710 if ((category == JumpListCategory::kMostVisited && | |
| 711 most_visited_icons_.empty()) || | |
| 712 (category == JumpListCategory::kRecentlyClosed && | |
| 713 recently_closed_icons_.empty()) || | |
| 714 FilesExceedLimitInDir(icon_dir, icon_limit)) { | |
| 715 DeleteDirectoryContentAndLogRuntime(icon_dir, kFileDeleteLimit); | 667 DeleteDirectoryContentAndLogRuntime(icon_dir, kFileDeleteLimit); |
| 716 most_visited_icons_.clear(); | 668 icon_cache->clear(); |
| 717 recently_closed_icons_.clear(); | |
| 718 // Create new icons only when the directory exists and is empty. | 669 // Create new icons only when the directory exists and is empty. |
| 719 if (base::CreateDirectory(icon_dir) && base::IsDirectoryEmpty(icon_dir)) | 670 if (base::CreateDirectory(icon_dir) && base::IsDirectoryEmpty(icon_dir)) |
| 720 icons_created += | 671 icons_created += |
| 721 CreateIconFiles(icon_dir, page_list, slot_limit, category); | 672 CreateIconFiles(icon_dir, page_list, slot_limit, icon_cache); |
| 722 } else if (base::CreateDirectory(icon_dir)) { | 673 } else if (base::CreateDirectory(icon_dir)) { |
| 723 icons_created += CreateIconFiles(icon_dir, page_list, slot_limit, category); | 674 icons_created += |
| 724 DeleteIconFiles(icon_dir, category); | 675 CreateIconFiles(icon_dir, page_list, slot_limit, icon_cache); |
| 676 DeleteIconFiles(icon_dir, icon_cache); | |
| 725 } | 677 } |
| 726 | 678 |
| 727 return icons_created; | 679 return icons_created; |
| 728 } | 680 } |
| 729 | 681 |
| 730 bool JumpList::UpdateJumpList( | 682 // static |
| 683 void JumpList::RunUpdateJumpList( | |
| 731 const base::string16& app_id, | 684 const base::string16& app_id, |
| 732 const base::FilePath& profile_dir, | 685 const base::FilePath& profile_dir, |
| 733 const ShellLinkItemList& most_visited_pages, | 686 const ShellLinkItemList& most_visited_pages, |
| 734 const ShellLinkItemList& recently_closed_pages, | 687 const ShellLinkItemList& recently_closed_pages, |
| 735 bool most_visited_pages_have_updates, | 688 bool most_visited_should_update, |
| 736 bool recently_closed_pages_have_updates, | 689 bool recently_closed_should_update, |
| 737 IncognitoModePrefs::Availability incognito_availability) { | 690 IncognitoModePrefs::Availability incognito_availability, |
| 691 UpdateResults* update_results) { | |
| 738 if (!JumpListUpdater::IsEnabled()) | 692 if (!JumpListUpdater::IsEnabled()) |
| 739 return true; | 693 return; |
| 694 | |
| 695 DCHECK(update_results); | |
| 740 | 696 |
| 741 JumpListUpdater jumplist_updater(app_id); | 697 JumpListUpdater jumplist_updater(app_id); |
| 742 | 698 |
| 743 base::ElapsedTimer begin_update_timer; | 699 base::ElapsedTimer begin_update_timer; |
| 744 | 700 |
| 745 if (!jumplist_updater.BeginUpdate()) | 701 if (!jumplist_updater.BeginUpdate()) |
| 746 return false; | 702 return; |
| 747 | 703 |
| 748 // Discard this JumpList update if JumpListUpdater::BeginUpdate takes longer | 704 // Discard this JumpList update if JumpListUpdater::BeginUpdate takes longer |
| 749 // than the maximum allowed time, as it's very likely the following update | 705 // than the maximum allowed time, as it's very likely the following update |
| 750 // steps will also take a long time. As we've not updated the icons on the | 706 // steps will also take a long time. As we've not updated the icons on the |
| 751 // disk, discarding this update wont't affect the current JumpList used by OS. | 707 // disk, discarding this update wont't affect the current JumpList used by OS. |
| 752 if (begin_update_timer.Elapsed() >= kTimeOutForJumplistBeginUpdate) { | 708 if (begin_update_timer.Elapsed() >= kTimeOutForJumplistBeginUpdate) { |
| 753 updates_to_skip_ = kUpdatesToSkipUnderHeavyLoad; | 709 update_results->update_timeout_ = true; |
| 754 return false; | 710 return; |
| 755 } | 711 } |
| 756 | 712 |
| 757 // Record the desired number of icons created in this JumpList update. | 713 // Record the desired number of icons created in this JumpList update. |
| 758 int icons_created = 0; | 714 int icons_created = 0; |
| 759 | 715 |
| 760 // Update the icons for "Most Visisted" category of the JumpList if needed. | 716 // Update the icons for "Most Visisted" category of the JumpList if needed. |
| 761 if (most_visited_pages_have_updates) { | 717 if (most_visited_should_update) { |
| 762 base::FilePath icon_dir_most_visited = GenerateJumplistIconDirName( | 718 base::FilePath icon_dir_most_visited = GenerateJumplistIconDirName( |
| 763 profile_dir, FILE_PATH_LITERAL("MostVisited")); | 719 profile_dir, FILE_PATH_LITERAL("MostVisited")); |
| 764 | 720 |
| 765 icons_created += | 721 icons_created += UpdateIconFiles( |
| 766 UpdateIconFiles(icon_dir_most_visited, most_visited_pages, | 722 icon_dir_most_visited, most_visited_pages, kMostVisitedItems, |
| 767 kMostVisitedItems, JumpListCategory::kMostVisited); | 723 &update_results->most_visited_icons_in_update_); |
| 768 } | 724 } |
| 769 | 725 |
| 770 // Update the icons for "Recently Closed" category of the JumpList if needed. | 726 // Update the icons for "Recently Closed" category of the JumpList if needed. |
| 771 if (recently_closed_pages_have_updates) { | 727 if (recently_closed_should_update) { |
| 772 base::FilePath icon_dir_recent_closed = GenerateJumplistIconDirName( | 728 base::FilePath icon_dir_recent_closed = GenerateJumplistIconDirName( |
| 773 profile_dir, FILE_PATH_LITERAL("RecentClosed")); | 729 profile_dir, FILE_PATH_LITERAL("RecentClosed")); |
| 774 | 730 |
| 775 icons_created += UpdateIconFiles( | 731 icons_created += UpdateIconFiles( |
| 776 icon_dir_recent_closed, recently_closed_pages, kRecentlyClosedItems, | 732 icon_dir_recent_closed, recently_closed_pages, kRecentlyClosedItems, |
| 777 JumpListCategory::kRecentlyClosed); | 733 &update_results->recently_closed_icons_in_update); |
| 778 } | 734 } |
| 779 | 735 |
| 780 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/40407. | 736 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/40407. |
| 781 UMA_HISTOGRAM_COUNTS_100("WinJumplist.CreateIconFilesCount", icons_created); | 737 UMA_HISTOGRAM_COUNTS_100("WinJumplist.CreateIconFilesCount", icons_created); |
| 782 | 738 |
| 783 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/40407. | 739 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/40407. |
| 784 SCOPED_UMA_HISTOGRAM_TIMER("WinJumplist.UpdateJumpListDuration"); | 740 SCOPED_UMA_HISTOGRAM_TIMER("WinJumplist.UpdateJumpListDuration"); |
| 785 | 741 |
| 786 base::ElapsedTimer add_custom_category_timer; | 742 base::ElapsedTimer add_custom_category_timer; |
| 787 | 743 |
| 788 // Update the "Most Visited" category of the JumpList if it exists. | 744 // Update the "Most Visited" category of the JumpList if it exists. |
| 789 // This update request is applied into the JumpList when we commit this | 745 // This update request is applied into the JumpList when we commit this |
| 790 // transaction. | 746 // transaction. |
| 791 if (!jumplist_updater.AddCustomCategory( | 747 if (!jumplist_updater.AddCustomCategory( |
| 792 l10n_util::GetStringUTF16(IDS_NEW_TAB_MOST_VISITED), | 748 l10n_util::GetStringUTF16(IDS_NEW_TAB_MOST_VISITED), |
| 793 most_visited_pages, kMostVisitedItems)) { | 749 most_visited_pages, kMostVisitedItems)) { |
| 794 return false; | 750 return; |
| 795 } | 751 } |
| 796 | 752 |
| 797 // Update the "Recently Closed" category of the JumpList. | 753 // Update the "Recently Closed" category of the JumpList. |
| 798 if (!jumplist_updater.AddCustomCategory( | 754 if (!jumplist_updater.AddCustomCategory( |
| 799 l10n_util::GetStringUTF16(IDS_RECENTLY_CLOSED), recently_closed_pages, | 755 l10n_util::GetStringUTF16(IDS_RECENTLY_CLOSED), recently_closed_pages, |
| 800 kRecentlyClosedItems)) { | 756 kRecentlyClosedItems)) { |
| 801 return false; | 757 return; |
| 802 } | 758 } |
| 803 | 759 |
| 804 // If JumpListUpdater::AddCustomCategory or JumpListUpdater::CommitUpdate | 760 // If JumpListUpdater::AddCustomCategory or JumpListUpdater::CommitUpdate |
| 805 // takes longer than the maximum allowed time, skip the next | 761 // takes longer than the maximum allowed time, skip the next |
| 806 // |kUpdatesToSkipUnderHeavyLoad| updates. This update should be finished | 762 // |kUpdatesToSkipUnderHeavyLoad| updates. This update should be finished |
| 807 // because we've already updated the icons on the disk. If discarding this | 763 // because we've already updated the icons on the disk. If discarding this |
| 808 // update from here, some items in the current JumpList may not have icons | 764 // update from here, some items in the current JumpList may not have icons |
| 809 // as they've been delete from the disk. In this case, the background color of | 765 // as they've been delete from the disk. In this case, the background color of |
| 810 // the JumpList panel is used instead, which doesn't look nice. | 766 // the JumpList panel is used instead, which doesn't look nice. |
| 811 | 767 |
| 812 if (add_custom_category_timer.Elapsed() >= kTimeOutForAddCustomCategory) | 768 if (add_custom_category_timer.Elapsed() >= kTimeOutForAddCustomCategory) |
| 813 updates_to_skip_ = kUpdatesToSkipUnderHeavyLoad; | 769 update_results->update_timeout_ = true; |
| 814 | 770 |
| 815 // Update the "Tasks" category of the JumpList. | 771 // Update the "Tasks" category of the JumpList. |
| 816 if (!UpdateTaskCategory(&jumplist_updater, incognito_availability)) | 772 if (!UpdateTaskCategory(&jumplist_updater, incognito_availability)) |
| 817 return false; | 773 return; |
| 818 | 774 |
| 819 base::ElapsedTimer commit_update_timer; | 775 base::ElapsedTimer commit_update_timer; |
| 820 | 776 |
| 821 // Commit this transaction and send the updated JumpList to Windows. | 777 // Commit this transaction and send the updated JumpList to Windows. |
| 822 bool commit_result = jumplist_updater.CommitUpdate(); | 778 if (jumplist_updater.CommitUpdate()) |
| 779 update_results->update_success_ = true; | |
| 823 | 780 |
| 824 if (commit_update_timer.Elapsed() >= kTimeOutForJumplistCommitUpdate) | 781 if (commit_update_timer.Elapsed() >= kTimeOutForJumplistCommitUpdate) |
| 782 update_results->update_timeout_ = true; | |
| 783 } | |
| 784 | |
| 785 void JumpList::OnRunUpdateCompletion( | |
| 786 std::unique_ptr<UpdateResults> update_results) { | |
| 787 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | |
| 788 | |
| 789 // Update JumpList member variables based on the results from the update run | |
| 790 // just finished. | |
| 791 if (update_results->update_timeout_) | |
| 825 updates_to_skip_ = kUpdatesToSkipUnderHeavyLoad; | 792 updates_to_skip_ = kUpdatesToSkipUnderHeavyLoad; |
| 826 | 793 |
| 827 return commit_result; | 794 if (update_results->update_success_) { |
| 828 } | 795 most_visited_icons_.swap(update_results->most_visited_icons_in_update_); |
| 829 | 796 recently_closed_icons_.swap( |
| 830 void JumpList::RunUpdateJumpList( | 797 update_results->recently_closed_icons_in_update); |
| 831 IncognitoModePrefs::Availability incognito_availability, | 798 most_visited_should_update_ = false; |
| 832 const base::string16& app_id, | 799 recently_closed_should_update_ = false; |
| 833 const base::FilePath& profile_dir, | |
| 834 base::RefCountedData<JumpListData>* ref_counted_data) { | |
| 835 JumpListData* data = &ref_counted_data->data; | |
| 836 ShellLinkItemList local_most_visited_pages; | |
| 837 ShellLinkItemList local_recently_closed_pages; | |
| 838 bool most_visited_pages_have_updates; | |
| 839 bool recently_closed_pages_have_updates; | |
| 840 | |
| 841 { | |
| 842 base::AutoLock auto_lock(data->list_lock_); | |
| 843 // Make sure we are not out of date: if icon_urls_ is not empty, then | |
| 844 // another notification has been received since we processed this one | |
| 845 if (!data->icon_urls_.empty()) | |
| 846 return; | |
| 847 | |
| 848 // Make local copies of lists and flags so we can release the lock. | |
| 849 local_most_visited_pages = data->most_visited_pages_; | |
| 850 local_recently_closed_pages = data->recently_closed_pages_; | |
| 851 | |
| 852 most_visited_pages_have_updates = data->most_visited_pages_have_updates_; | |
| 853 recently_closed_pages_have_updates = | |
| 854 data->recently_closed_pages_have_updates_; | |
| 855 | |
| 856 // Clear the flags to reflect that we'll take actions on these updates. | |
| 857 data->most_visited_pages_have_updates_ = false; | |
| 858 data->recently_closed_pages_have_updates_ = false; | |
| 859 } | 800 } |
| 860 | 801 |
| 861 if (!most_visited_pages_have_updates && !recently_closed_pages_have_updates) | 802 update_in_progress_ = false; |
| 862 return; | |
| 863 | 803 |
| 864 // Update the application JumpList. If it fails, reset the flags to true if | 804 // If there is any new notification during the update run just finished, start |
| 865 // they were so that the corresponding JumpList categories will be tried to | 805 // another JumpList update. |
| 866 // update again in the next run. | 806 // Otherwise, post a task to delete the JumpListIcons and JumpListIconsOld |
| 867 if (!UpdateJumpList( | 807 // folders as they are no longer needed. Now we have the |
| 868 app_id, profile_dir, local_most_visited_pages, | 808 // JumpListIcons{MostVisited, RecentClosed} folders instead. |
| 869 local_recently_closed_pages, most_visited_pages_have_updates, | 809 if (top_sites_has_pending_notification_ || |
| 870 recently_closed_pages_have_updates, incognito_availability)) { | 810 tab_restore_has_pending_notification_) { |
| 871 base::AutoLock auto_lock(data->list_lock_); | 811 InitializeTimerForUpdate(); |
| 872 if (most_visited_pages_have_updates) | 812 } else { |
| 873 data->most_visited_pages_have_updates_ = true; | 813 base::FilePath profile_dir = profile_->GetPath(); |
| 874 if (recently_closed_pages_have_updates) | 814 base::FilePath icon_dir = |
| 875 data->recently_closed_pages_have_updates_ = true; | 815 GenerateJumplistIconDirName(profile_dir, FILE_PATH_LITERAL("")); |
| 816 delete_jumplisticons_task_runner_->PostTask( | |
| 817 FROM_HERE, | |
| 818 base::Bind(&DeleteDirectory, std::move(icon_dir), kFileDeleteLimit)); | |
| 819 | |
| 820 base::FilePath icon_dir_old = | |
| 821 GenerateJumplistIconDirName(profile_dir, FILE_PATH_LITERAL("Old")); | |
| 822 delete_jumplisticons_task_runner_->PostTask( | |
| 823 FROM_HERE, base::Bind(&DeleteDirectory, std::move(icon_dir_old), | |
| 824 kFileDeleteLimit)); | |
| 876 } | 825 } |
| 877 } | 826 } |
| OLD | NEW |