| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/jumplist_win.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/bind_helpers.h" | |
| 9 #include "base/command_line.h" | |
| 10 #include "base/files/file_util.h" | |
| 11 #include "base/macros.h" | |
| 12 #include "base/path_service.h" | |
| 13 #include "base/strings/string_util.h" | |
| 14 #include "base/strings/utf_string_conversions.h" | |
| 15 #include "base/threading/thread.h" | |
| 16 #include "base/trace_event/trace_event.h" | |
| 17 #include "chrome/browser/chrome_notification_types.h" | |
| 18 #include "chrome/browser/favicon/favicon_service_factory.h" | |
| 19 #include "chrome/browser/history/top_sites_factory.h" | |
| 20 #include "chrome/browser/metrics/jumplist_metrics_win.h" | |
| 21 #include "chrome/browser/profiles/profile.h" | |
| 22 #include "chrome/browser/sessions/tab_restore_service_factory.h" | |
| 23 #include "chrome/browser/shell_integration_win.h" | |
| 24 #include "chrome/common/chrome_constants.h" | |
| 25 #include "chrome/common/chrome_switches.h" | |
| 26 #include "chrome/common/pref_names.h" | |
| 27 #include "chrome/common/url_constants.h" | |
| 28 #include "chrome/grit/generated_resources.h" | |
| 29 #include "components/favicon/core/favicon_service.h" | |
| 30 #include "components/favicon_base/favicon_types.h" | |
| 31 #include "components/history/core/browser/history_service.h" | |
| 32 #include "components/history/core/browser/page_usage_data.h" | |
| 33 #include "components/history/core/browser/top_sites.h" | |
| 34 #include "components/prefs/pref_change_registrar.h" | |
| 35 #include "components/sessions/core/session_types.h" | |
| 36 #include "components/sessions/core/tab_restore_service.h" | |
| 37 #include "components/strings/grit/components_strings.h" | |
| 38 #include "content/public/browser/browser_thread.h" | |
| 39 #include "content/public/browser/notification_registrar.h" | |
| 40 #include "content/public/browser/notification_source.h" | |
| 41 #include "ui/base/l10n/l10n_util.h" | |
| 42 #include "ui/gfx/codec/png_codec.h" | |
| 43 #include "ui/gfx/favicon_size.h" | |
| 44 #include "ui/gfx/icon_util.h" | |
| 45 #include "ui/gfx/image/image_family.h" | |
| 46 #include "url/gurl.h" | |
| 47 | |
| 48 using content::BrowserThread; | |
| 49 using JumpListData = JumpList::JumpListData; | |
| 50 | |
| 51 namespace { | |
| 52 | |
| 53 // Delay jumplist updates to allow collapsing of redundant update requests. | |
| 54 const int kDelayForJumplistUpdateInMS = 3500; | |
| 55 | |
| 56 // Append the common switches to each shell link. | |
| 57 void AppendCommonSwitches(ShellLinkItem* shell_link) { | |
| 58 const char* kSwitchNames[] = { switches::kUserDataDir }; | |
| 59 const base::CommandLine& command_line = | |
| 60 *base::CommandLine::ForCurrentProcess(); | |
| 61 shell_link->GetCommandLine()->CopySwitchesFrom(command_line, | |
| 62 kSwitchNames, | |
| 63 arraysize(kSwitchNames)); | |
| 64 } | |
| 65 | |
| 66 // Create a ShellLinkItem preloaded with common switches. | |
| 67 scoped_refptr<ShellLinkItem> CreateShellLink() { | |
| 68 scoped_refptr<ShellLinkItem> link(new ShellLinkItem); | |
| 69 AppendCommonSwitches(link.get()); | |
| 70 return link; | |
| 71 } | |
| 72 | |
| 73 // Creates a temporary icon file to be shown in JumpList. | |
| 74 bool CreateIconFile(const SkBitmap& bitmap, | |
| 75 const base::FilePath& icon_dir, | |
| 76 base::FilePath* icon_path) { | |
| 77 // Retrieve the path to a temporary file. | |
| 78 // We don't have to care about the extension of this temporary file because | |
| 79 // JumpList does not care about it. | |
| 80 base::FilePath path; | |
| 81 if (!base::CreateTemporaryFileInDir(icon_dir, &path)) | |
| 82 return false; | |
| 83 | |
| 84 // Create an icon file from the favicon attached to the given |page|, and | |
| 85 // save it as the temporary file. | |
| 86 gfx::ImageFamily image_family; | |
| 87 image_family.Add(gfx::Image::CreateFrom1xBitmap(bitmap)); | |
| 88 if (!IconUtil::CreateIconFileFromImageFamily(image_family, path, | |
| 89 IconUtil::NORMAL_WRITE)) | |
| 90 return false; | |
| 91 | |
| 92 // Add this icon file to the list and return its absolute path. | |
| 93 // The IShellLink::SetIcon() function needs the absolute path to an icon. | |
| 94 *icon_path = path; | |
| 95 return true; | |
| 96 } | |
| 97 | |
| 98 // Helper method for RunUpdate to create icon files for the asynchrounously | |
| 99 // loaded icons. | |
| 100 void CreateIconFiles(const base::FilePath& icon_dir, | |
| 101 const ShellLinkItemList& item_list) { | |
| 102 for (ShellLinkItemList::const_iterator item = item_list.begin(); | |
| 103 item != item_list.end(); ++item) { | |
| 104 base::FilePath icon_path; | |
| 105 if (CreateIconFile((*item)->icon_data(), icon_dir, &icon_path)) | |
| 106 (*item)->set_icon(icon_path.value(), 0); | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 // Updates the "Tasks" category of the JumpList. | |
| 111 bool UpdateTaskCategory( | |
| 112 JumpListUpdater* jumplist_updater, | |
| 113 IncognitoModePrefs::Availability incognito_availability) { | |
| 114 base::FilePath chrome_path; | |
| 115 if (!PathService::Get(base::FILE_EXE, &chrome_path)) | |
| 116 return false; | |
| 117 | |
| 118 ShellLinkItemList items; | |
| 119 | |
| 120 // Create an IShellLink object which launches Chrome, and add it to the | |
| 121 // collection. We use our application icon as the icon for this item. | |
| 122 // We remove '&' characters from this string so we can share it with our | |
| 123 // system menu. | |
| 124 if (incognito_availability != IncognitoModePrefs::FORCED) { | |
| 125 scoped_refptr<ShellLinkItem> chrome = CreateShellLink(); | |
| 126 base::string16 chrome_title = l10n_util::GetStringUTF16(IDS_NEW_WINDOW); | |
| 127 base::ReplaceSubstringsAfterOffset( | |
| 128 &chrome_title, 0, L"&", base::StringPiece16()); | |
| 129 chrome->set_title(chrome_title); | |
| 130 chrome->set_icon(chrome_path.value(), 0); | |
| 131 items.push_back(chrome); | |
| 132 } | |
| 133 | |
| 134 // Create an IShellLink object which launches Chrome in incognito mode, and | |
| 135 // add it to the collection. We use our application icon as the icon for | |
| 136 // this item. | |
| 137 if (incognito_availability != IncognitoModePrefs::DISABLED) { | |
| 138 scoped_refptr<ShellLinkItem> incognito = CreateShellLink(); | |
| 139 incognito->GetCommandLine()->AppendSwitch(switches::kIncognito); | |
| 140 base::string16 incognito_title = | |
| 141 l10n_util::GetStringUTF16(IDS_NEW_INCOGNITO_WINDOW); | |
| 142 base::ReplaceSubstringsAfterOffset( | |
| 143 &incognito_title, 0, L"&", base::StringPiece16()); | |
| 144 incognito->set_title(incognito_title); | |
| 145 incognito->set_icon(chrome_path.value(), 0); | |
| 146 items.push_back(incognito); | |
| 147 } | |
| 148 | |
| 149 return jumplist_updater->AddTasks(items); | |
| 150 } | |
| 151 | |
| 152 // Updates the application JumpList. | |
| 153 bool UpdateJumpList(const wchar_t* app_id, | |
| 154 const ShellLinkItemList& most_visited_pages, | |
| 155 const ShellLinkItemList& recently_closed_pages, | |
| 156 IncognitoModePrefs::Availability incognito_availability) { | |
| 157 // JumpList is implemented only on Windows 7 or later. | |
| 158 // So, we should return now when this function is called on earlier versions | |
| 159 // of Windows. | |
| 160 if (!JumpListUpdater::IsEnabled()) | |
| 161 return true; | |
| 162 | |
| 163 JumpListUpdater jumplist_updater(app_id); | |
| 164 if (!jumplist_updater.BeginUpdate()) | |
| 165 return false; | |
| 166 | |
| 167 // We allocate 60% of the given JumpList slots to "most-visited" items | |
| 168 // and 40% to "recently-closed" items, respectively. | |
| 169 // Nevertheless, if there are not so many items in |recently_closed_pages|, | |
| 170 // we give the remaining slots to "most-visited" items. | |
| 171 const int kMostVisited = 60; | |
| 172 const int kRecentlyClosed = 40; | |
| 173 const int kTotal = kMostVisited + kRecentlyClosed; | |
| 174 size_t most_visited_items = | |
| 175 MulDiv(jumplist_updater.user_max_items(), kMostVisited, kTotal); | |
| 176 size_t recently_closed_items = | |
| 177 jumplist_updater.user_max_items() - most_visited_items; | |
| 178 if (recently_closed_pages.size() < recently_closed_items) { | |
| 179 most_visited_items += recently_closed_items - recently_closed_pages.size(); | |
| 180 recently_closed_items = recently_closed_pages.size(); | |
| 181 } | |
| 182 | |
| 183 // Update the "Most Visited" category of the JumpList if it exists. | |
| 184 // This update request is applied into the JumpList when we commit this | |
| 185 // transaction. | |
| 186 if (!jumplist_updater.AddCustomCategory( | |
| 187 l10n_util::GetStringUTF16(IDS_NEW_TAB_MOST_VISITED), | |
| 188 most_visited_pages, most_visited_items)) { | |
| 189 return false; | |
| 190 } | |
| 191 | |
| 192 // Update the "Recently Closed" category of the JumpList. | |
| 193 if (!jumplist_updater.AddCustomCategory( | |
| 194 l10n_util::GetStringUTF16(IDS_RECENTLY_CLOSED), | |
| 195 recently_closed_pages, recently_closed_items)) { | |
| 196 return false; | |
| 197 } | |
| 198 | |
| 199 // Update the "Tasks" category of the JumpList. | |
| 200 if (!UpdateTaskCategory(&jumplist_updater, incognito_availability)) | |
| 201 return false; | |
| 202 | |
| 203 // Commit this transaction and send the updated JumpList to Windows. | |
| 204 if (!jumplist_updater.CommitUpdate()) | |
| 205 return false; | |
| 206 | |
| 207 return true; | |
| 208 } | |
| 209 | |
| 210 // Updates the jumplist, once all the data has been fetched. | |
| 211 void RunUpdateOnFileThread( | |
| 212 IncognitoModePrefs::Availability incognito_availability, | |
| 213 const std::wstring& app_id, | |
| 214 const base::FilePath& icon_dir, | |
| 215 base::RefCountedData<JumpListData>* ref_counted_data) { | |
| 216 JumpListData* data = &ref_counted_data->data; | |
| 217 ShellLinkItemList local_most_visited_pages; | |
| 218 ShellLinkItemList local_recently_closed_pages; | |
| 219 | |
| 220 { | |
| 221 base::AutoLock auto_lock(data->list_lock_); | |
| 222 // Make sure we are not out of date: if icon_urls_ is not empty, then | |
| 223 // another notification has been received since we processed this one | |
| 224 if (!data->icon_urls_.empty()) | |
| 225 return; | |
| 226 | |
| 227 // Make local copies of lists so we can release the lock. | |
| 228 local_most_visited_pages = data->most_visited_pages_; | |
| 229 local_recently_closed_pages = data->recently_closed_pages_; | |
| 230 } | |
| 231 | |
| 232 // Delete the directory which contains old icon files, rename the current | |
| 233 // icon directory, and create a new directory which contains new JumpList | |
| 234 // icon files. | |
| 235 base::FilePath icon_dir_old(icon_dir.value() + L"Old"); | |
| 236 if (base::PathExists(icon_dir_old)) | |
| 237 base::DeleteFile(icon_dir_old, true); | |
| 238 base::Move(icon_dir, icon_dir_old); | |
| 239 base::CreateDirectory(icon_dir); | |
| 240 | |
| 241 // Create temporary icon files for shortcuts in the "Most Visited" category. | |
| 242 CreateIconFiles(icon_dir, local_most_visited_pages); | |
| 243 | |
| 244 // Create temporary icon files for shortcuts in the "Recently Closed" | |
| 245 // category. | |
| 246 CreateIconFiles(icon_dir, local_recently_closed_pages); | |
| 247 | |
| 248 // We finished collecting all resources needed for updating an application | |
| 249 // JumpList. So, create a new JumpList and replace the current JumpList | |
| 250 // with it. | |
| 251 UpdateJumpList(app_id.c_str(), | |
| 252 local_most_visited_pages, | |
| 253 local_recently_closed_pages, | |
| 254 incognito_availability); | |
| 255 } | |
| 256 | |
| 257 } // namespace | |
| 258 | |
| 259 JumpList::JumpListData::JumpListData() {} | |
| 260 | |
| 261 JumpList::JumpListData::~JumpListData() {} | |
| 262 | |
| 263 JumpList::JumpList(Profile* profile) | |
| 264 : profile_(profile), | |
| 265 jumplist_data_(new base::RefCountedData<JumpListData>), | |
| 266 task_id_(base::CancelableTaskTracker::kBadTaskId), | |
| 267 weak_ptr_factory_(this) { | |
| 268 DCHECK(Enabled()); | |
| 269 // To update JumpList when a tab is added or removed, we add this object to | |
| 270 // the observer list of the TabRestoreService class. | |
| 271 // When we add this object to the observer list, we save the pointer to this | |
| 272 // TabRestoreService object. This pointer is used when we remove this object | |
| 273 // from the observer list. | |
| 274 sessions::TabRestoreService* tab_restore_service = | |
| 275 TabRestoreServiceFactory::GetForProfile(profile_); | |
| 276 if (!tab_restore_service) | |
| 277 return; | |
| 278 | |
| 279 app_id_ = | |
| 280 shell_integration::win::GetChromiumModelIdForProfile(profile_->GetPath()); | |
| 281 icon_dir_ = profile_->GetPath().Append(chrome::kJumpListIconDirname); | |
| 282 | |
| 283 scoped_refptr<history::TopSites> top_sites = | |
| 284 TopSitesFactory::GetForProfile(profile_); | |
| 285 if (top_sites) { | |
| 286 // TopSites updates itself after a delay. This is especially noticable when | |
| 287 // your profile is empty. Ask TopSites to update itself when jumplist is | |
| 288 // initialized. | |
| 289 top_sites->SyncWithHistory(); | |
| 290 registrar_.reset(new content::NotificationRegistrar); | |
| 291 // Register as TopSitesObserver so that we can update ourselves when the | |
| 292 // TopSites changes. | |
| 293 top_sites->AddObserver(this); | |
| 294 // Register for notification when profile is destroyed to ensure that all | |
| 295 // observers are detatched at that time. | |
| 296 registrar_->Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED, | |
| 297 content::Source<Profile>(profile_)); | |
| 298 } | |
| 299 tab_restore_service->AddObserver(this); | |
| 300 pref_change_registrar_.reset(new PrefChangeRegistrar); | |
| 301 pref_change_registrar_->Init(profile_->GetPrefs()); | |
| 302 pref_change_registrar_->Add( | |
| 303 prefs::kIncognitoModeAvailability, | |
| 304 base::Bind(&JumpList::OnIncognitoAvailabilityChanged, this)); | |
| 305 } | |
| 306 | |
| 307 JumpList::~JumpList() { | |
| 308 DCHECK(CalledOnValidThread()); | |
| 309 Terminate(); | |
| 310 } | |
| 311 | |
| 312 // static | |
| 313 bool JumpList::Enabled() { | |
| 314 return JumpListUpdater::IsEnabled(); | |
| 315 } | |
| 316 | |
| 317 void JumpList::Observe(int type, | |
| 318 const content::NotificationSource& source, | |
| 319 const content::NotificationDetails& details) { | |
| 320 DCHECK(CalledOnValidThread()); | |
| 321 DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type); | |
| 322 // Profile was destroyed, do clean-up. | |
| 323 Terminate(); | |
| 324 } | |
| 325 | |
| 326 void JumpList::CancelPendingUpdate() { | |
| 327 DCHECK(CalledOnValidThread()); | |
| 328 if (task_id_ != base::CancelableTaskTracker::kBadTaskId) { | |
| 329 cancelable_task_tracker_.TryCancel(task_id_); | |
| 330 task_id_ = base::CancelableTaskTracker::kBadTaskId; | |
| 331 } | |
| 332 } | |
| 333 | |
| 334 void JumpList::Terminate() { | |
| 335 DCHECK(CalledOnValidThread()); | |
| 336 CancelPendingUpdate(); | |
| 337 if (profile_) { | |
| 338 sessions::TabRestoreService* tab_restore_service = | |
| 339 TabRestoreServiceFactory::GetForProfile(profile_); | |
| 340 if (tab_restore_service) | |
| 341 tab_restore_service->RemoveObserver(this); | |
| 342 scoped_refptr<history::TopSites> top_sites = | |
| 343 TopSitesFactory::GetForProfile(profile_); | |
| 344 if (top_sites) | |
| 345 top_sites->RemoveObserver(this); | |
| 346 registrar_.reset(); | |
| 347 pref_change_registrar_.reset(); | |
| 348 } | |
| 349 profile_ = NULL; | |
| 350 } | |
| 351 | |
| 352 void JumpList::OnMostVisitedURLsAvailable( | |
| 353 const history::MostVisitedURLList& urls) { | |
| 354 DCHECK(CalledOnValidThread()); | |
| 355 // If we have a pending favicon request, cancel it here (it is out of date). | |
| 356 CancelPendingUpdate(); | |
| 357 | |
| 358 { | |
| 359 JumpListData* data = &jumplist_data_->data; | |
| 360 base::AutoLock auto_lock(data->list_lock_); | |
| 361 data->most_visited_pages_.clear(); | |
| 362 for (size_t i = 0; i < urls.size(); i++) { | |
| 363 const history::MostVisitedURL& url = urls[i]; | |
| 364 scoped_refptr<ShellLinkItem> link = CreateShellLink(); | |
| 365 std::string url_string = url.url.spec(); | |
| 366 std::wstring url_string_wide = base::UTF8ToWide(url_string); | |
| 367 link->GetCommandLine()->AppendArgNative(url_string_wide); | |
| 368 link->GetCommandLine()->AppendSwitchASCII( | |
| 369 switches::kWinJumplistAction, jumplist::kMostVisitedCategory); | |
| 370 link->set_title(!url.title.empty() ? url.title : url_string_wide); | |
| 371 data->most_visited_pages_.push_back(link); | |
| 372 data->icon_urls_.push_back(make_pair(url_string, link)); | |
| 373 } | |
| 374 } | |
| 375 | |
| 376 // Send a query that retrieves the first favicon. | |
| 377 StartLoadingFavicon(); | |
| 378 } | |
| 379 | |
| 380 void JumpList::TabRestoreServiceChanged(sessions::TabRestoreService* service) { | |
| 381 DCHECK(CalledOnValidThread()); | |
| 382 // if we have a pending handle request, cancel it here (it is out of date). | |
| 383 CancelPendingUpdate(); | |
| 384 | |
| 385 // local list to pass to methods | |
| 386 ShellLinkItemList temp_list; | |
| 387 | |
| 388 // Create a list of ShellLinkItems from the "Recently Closed" pages. | |
| 389 // As noted above, we create a ShellLinkItem objects with the following | |
| 390 // parameters. | |
| 391 // * arguments | |
| 392 // The last URL of the tab object. | |
| 393 // * title | |
| 394 // The title of the last URL. | |
| 395 // * icon | |
| 396 // An empty string. This value is to be updated in OnFaviconDataAvailable(). | |
| 397 // This code is copied from | |
| 398 // RecentlyClosedTabsHandler::TabRestoreServiceChanged() to emulate it. | |
| 399 const int kRecentlyClosedCount = 4; | |
| 400 sessions::TabRestoreService* tab_restore_service = | |
| 401 TabRestoreServiceFactory::GetForProfile(profile_); | |
| 402 const sessions::TabRestoreService::Entries& entries = | |
| 403 tab_restore_service->entries(); | |
| 404 for (sessions::TabRestoreService::Entries::const_iterator it = | |
| 405 entries.begin(); | |
| 406 it != entries.end(); ++it) { | |
| 407 const sessions::TabRestoreService::Entry* entry = *it; | |
| 408 if (entry->type == sessions::TabRestoreService::TAB) { | |
| 409 AddTab(static_cast<const sessions::TabRestoreService::Tab*>(entry), | |
| 410 &temp_list, kRecentlyClosedCount); | |
| 411 } else if (entry->type == sessions::TabRestoreService::WINDOW) { | |
| 412 AddWindow(static_cast<const sessions::TabRestoreService::Window*>(entry), | |
| 413 &temp_list, kRecentlyClosedCount); | |
| 414 } | |
| 415 } | |
| 416 // Lock recently_closed_pages and copy temp_list into it. | |
| 417 { | |
| 418 JumpListData* data = &jumplist_data_->data; | |
| 419 base::AutoLock auto_lock(data->list_lock_); | |
| 420 data->recently_closed_pages_ = temp_list; | |
| 421 } | |
| 422 | |
| 423 // Send a query that retrieves the first favicon. | |
| 424 StartLoadingFavicon(); | |
| 425 } | |
| 426 | |
| 427 void JumpList::TabRestoreServiceDestroyed( | |
| 428 sessions::TabRestoreService* service) {} | |
| 429 | |
| 430 bool JumpList::AddTab(const sessions::TabRestoreService::Tab* tab, | |
| 431 ShellLinkItemList* list, | |
| 432 size_t max_items) { | |
| 433 DCHECK(CalledOnValidThread()); | |
| 434 | |
| 435 // This code adds the URL and the title strings of the given tab to the | |
| 436 // specified list. | |
| 437 if (list->size() >= max_items) | |
| 438 return false; | |
| 439 | |
| 440 scoped_refptr<ShellLinkItem> link = CreateShellLink(); | |
| 441 const sessions::SerializedNavigationEntry& current_navigation = | |
| 442 tab->navigations.at(tab->current_navigation_index); | |
| 443 std::string url = current_navigation.virtual_url().spec(); | |
| 444 link->GetCommandLine()->AppendArgNative(base::UTF8ToWide(url)); | |
| 445 link->GetCommandLine()->AppendSwitchASCII( | |
| 446 switches::kWinJumplistAction, jumplist::kRecentlyClosedCategory); | |
| 447 link->set_title(current_navigation.title()); | |
| 448 list->push_back(link); | |
| 449 { | |
| 450 JumpListData* data = &jumplist_data_->data; | |
| 451 base::AutoLock auto_lock(data->list_lock_); | |
| 452 data->icon_urls_.push_back(std::make_pair(std::move(url), std::move(link))); | |
| 453 } | |
| 454 return true; | |
| 455 } | |
| 456 | |
| 457 void JumpList::AddWindow(const sessions::TabRestoreService::Window* window, | |
| 458 ShellLinkItemList* list, | |
| 459 size_t max_items) { | |
| 460 DCHECK(CalledOnValidThread()); | |
| 461 | |
| 462 // This code enumerates al the tabs in the given window object and add their | |
| 463 // URLs and titles to the list. | |
| 464 DCHECK(!window->tabs.empty()); | |
| 465 | |
| 466 for (size_t i = 0; i < window->tabs.size(); ++i) { | |
| 467 if (!AddTab(&window->tabs[i], list, max_items)) | |
| 468 return; | |
| 469 } | |
| 470 } | |
| 471 | |
| 472 void JumpList::StartLoadingFavicon() { | |
| 473 DCHECK(CalledOnValidThread()); | |
| 474 | |
| 475 GURL url; | |
| 476 bool waiting_for_icons = true; | |
| 477 { | |
| 478 JumpListData* data = &jumplist_data_->data; | |
| 479 base::AutoLock auto_lock(data->list_lock_); | |
| 480 waiting_for_icons = !data->icon_urls_.empty(); | |
| 481 if (waiting_for_icons) { | |
| 482 // Ask FaviconService if it has a favicon of a URL. | |
| 483 // When FaviconService has one, it will call OnFaviconDataAvailable(). | |
| 484 url = GURL(data->icon_urls_.front().first); | |
| 485 } | |
| 486 } | |
| 487 | |
| 488 if (!waiting_for_icons) { | |
| 489 // No more favicons are needed by the application JumpList. Schedule a | |
| 490 // RunUpdateOnFileThread call. | |
| 491 PostRunUpdate(); | |
| 492 return; | |
| 493 } | |
| 494 | |
| 495 favicon::FaviconService* favicon_service = | |
| 496 FaviconServiceFactory::GetForProfile(profile_, | |
| 497 ServiceAccessType::EXPLICIT_ACCESS); | |
| 498 task_id_ = favicon_service->GetFaviconImageForPageURL( | |
| 499 url, | |
| 500 base::Bind(&JumpList::OnFaviconDataAvailable, base::Unretained(this)), | |
| 501 &cancelable_task_tracker_); | |
| 502 } | |
| 503 | |
| 504 void JumpList::OnFaviconDataAvailable( | |
| 505 const favicon_base::FaviconImageResult& image_result) { | |
| 506 DCHECK(CalledOnValidThread()); | |
| 507 | |
| 508 // If there is currently a favicon request in progress, it is now outdated, | |
| 509 // as we have received another, so nullify the handle from the old request. | |
| 510 task_id_ = base::CancelableTaskTracker::kBadTaskId; | |
| 511 // Lock the list to set icon data and pop the url. | |
| 512 { | |
| 513 JumpListData* data = &jumplist_data_->data; | |
| 514 base::AutoLock auto_lock(data->list_lock_); | |
| 515 // Attach the received data to the ShellLinkItem object. | |
| 516 // This data will be decoded by the RunUpdateOnFileThread method. | |
| 517 if (!image_result.image.IsEmpty()) { | |
| 518 if (!data->icon_urls_.empty() && data->icon_urls_.front().second.get()) | |
| 519 data->icon_urls_.front().second->set_icon_data( | |
| 520 image_result.image.AsBitmap()); | |
| 521 } | |
| 522 | |
| 523 if (!data->icon_urls_.empty()) | |
| 524 data->icon_urls_.pop_front(); | |
| 525 } | |
| 526 // Check whether we need to load more favicons. | |
| 527 StartLoadingFavicon(); | |
| 528 } | |
| 529 | |
| 530 void JumpList::OnIncognitoAvailabilityChanged() { | |
| 531 DCHECK(CalledOnValidThread()); | |
| 532 | |
| 533 bool waiting_for_icons = true; | |
| 534 { | |
| 535 JumpListData* data = &jumplist_data_->data; | |
| 536 base::AutoLock auto_lock(data->list_lock_); | |
| 537 waiting_for_icons = !data->icon_urls_.empty(); | |
| 538 } | |
| 539 if (!waiting_for_icons) | |
| 540 PostRunUpdate(); | |
| 541 // If |icon_urls_| isn't empty then OnFaviconDataAvailable will eventually | |
| 542 // call PostRunUpdate(). | |
| 543 } | |
| 544 | |
| 545 void JumpList::PostRunUpdate() { | |
| 546 DCHECK(CalledOnValidThread()); | |
| 547 | |
| 548 TRACE_EVENT0("browser", "JumpList::PostRunUpdate"); | |
| 549 // Initialize the one-shot timer to update the jumplists in a while. | |
| 550 // If there is already a request queued then cancel it and post the new | |
| 551 // request. This ensures that JumpListUpdates won't happen until there has | |
| 552 // been a brief quiet period, thus avoiding update storms. | |
| 553 if (timer_.IsRunning()) { | |
| 554 timer_.Reset(); | |
| 555 } else { | |
| 556 timer_.Start(FROM_HERE, | |
| 557 base::TimeDelta::FromMilliseconds(kDelayForJumplistUpdateInMS), | |
| 558 this, | |
| 559 &JumpList::DeferredRunUpdate); | |
| 560 } | |
| 561 } | |
| 562 | |
| 563 void JumpList::DeferredRunUpdate() { | |
| 564 DCHECK(CalledOnValidThread()); | |
| 565 | |
| 566 TRACE_EVENT0("browser", "JumpList::DeferredRunUpdate"); | |
| 567 // Check if incognito windows (or normal windows) are disabled by policy. | |
| 568 IncognitoModePrefs::Availability incognito_availability = | |
| 569 profile_ ? IncognitoModePrefs::GetAvailability(profile_->GetPrefs()) | |
| 570 : IncognitoModePrefs::ENABLED; | |
| 571 | |
| 572 BrowserThread::PostTask( | |
| 573 BrowserThread::FILE, FROM_HERE, | |
| 574 base::Bind(&RunUpdateOnFileThread, | |
| 575 incognito_availability, | |
| 576 app_id_, | |
| 577 icon_dir_, | |
| 578 base::RetainedRef(jumplist_data_))); | |
| 579 } | |
| 580 | |
| 581 void JumpList::TopSitesLoaded(history::TopSites* top_sites) { | |
| 582 } | |
| 583 | |
| 584 void JumpList::TopSitesChanged(history::TopSites* top_sites, | |
| 585 ChangeReason change_reason) { | |
| 586 top_sites->GetMostVisitedURLs( | |
| 587 base::Bind(&JumpList::OnMostVisitedURLsAvailable, | |
| 588 weak_ptr_factory_.GetWeakPtr()), | |
| 589 false); | |
| 590 } | |
| OLD | NEW |