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" |
45 #include "ui/base/l10n/l10n_util.h" | 46 #include "ui/base/l10n/l10n_util.h" |
46 #include "ui/gfx/codec/png_codec.h" | 47 #include "ui/gfx/codec/png_codec.h" |
47 #include "ui/gfx/favicon_size.h" | 48 #include "ui/gfx/favicon_size.h" |
48 #include "ui/gfx/icon_util.h" | 49 #include "ui/gfx/icon_util.h" |
49 #include "ui/gfx/image/image.h" | 50 #include "ui/gfx/image/image.h" |
50 #include "ui/gfx/image/image_family.h" | 51 #include "ui/gfx/image/image_family.h" |
51 #include "ui/gfx/image/image_skia.h" | 52 #include "ui/gfx/image/image_skia.h" |
52 #include "ui/gfx/image/image_skia_rep.h" | 53 #include "ui/gfx/image/image_skia_rep.h" |
53 #include "url/gurl.h" | 54 #include "url/gurl.h" |
54 | 55 |
55 using content::BrowserThread; | |
56 using JumpListData = JumpList::JumpListData; | |
57 | |
58 namespace { | 56 namespace { |
59 | 57 |
60 // The default maximum number of items to display in JumpList is 10. | 58 // The default maximum number of items to display in JumpList is 10. |
61 // https://msdn.microsoft.com/library/windows/desktop/dd378398.aspx | 59 // https://msdn.microsoft.com/library/windows/desktop/dd378398.aspx |
62 // The "Most visited" and "Recently closed" category titles always take 2 slots. | 60 // 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 | 61 // For the remaining 8 slots, we allocate 5 slots to "most-visited" items and 3 |
64 // slots to "recently-closed" items, respectively. | 62 // slots to "recently-closed" items, respectively. |
65 constexpr size_t kMostVisitedItems = 5; | 63 constexpr size_t kMostVisitedItems = 5; |
66 constexpr size_t kRecentlyClosedItems = 3; | 64 constexpr size_t kRecentlyClosedItems = 3; |
67 | 65 |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
189 base::FilePath GenerateJumplistIconDirName( | 187 base::FilePath GenerateJumplistIconDirName( |
190 const base::FilePath& profile_dir, | 188 const base::FilePath& profile_dir, |
191 const base::FilePath::StringPieceType& suffix) { | 189 const base::FilePath::StringPieceType& suffix) { |
192 base::FilePath::StringType dir_name(chrome::kJumpListIconDirname); | 190 base::FilePath::StringType dir_name(chrome::kJumpListIconDirname); |
193 suffix.AppendToString(&dir_name); | 191 suffix.AppendToString(&dir_name); |
194 return profile_dir.Append(dir_name); | 192 return profile_dir.Append(dir_name); |
195 } | 193 } |
196 | 194 |
197 } // namespace | 195 } // namespace |
198 | 196 |
199 JumpList::JumpListData::JumpListData() {} | 197 JumpList::UpdateResults::UpdateResults() {} |
200 | 198 |
201 JumpList::JumpListData::~JumpListData() {} | 199 JumpList::UpdateResults::~UpdateResults() {} |
202 | 200 |
203 JumpList::JumpList(Profile* profile) | 201 JumpList::JumpList(Profile* profile) |
204 : RefcountedKeyedService(content::BrowserThread::GetTaskRunnerForThread( | 202 : 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( | 203 update_jumplist_task_runner_(base::CreateCOMSTATaskRunnerWithTraits( |
210 {base::MayBlock(), base::TaskPriority::USER_VISIBLE, | 204 {base::MayBlock(), base::TaskPriority::USER_VISIBLE, |
211 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})), | 205 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})), |
212 delete_jumplisticons_task_runner_( | 206 delete_jumplisticons_task_runner_( |
213 base::CreateSequencedTaskRunnerWithTraits( | 207 base::CreateSequencedTaskRunnerWithTraits( |
214 {base::MayBlock(), base::TaskPriority::BACKGROUND, | 208 {base::MayBlock(), base::TaskPriority::BACKGROUND, |
215 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})), | 209 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})), |
216 weak_ptr_factory_(this) { | 210 weak_ptr_factory_(this) { |
217 DCHECK(Enabled()); | 211 DCHECK(Enabled()); |
218 // To update JumpList when a tab is added or removed, we add this object to | 212 // To update JumpList when a tab is added or removed, we add this object to |
219 // the observer list of the TabRestoreService class. | 213 // the observer list of the TabRestoreService class. |
220 // When we add this object to the observer list, we save the pointer to this | 214 // 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 | 215 // TabRestoreService object. This pointer is used when we remove this object |
222 // from the observer list. | 216 // from the observer list. |
223 sessions::TabRestoreService* tab_restore_service = | 217 sessions::TabRestoreService* tab_restore_service = |
224 TabRestoreServiceFactory::GetForProfile(profile_); | 218 TabRestoreServiceFactory::GetForProfile(profile_); |
225 if (!tab_restore_service) | 219 if (!tab_restore_service) |
226 return; | 220 return; |
227 | 221 |
228 app_id_ = | 222 app_id_ = |
229 shell_integration::win::GetChromiumModelIdForProfile(profile_->GetPath()); | 223 shell_integration::win::GetChromiumModelIdForProfile(profile_->GetPath()); |
230 | 224 |
| 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. |
231 scoped_refptr<history::TopSites> top_sites = | 228 scoped_refptr<history::TopSites> top_sites = |
232 TopSitesFactory::GetForProfile(profile_); | 229 TopSitesFactory::GetForProfile(profile_); |
233 if (top_sites) { | 230 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); | 231 top_sites->AddObserver(this); |
238 } | 232 |
| 233 // Register as TabRestoreServiceObserver so that we can update ourselves when |
| 234 // recently closed tabs have changes. |
239 tab_restore_service->AddObserver(this); | 235 tab_restore_service->AddObserver(this); |
| 236 |
| 237 // kIncognitoModeAvailability is monitored for changes on Incognito mode. |
240 pref_change_registrar_.reset(new PrefChangeRegistrar); | 238 pref_change_registrar_.reset(new PrefChangeRegistrar); |
241 pref_change_registrar_->Init(profile_->GetPrefs()); | 239 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, this)); | 244 base::Bind(&JumpList::OnIncognitoAvailabilityChanged, |
| 245 base::Unretained(this))); |
245 } | 246 } |
246 | 247 |
247 JumpList::~JumpList() { | 248 JumpList::~JumpList() { |
248 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 249 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
249 Terminate(); | 250 Terminate(); |
250 } | 251 } |
251 | 252 |
252 // static | 253 // static |
253 bool JumpList::Enabled() { | 254 bool JumpList::Enabled() { |
254 return JumpListUpdater::IsEnabled(); | 255 return JumpListUpdater::IsEnabled(); |
255 } | 256 } |
256 | 257 |
257 void JumpList::CancelPendingUpdate() { | 258 void JumpList::CancelPendingUpdate() { |
258 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 259 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. |
259 if (task_id_ != base::CancelableTaskTracker::kBadTaskId) { | 265 if (task_id_ != base::CancelableTaskTracker::kBadTaskId) { |
260 cancelable_task_tracker_.TryCancel(task_id_); | 266 cancelable_task_tracker_.TryCancel(task_id_); |
261 task_id_ = base::CancelableTaskTracker::kBadTaskId; | 267 task_id_ = base::CancelableTaskTracker::kBadTaskId; |
262 } | 268 } |
263 } | 269 } |
264 | 270 |
265 void JumpList::Terminate() { | 271 void JumpList::Terminate() { |
266 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 272 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
267 timer_most_visited_.Stop(); | 273 timer_.Stop(); |
268 timer_recently_closed_.Stop(); | |
269 CancelPendingUpdate(); | 274 CancelPendingUpdate(); |
| 275 update_in_progress_ = false; |
270 if (profile_) { | 276 if (profile_) { |
271 sessions::TabRestoreService* tab_restore_service = | 277 sessions::TabRestoreService* tab_restore_service = |
272 TabRestoreServiceFactory::GetForProfile(profile_); | 278 TabRestoreServiceFactory::GetForProfile(profile_); |
273 if (tab_restore_service) | 279 if (tab_restore_service) |
274 tab_restore_service->RemoveObserver(this); | 280 tab_restore_service->RemoveObserver(this); |
275 scoped_refptr<history::TopSites> top_sites = | 281 scoped_refptr<history::TopSites> top_sites = |
276 TopSitesFactory::GetForProfile(profile_); | 282 TopSitesFactory::GetForProfile(profile_); |
277 if (top_sites) | 283 if (top_sites) |
278 top_sites->RemoveObserver(this); | 284 top_sites->RemoveObserver(this); |
279 pref_change_registrar_.reset(); | 285 pref_change_registrar_.reset(); |
280 } | 286 } |
281 profile_ = NULL; | 287 profile_ = nullptr; |
282 } | 288 } |
283 | 289 |
284 void JumpList::ShutdownOnUIThread() { | 290 void JumpList::Shutdown() { |
285 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 291 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
286 Terminate(); | 292 Terminate(); |
287 } | 293 } |
288 | 294 |
289 void JumpList::OnMostVisitedURLsAvailable( | 295 void JumpList::OnMostVisitedURLsAvailable( |
290 const history::MostVisitedURLList& urls) { | 296 const history::MostVisitedURLList& urls) { |
291 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 297 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
292 | 298 |
293 { | 299 top_sites_has_pending_notification_ = false; |
294 JumpListData* data = &jumplist_data_->data; | |
295 base::AutoLock auto_lock(data->list_lock_); | |
296 | 300 |
297 // There is no need to update the JumpList if the top most visited sites in | 301 // There is no need to update the JumpList if the top most visited sites in |
298 // display have not changed. | 302 // display have not changed. |
299 if (MostVisitedItemsUnchanged(data->most_visited_pages_, urls, | 303 if (MostVisitedItemsUnchanged(most_visited_pages_, urls, kMostVisitedItems)) |
300 kMostVisitedItems)) { | 304 return; |
301 return; | |
302 } | |
303 | 305 |
304 data->most_visited_pages_.clear(); | 306 most_visited_pages_.clear(); |
305 | 307 |
306 for (size_t i = 0; i < urls.size() && i < kMostVisitedItems; i++) { | 308 const size_t num_items = std::min(urls.size(), kMostVisitedItems); |
307 const history::MostVisitedURL& url = urls[i]; | 309 for (size_t i = 0; i < num_items; ++i) { |
308 scoped_refptr<ShellLinkItem> link = CreateShellLink(); | 310 const history::MostVisitedURL& url = urls[i]; |
309 std::string url_string = url.url.spec(); | 311 scoped_refptr<ShellLinkItem> link = CreateShellLink(); |
310 base::string16 url_string_wide = base::UTF8ToUTF16(url_string); | 312 std::string url_string = url.url.spec(); |
311 link->GetCommandLine()->AppendArgNative(url_string_wide); | 313 base::string16 url_string_wide = base::UTF8ToUTF16(url_string); |
312 link->GetCommandLine()->AppendSwitchASCII( | 314 link->GetCommandLine()->AppendArgNative(url_string_wide); |
313 switches::kWinJumplistAction, jumplist::kMostVisitedCategory); | 315 link->GetCommandLine()->AppendSwitchASCII(switches::kWinJumplistAction, |
314 link->set_title(!url.title.empty() ? url.title : url_string_wide); | 316 jumplist::kMostVisitedCategory); |
315 link->set_url(url_string); | 317 link->set_title(!url.title.empty() ? url.title : url_string_wide); |
316 data->most_visited_pages_.push_back(link); | 318 link->set_url(url_string); |
317 data->icon_urls_.push_back(std::make_pair(url_string, link)); | 319 most_visited_pages_.push_back(link); |
318 } | 320 icon_urls_.emplace_back(std::move(url_string), std::move(link)); |
319 data->most_visited_pages_have_updates_ = true; | |
320 } | 321 } |
321 | 322 |
| 323 most_visited_should_update_ = true; |
| 324 |
322 // Send a query that retrieves the first favicon. | 325 // Send a query that retrieves the first favicon. |
323 StartLoadingFavicon(); | 326 StartLoadingFavicon(); |
324 } | 327 } |
325 | 328 |
326 void JumpList::TabRestoreServiceChanged(sessions::TabRestoreService* service) { | 329 void JumpList::TabRestoreServiceChanged(sessions::TabRestoreService* service) { |
327 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 330 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
328 | 331 |
329 // if we have a pending favicon request, cancel it here (it is out of date). | 332 tab_restore_has_pending_notification_ = true; |
| 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. |
330 CancelPendingUpdate(); | 339 CancelPendingUpdate(); |
331 | 340 |
332 // Initialize the one-shot timer to update the the "Recently Closed" category | 341 // 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 | 342 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 } | 343 } |
346 | 344 |
347 void JumpList::TabRestoreServiceDestroyed( | 345 void JumpList::TabRestoreServiceDestroyed( |
348 sessions::TabRestoreService* service) {} | 346 sessions::TabRestoreService* service) {} |
349 | 347 |
350 bool JumpList::AddTab(const sessions::TabRestoreService::Tab& tab, | 348 bool JumpList::AddTab(const sessions::TabRestoreService::Tab& tab, |
351 size_t max_items, | 349 size_t max_items) { |
352 JumpListData* data) { | |
353 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 350 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
354 data->list_lock_.AssertAcquired(); | |
355 | 351 |
356 // This code adds the URL and the title strings of the given tab to |data|. | 352 // This code adds the URL and the title strings of the given tab to the |
357 if (data->recently_closed_pages_.size() >= max_items) | 353 // JumpList variables. |
| 354 if (recently_closed_pages_.size() >= max_items) |
358 return false; | 355 return false; |
359 | 356 |
360 scoped_refptr<ShellLinkItem> link = CreateShellLink(); | 357 scoped_refptr<ShellLinkItem> link = CreateShellLink(); |
361 const sessions::SerializedNavigationEntry& current_navigation = | 358 const sessions::SerializedNavigationEntry& current_navigation = |
362 tab.navigations.at(tab.current_navigation_index); | 359 tab.navigations.at(tab.current_navigation_index); |
363 std::string url = current_navigation.virtual_url().spec(); | 360 std::string url = current_navigation.virtual_url().spec(); |
364 link->GetCommandLine()->AppendArgNative(base::UTF8ToUTF16(url)); | 361 link->GetCommandLine()->AppendArgNative(base::UTF8ToUTF16(url)); |
365 link->GetCommandLine()->AppendSwitchASCII(switches::kWinJumplistAction, | 362 link->GetCommandLine()->AppendSwitchASCII(switches::kWinJumplistAction, |
366 jumplist::kRecentlyClosedCategory); | 363 jumplist::kRecentlyClosedCategory); |
367 link->set_title(current_navigation.title()); | 364 link->set_title(current_navigation.title()); |
368 link->set_url(url); | 365 link->set_url(url); |
369 data->recently_closed_pages_.push_back(link); | 366 recently_closed_pages_.push_back(link); |
370 data->icon_urls_.push_back(std::make_pair(std::move(url), std::move(link))); | 367 icon_urls_.emplace_back(std::move(url), std::move(link)); |
371 | 368 |
372 return true; | 369 return true; |
373 } | 370 } |
374 | 371 |
375 void JumpList::AddWindow(const sessions::TabRestoreService::Window& window, | 372 void JumpList::AddWindow(const sessions::TabRestoreService::Window& window, |
376 size_t max_items, | 373 size_t max_items) { |
377 JumpListData* data) { | |
378 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 374 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()); | 375 DCHECK(!window.tabs.empty()); |
384 | 376 |
385 for (const auto& tab : window.tabs) { | 377 for (const auto& tab : window.tabs) { |
386 if (!AddTab(*tab, max_items, data)) | 378 if (!AddTab(*tab, max_items)) |
387 return; | 379 return; |
388 } | 380 } |
389 } | 381 } |
390 | 382 |
391 void JumpList::StartLoadingFavicon() { | 383 void JumpList::StartLoadingFavicon() { |
392 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 384 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
393 | 385 |
394 base::ElapsedTimer timer; | 386 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 | 387 // No more favicons are needed by the application JumpList. Schedule a |
411 // RunUpdateJumpList call. | 388 // RunUpdateJumpList call. |
412 PostRunUpdate(); | 389 PostRunUpdate(); |
413 return; | 390 return; |
414 } | 391 } |
415 | 392 |
| 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(). |
416 favicon::FaviconService* favicon_service = | 397 favicon::FaviconService* favicon_service = |
417 FaviconServiceFactory::GetForProfile(profile_, | 398 FaviconServiceFactory::GetForProfile(profile_, |
418 ServiceAccessType::EXPLICIT_ACCESS); | 399 ServiceAccessType::EXPLICIT_ACCESS); |
| 400 // base::Unretained is safe since |this| is guaranteed to outlive |
| 401 // cancelable_task_tracker_. |
419 task_id_ = favicon_service->GetFaviconImageForPageURL( | 402 task_id_ = favicon_service->GetFaviconImageForPageURL( |
420 url, | 403 GURL(icon_urls_.front().first), |
421 base::Bind(&JumpList::OnFaviconDataAvailable, base::Unretained(this)), | 404 base::Bind(&JumpList::OnFaviconDataAvailable, base::Unretained(this)), |
422 &cancelable_task_tracker_); | 405 &cancelable_task_tracker_); |
423 | 406 |
424 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/717236 | 407 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/717236 |
425 UMA_HISTOGRAM_TIMES("WinJumplist.StartLoadingFaviconDuration", | 408 UMA_HISTOGRAM_TIMES("WinJumplist.StartLoadingFaviconDuration", |
426 timer.Elapsed()); | 409 timer.Elapsed()); |
427 } | 410 } |
428 | 411 |
429 void JumpList::OnFaviconDataAvailable( | 412 void JumpList::OnFaviconDataAvailable( |
430 const favicon_base::FaviconImageResult& image_result) { | 413 const favicon_base::FaviconImageResult& image_result) { |
431 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 414 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
432 | 415 |
433 base::ElapsedTimer timer; | 416 base::ElapsedTimer timer; |
434 | 417 |
435 // If there is currently a favicon request in progress, it is now outdated, | 418 // 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. | 419 // as we have received another, so nullify the handle from the old request. |
437 task_id_ = base::CancelableTaskTracker::kBadTaskId; | 420 task_id_ = base::CancelableTaskTracker::kBadTaskId; |
438 // Lock the list to set icon data and pop the url. | 421 |
439 { | 422 // Attach the received data to the ShellLinkItem object. This data will be |
440 JumpListData* data = &jumplist_data_->data; | 423 // decoded by the RunUpdateJumpList method. |
441 base::AutoLock auto_lock(data->list_lock_); | 424 if (!icon_urls_.empty()) { |
442 // Attach the received data to the ShellLinkItem object. | 425 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(); | 426 gfx::ImageSkia image_skia = image_result.image.AsImageSkia(); |
448 image_skia.EnsureRepsForSupportedScales(); | 427 image_skia.EnsureRepsForSupportedScales(); |
449 std::unique_ptr<gfx::ImageSkia> deep_copy(image_skia.DeepCopy()); | 428 std::unique_ptr<gfx::ImageSkia> deep_copy(image_skia.DeepCopy()); |
450 data->icon_urls_.front().second->set_icon_image(*deep_copy); | 429 icon_urls_.front().second->set_icon_image(*deep_copy); |
451 } | 430 } |
452 | 431 icon_urls_.pop_front(); |
453 if (!data->icon_urls_.empty()) | |
454 data->icon_urls_.pop_front(); | |
455 } | 432 } |
456 | 433 |
457 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/717236 | 434 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/717236 |
458 UMA_HISTOGRAM_TIMES("WinJumplist.OnFaviconDataAvailableDuration", | 435 UMA_HISTOGRAM_TIMES("WinJumplist.OnFaviconDataAvailableDuration", |
459 timer.Elapsed()); | 436 timer.Elapsed()); |
460 | 437 |
461 // Check whether we need to load more favicons. | 438 // Check whether we need to load more favicons. |
462 StartLoadingFavicon(); | 439 StartLoadingFavicon(); |
463 } | 440 } |
464 | 441 |
465 void JumpList::OnIncognitoAvailabilityChanged() { | 442 void JumpList::OnIncognitoAvailabilityChanged() { |
466 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 443 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
467 | 444 |
468 bool waiting_for_icons = true; | 445 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(); | 446 PostRunUpdate(); |
480 } | 447 } |
481 | 448 |
482 void JumpList::PostRunUpdate() { | 449 void JumpList::PostRunUpdate() { |
483 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 450 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
484 | 451 |
485 TRACE_EVENT0("browser", "JumpList::PostRunUpdate"); | 452 TRACE_EVENT0("browser", "JumpList::PostRunUpdate"); |
486 if (!profile_) | 453 |
487 return; | 454 update_in_progress_ = true; |
488 | 455 |
489 base::FilePath profile_dir = profile_->GetPath(); | 456 base::FilePath profile_dir = profile_->GetPath(); |
490 | 457 |
491 // Check if incognito windows (or normal windows) are disabled by policy. | 458 // Check if incognito windows (or normal windows) are disabled by policy. |
492 IncognitoModePrefs::Availability incognito_availability = | 459 IncognitoModePrefs::Availability incognito_availability = |
493 IncognitoModePrefs::GetAvailability(profile_->GetPrefs()); | 460 IncognitoModePrefs::GetAvailability(profile_->GetPrefs()); |
494 | 461 |
495 // Post a task to update the JumpList, which consists of 1) delete old icons, | 462 // Make local copies of JumpList member variables and use them for an update. |
496 // 2) create new icons, 3) notify the OS. | 463 ShellLinkItemList local_most_visited_pages = most_visited_pages_; |
497 update_jumplist_task_runner_->PostTask( | 464 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 | 465 |
502 // Post a task to delete JumpListIcons folder as it's no longer needed. | 466 bool most_visited_should_update = most_visited_should_update_; |
503 // Now we have JumpListIconsMostVisited folder and JumpListIconsRecentClosed | 467 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 | 468 |
508 delete_jumplisticons_task_runner_->PostTask( | 469 auto update_results = base::MakeUnique<UpdateResults>(); |
509 FROM_HERE, | 470 update_results->most_visited_icons_in_update = most_visited_icons_; |
510 base::Bind(&DeleteDirectory, std::move(icon_dir), kFileDeleteLimit)); | 471 update_results->recently_closed_icons_in_update = recently_closed_icons_; |
511 | 472 |
512 // Post a task to delete JumpListIconsOld folder as it's no longer needed. | 473 // Post a task to update the JumpList, which consists of 1) create new icons, |
513 base::FilePath icon_dir_old = | 474 // 2) delete old icons, 3) notify the OS. |
514 GenerateJumplistIconDirName(profile_dir, FILE_PATH_LITERAL("Old")); | 475 if (!update_jumplist_task_runner_->PostTaskAndReply( |
515 | 476 FROM_HERE, |
516 delete_jumplisticons_task_runner_->PostTask( | 477 base::Bind(&JumpList::RunUpdateJumpList, app_id_, profile_dir, |
517 FROM_HERE, | 478 local_most_visited_pages, local_recently_closed_pages, |
518 base::Bind(&DeleteDirectory, std::move(icon_dir_old), kFileDeleteLimit)); | 479 most_visited_should_update, recently_closed_should_update, |
| 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 } |
519 } | 486 } |
520 | 487 |
521 void JumpList::TopSitesLoaded(history::TopSites* top_sites) { | 488 void JumpList::TopSitesLoaded(history::TopSites* top_sites) { |
522 } | 489 } |
523 | 490 |
524 void JumpList::TopSitesChanged(history::TopSites* top_sites, | 491 void JumpList::TopSitesChanged(history::TopSites* top_sites, |
525 ChangeReason change_reason) { | 492 ChangeReason change_reason) { |
526 // If we have a pending favicon request, cancel it here (it is out of date). | 493 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 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. |
527 CancelPendingUpdate(); | 502 CancelPendingUpdate(); |
528 | 503 |
529 // Initialize the one-shot timer to update the the "Most visited" category in | 504 // 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 | 505 InitializeTimerForUpdate(); |
531 // new request. This ensures that JumpList update of the "Most visited" | 506 } |
532 // category won't happen until there has been a brief quiet period, thus | 507 |
533 // avoiding update storms. | 508 void JumpList::InitializeTimerForUpdate() { |
534 if (timer_most_visited_.IsRunning()) { | 509 if (timer_.IsRunning()) { |
535 timer_most_visited_.Reset(); | 510 timer_.Reset(); |
536 } else { | 511 } else { |
537 timer_most_visited_.Start( | 512 // base::Unretained is safe since |this| is guaranteed to outlive timer_. |
538 FROM_HERE, kDelayForJumplistUpdate, | 513 timer_.Start(FROM_HERE, kDelayForJumplistUpdate, |
539 base::Bind(&JumpList::DeferredTopSitesChanged, base::Unretained(this))); | 514 base::Bind(&JumpList::OnDelayTimer, base::Unretained(this))); |
540 } | 515 } |
541 } | 516 } |
542 | 517 |
543 void JumpList::DeferredTopSitesChanged() { | 518 void JumpList::OnDelayTimer() { |
544 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 519 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
545 | 520 |
546 if (updates_to_skip_ > 0) { | 521 if (updates_to_skip_ > 0) { |
547 --updates_to_skip_; | 522 --updates_to_skip_; |
548 return; | 523 return; |
549 } | 524 } |
550 | 525 |
| 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 |
551 // Opening the first tab in one session triggers a TopSite history sync. | 544 // 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" | 545 // Delay this sync till the first tab is closed to allow the "recently closed" |
553 // category from last session to stay longer. | 546 // category from last session to stay longer. All previous pending |
554 if (!has_tab_closed_) | 547 // notifications from TopSites are ignored. |
| 548 if (!has_tab_closed_) { |
| 549 top_sites_has_pending_notification_ = false; |
555 return; | 550 return; |
| 551 } |
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 tasks 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 |