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