Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(155)

Side by Side Diff: chrome/browser/win/jumplist.cc

Issue 2937993002: Revert of Fix stability and data racing issues, coalesce more updates for JumpList (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/win/jumplist.h ('k') | chrome/browser/win/jumplist_factory.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 }
OLDNEW
« no previous file with comments | « chrome/browser/win/jumplist.h ('k') | chrome/browser/win/jumplist_factory.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698