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 |