| 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 <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
| 11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 12 #include "base/files/file_util.h" | 12 #include "base/files/file_util.h" |
| 13 #include "base/macros.h" | 13 #include "base/macros.h" |
| 14 #include "base/metrics/histogram_macros.h" | 14 #include "base/metrics/histogram_macros.h" |
| 15 #include "base/path_service.h" | 15 #include "base/path_service.h" |
| 16 #include "base/sequenced_task_runner.h" | 16 #include "base/sequenced_task_runner.h" |
| 17 #include "base/single_thread_task_runner.h" | 17 #include "base/single_thread_task_runner.h" |
| 18 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
| 19 #include "base/strings/utf_string_conversions.h" | 19 #include "base/strings/utf_string_conversions.h" |
| 20 #include "base/task_scheduler/post_task.h" | 20 #include "base/task_scheduler/post_task.h" |
| 21 #include "base/threading/thread.h" | 21 #include "base/threading/thread.h" |
| 22 #include "base/threading/thread_restrictions.h" | 22 #include "base/threading/thread_restrictions.h" |
| 23 #include "base/timer/elapsed_timer.h" |
| 23 #include "base/trace_event/trace_event.h" | 24 #include "base/trace_event/trace_event.h" |
| 24 #include "chrome/browser/chrome_notification_types.h" | 25 #include "chrome/browser/chrome_notification_types.h" |
| 25 #include "chrome/browser/favicon/favicon_service_factory.h" | 26 #include "chrome/browser/favicon/favicon_service_factory.h" |
| 26 #include "chrome/browser/history/top_sites_factory.h" | 27 #include "chrome/browser/history/top_sites_factory.h" |
| 27 #include "chrome/browser/metrics/jumplist_metrics_win.h" | 28 #include "chrome/browser/metrics/jumplist_metrics_win.h" |
| 28 #include "chrome/browser/profiles/profile.h" | 29 #include "chrome/browser/profiles/profile.h" |
| 29 #include "chrome/browser/sessions/tab_restore_service_factory.h" | 30 #include "chrome/browser/sessions/tab_restore_service_factory.h" |
| 30 #include "chrome/browser/shell_integration_win.h" | 31 #include "chrome/browser/shell_integration_win.h" |
| 31 #include "chrome/browser/win/jumplist_file_util.h" | 32 #include "chrome/browser/win/jumplist_file_util.h" |
| 32 #include "chrome/common/chrome_constants.h" | 33 #include "chrome/common/chrome_constants.h" |
| (...skipping 24 matching lines...) Expand all Loading... |
| 57 #include "ui/gfx/image/image_skia.h" | 58 #include "ui/gfx/image/image_skia.h" |
| 58 #include "ui/gfx/image/image_skia_rep.h" | 59 #include "ui/gfx/image/image_skia_rep.h" |
| 59 #include "url/gurl.h" | 60 #include "url/gurl.h" |
| 60 | 61 |
| 61 using content::BrowserThread; | 62 using content::BrowserThread; |
| 62 using JumpListData = JumpList::JumpListData; | 63 using JumpListData = JumpList::JumpListData; |
| 63 | 64 |
| 64 namespace { | 65 namespace { |
| 65 | 66 |
| 66 // Delay jumplist updates to allow collapsing of redundant update requests. | 67 // Delay jumplist updates to allow collapsing of redundant update requests. |
| 67 const int kDelayForJumplistUpdateInMS = 3500; | 68 constexpr base::TimeDelta kDelayForJumplistUpdate = |
| 69 base::TimeDelta::FromMilliseconds(3500); |
| 70 |
| 71 // Timeout jumplist updates for users with extremely slow OS. |
| 72 constexpr base::TimeDelta kTimeOutForJumplistUpdate = |
| 73 base::TimeDelta::FromMilliseconds(500); |
| 68 | 74 |
| 69 // Append the common switches to each shell link. | 75 // Append the common switches to each shell link. |
| 70 void AppendCommonSwitches(ShellLinkItem* shell_link) { | 76 void AppendCommonSwitches(ShellLinkItem* shell_link) { |
| 71 const char* kSwitchNames[] = { switches::kUserDataDir }; | 77 const char* kSwitchNames[] = { switches::kUserDataDir }; |
| 72 const base::CommandLine& command_line = | 78 const base::CommandLine& command_line = |
| 73 *base::CommandLine::ForCurrentProcess(); | 79 *base::CommandLine::ForCurrentProcess(); |
| 74 shell_link->GetCommandLine()->CopySwitchesFrom(command_line, | 80 shell_link->GetCommandLine()->CopySwitchesFrom(command_line, |
| 75 kSwitchNames, | 81 kSwitchNames, |
| 76 arraysize(kSwitchNames)); | 82 arraysize(kSwitchNames)); |
| 77 } | 83 } |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 169 base::ReplaceSubstringsAfterOffset( | 175 base::ReplaceSubstringsAfterOffset( |
| 170 &incognito_title, 0, L"&", base::StringPiece16()); | 176 &incognito_title, 0, L"&", base::StringPiece16()); |
| 171 incognito->set_title(incognito_title); | 177 incognito->set_title(incognito_title); |
| 172 incognito->set_icon(chrome_path.value(), icon_resources::kIncognitoIndex); | 178 incognito->set_icon(chrome_path.value(), icon_resources::kIncognitoIndex); |
| 173 items.push_back(incognito); | 179 items.push_back(incognito); |
| 174 } | 180 } |
| 175 | 181 |
| 176 return jumplist_updater->AddTasks(items); | 182 return jumplist_updater->AddTasks(items); |
| 177 } | 183 } |
| 178 | 184 |
| 179 // Updates the application JumpList. | 185 // Updates the application JumpList, which consists of 1) delete old icon files; |
| 180 bool UpdateJumpList(const wchar_t* app_id, | 186 // 2) create new icon files; 3) notify the OS. |
| 187 // Note that any timeout error along the way results in the old jumplist being |
| 188 // left as-is, while any non-timeout error results in the old jumplist being |
| 189 // left as-is, but without icon files. |
| 190 void UpdateJumpList(const wchar_t* app_id, |
| 181 const base::FilePath& icon_dir, | 191 const base::FilePath& icon_dir, |
| 182 const ShellLinkItemList& most_visited_pages, | 192 const ShellLinkItemList& most_visited_pages, |
| 183 const ShellLinkItemList& recently_closed_pages, | 193 const ShellLinkItemList& recently_closed_pages, |
| 184 IncognitoModePrefs::Availability incognito_availability) { | 194 IncognitoModePrefs::Availability incognito_availability) { |
| 185 // JumpList is implemented only on Windows 7 or later. | |
| 186 // So, we should return now when this function is called on earlier versions | |
| 187 // of Windows. | |
| 188 if (!JumpListUpdater::IsEnabled()) | 195 if (!JumpListUpdater::IsEnabled()) |
| 189 return true; | 196 return; |
| 197 |
| 198 // Records the time cost of starting a JumpListUpdater. |
| 199 base::ElapsedTimer begin_update_timer; |
| 190 | 200 |
| 191 JumpListUpdater jumplist_updater(app_id); | 201 JumpListUpdater jumplist_updater(app_id); |
| 192 if (!jumplist_updater.BeginUpdate()) | 202 if (!jumplist_updater.BeginUpdate()) |
| 193 return false; | 203 return; |
| 204 |
| 205 // Stops jumplist update if JumpListUpdater's start times out, as it's very |
| 206 // likely the following update steps will also take a long time. |
| 207 if (begin_update_timer.Elapsed() >= kTimeOutForJumplistUpdate) |
| 208 return; |
| 194 | 209 |
| 195 // We allocate 60% of the given JumpList slots to "most-visited" items | 210 // We allocate 60% of the given JumpList slots to "most-visited" items |
| 196 // and 40% to "recently-closed" items, respectively. | 211 // and 40% to "recently-closed" items, respectively. |
| 197 // Nevertheless, if there are not so many items in |recently_closed_pages|, | 212 // Nevertheless, if there are not so many items in |recently_closed_pages|, |
| 198 // we give the remaining slots to "most-visited" items. | 213 // we give the remaining slots to "most-visited" items. |
| 199 // The default maximum number of items to display in JumpList is 10. | 214 // The default maximum number of items to display in JumpList is 10. |
| 200 // https://msdn.microsoft.com/en-us/library/windows/desktop/dd378398(v=vs.85).
aspx | 215 // https://msdn.microsoft.com/en-us/library/windows/desktop/dd378398(v=vs.85).
aspx |
| 201 const int kMostVisited = 60; | 216 const int kMostVisited = 60; |
| 202 const int kRecentlyClosed = 40; | 217 const int kRecentlyClosed = 40; |
| 203 const int kTotal = kMostVisited + kRecentlyClosed; | 218 const int kTotal = kMostVisited + kRecentlyClosed; |
| 204 size_t most_visited_items = | 219 size_t most_visited_items = |
| 205 MulDiv(jumplist_updater.user_max_items(), kMostVisited, kTotal); | 220 MulDiv(jumplist_updater.user_max_items(), kMostVisited, kTotal); |
| 206 size_t recently_closed_items = | 221 size_t recently_closed_items = |
| 207 jumplist_updater.user_max_items() - most_visited_items; | 222 jumplist_updater.user_max_items() - most_visited_items; |
| 208 if (recently_closed_pages.size() < recently_closed_items) { | 223 if (recently_closed_pages.size() < recently_closed_items) { |
| 209 most_visited_items += recently_closed_items - recently_closed_pages.size(); | 224 most_visited_items += recently_closed_items - recently_closed_pages.size(); |
| 210 recently_closed_items = recently_closed_pages.size(); | 225 recently_closed_items = recently_closed_pages.size(); |
| 211 } | 226 } |
| 212 | 227 |
| 213 // Delete the content in JumpListIcons folder and log the results to UMA. | 228 // Delete the content in JumpListIcons folder and log the results to UMA. |
| 214 DeleteDirectoryContentAndLogResults(icon_dir, kFileDeleteLimit); | 229 DeleteDirectoryContentAndLogResults(icon_dir, kFileDeleteLimit); |
| 215 | 230 |
| 216 // If JumpListIcons directory doesn't exist (we have tried to create it | 231 // If JumpListIcons directory doesn't exist (we have tried to create it |
| 217 // already) or is not empty, skip updating the jumplist icons. The jumplist | 232 // already) or is not empty, skip updating the jumplist icons. The jumplist |
| 218 // links should be updated anyway, as it doesn't involve disk IO. In this | 233 // links should be updated anyway, as it doesn't involve disk IO. In this |
| 219 // case, Chrome's icon will be used for the new links. | 234 // case, Chrome's icon will be used for the new links. |
| 220 | 235 |
| 221 if (base::DirectoryExists(icon_dir) && base::IsDirectoryEmpty(icon_dir)) { | 236 bool should_create_icons = |
| 222 // TODO(chengx): Remove this UMA metric after fixing http://crbug.com/40407. | 237 base::DirectoryExists(icon_dir) && base::IsDirectoryEmpty(icon_dir); |
| 223 UMA_HISTOGRAM_COUNTS_100( | |
| 224 "WinJumplist.CreateIconFilesCount", | |
| 225 most_visited_pages.size() + recently_closed_pages.size()); | |
| 226 | 238 |
| 227 // Create icon files for shortcuts in the "Most Visited" category. | 239 // Create icon files for shortcuts in the "Most Visited" category. |
| 240 if (should_create_icons) |
| 228 CreateIconFiles(icon_dir, most_visited_pages, most_visited_items); | 241 CreateIconFiles(icon_dir, most_visited_pages, most_visited_items); |
| 229 | 242 |
| 230 // Create icon files for shortcuts in the "Recently Closed" category. | |
| 231 CreateIconFiles(icon_dir, recently_closed_pages, recently_closed_items); | |
| 232 } | |
| 233 | |
| 234 // TODO(chengx): Remove the UMA histogram after fixing http://crbug.com/40407. | |
| 235 SCOPED_UMA_HISTOGRAM_TIMER("WinJumplist.UpdateJumpListDuration"); | |
| 236 | |
| 237 // Update the "Most Visited" category of the JumpList if it exists. | 243 // Update the "Most Visited" category of the JumpList if it exists. |
| 238 // This update request is applied into the JumpList when we commit this | 244 // This update request is applied into the JumpList when we commit this |
| 239 // transaction. | 245 // transaction. |
| 240 if (!jumplist_updater.AddCustomCategory( | 246 if (!jumplist_updater.AddCustomCategory( |
| 241 l10n_util::GetStringUTF16(IDS_NEW_TAB_MOST_VISITED), | 247 l10n_util::GetStringUTF16(IDS_NEW_TAB_MOST_VISITED), |
| 242 most_visited_pages, most_visited_items)) { | 248 most_visited_pages, most_visited_items)) { |
| 243 return false; | 249 return; |
| 244 } | 250 } |
| 245 | 251 |
| 252 // Create icon files for shortcuts in the "Recently Closed" category. |
| 253 if (should_create_icons) |
| 254 CreateIconFiles(icon_dir, recently_closed_pages, recently_closed_items); |
| 255 |
| 246 // Update the "Recently Closed" category of the JumpList. | 256 // Update the "Recently Closed" category of the JumpList. |
| 247 if (!jumplist_updater.AddCustomCategory( | 257 if (!jumplist_updater.AddCustomCategory( |
| 248 l10n_util::GetStringUTF16(IDS_RECENTLY_CLOSED), | 258 l10n_util::GetStringUTF16(IDS_RECENTLY_CLOSED), |
| 249 recently_closed_pages, recently_closed_items)) { | 259 recently_closed_pages, recently_closed_items)) { |
| 250 return false; | 260 return; |
| 251 } | 261 } |
| 252 | 262 |
| 253 // Update the "Tasks" category of the JumpList. | 263 // Update the "Tasks" category of the JumpList. |
| 254 if (!UpdateTaskCategory(&jumplist_updater, incognito_availability)) | 264 if (!UpdateTaskCategory(&jumplist_updater, incognito_availability)) |
| 255 return false; | 265 return; |
| 256 | 266 |
| 257 // Commit this transaction and send the updated JumpList to Windows. | 267 // Commit this transaction and send the updated JumpList to Windows. |
| 258 if (!jumplist_updater.CommitUpdate()) | 268 jumplist_updater.CommitUpdate(); |
| 259 return false; | |
| 260 | |
| 261 return true; | |
| 262 } | 269 } |
| 263 | 270 |
| 264 // Updates the jumplist, once all the data has been fetched. | 271 // Updates the jumplist, once all the data has been fetched. |
| 265 void RunUpdateJumpList(IncognitoModePrefs::Availability incognito_availability, | 272 void RunUpdateJumpList(IncognitoModePrefs::Availability incognito_availability, |
| 266 const std::wstring& app_id, | 273 const std::wstring& app_id, |
| 267 const base::FilePath& icon_dir, | 274 const base::FilePath& icon_dir, |
| 268 base::RefCountedData<JumpListData>* ref_counted_data) { | 275 base::RefCountedData<JumpListData>* ref_counted_data) { |
| 269 JumpListData* data = &ref_counted_data->data; | 276 JumpListData* data = &ref_counted_data->data; |
| 270 ShellLinkItemList local_most_visited_pages; | 277 ShellLinkItemList local_most_visited_pages; |
| 271 ShellLinkItemList local_recently_closed_pages; | 278 ShellLinkItemList local_recently_closed_pages; |
| 272 | 279 |
| 273 { | 280 { |
| 274 base::AutoLock auto_lock(data->list_lock_); | 281 base::AutoLock auto_lock(data->list_lock_); |
| 275 // Make sure we are not out of date: if icon_urls_ is not empty, then | 282 // Make sure we are not out of date: if icon_urls_ is not empty, then |
| 276 // another notification has been received since we processed this one | 283 // another notification has been received since we processed this one |
| 277 if (!data->icon_urls_.empty()) | 284 if (!data->icon_urls_.empty()) |
| 278 return; | 285 return; |
| 279 | 286 |
| 280 // Make local copies of lists so we can release the lock. | 287 // Make local copies of lists so we can release the lock. |
| 281 local_most_visited_pages = data->most_visited_pages_; | 288 local_most_visited_pages = data->most_visited_pages_; |
| 282 local_recently_closed_pages = data->recently_closed_pages_; | 289 local_recently_closed_pages = data->recently_closed_pages_; |
| 283 } | 290 } |
| 284 | 291 |
| 285 // Create a new JumpList and replace the current JumpList with it. The | 292 // Updates the application JumpList. |
| 286 // jumplist links are updated anyway, while the jumplist icons may not as | |
| 287 // mentioned above. | |
| 288 UpdateJumpList(app_id.c_str(), icon_dir, local_most_visited_pages, | 293 UpdateJumpList(app_id.c_str(), icon_dir, local_most_visited_pages, |
| 289 local_recently_closed_pages, incognito_availability); | 294 local_recently_closed_pages, incognito_availability); |
| 290 } | 295 } |
| 291 | 296 |
| 292 } // namespace | 297 } // namespace |
| 293 | 298 |
| 294 JumpList::JumpListData::JumpListData() {} | 299 JumpList::JumpListData::JumpListData() {} |
| 295 | 300 |
| 296 JumpList::JumpListData::~JumpListData() {} | 301 JumpList::JumpListData::~JumpListData() {} |
| 297 | 302 |
| (...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 588 DCHECK(CalledOnValidThread()); | 593 DCHECK(CalledOnValidThread()); |
| 589 | 594 |
| 590 TRACE_EVENT0("browser", "JumpList::PostRunUpdate"); | 595 TRACE_EVENT0("browser", "JumpList::PostRunUpdate"); |
| 591 // Initialize the one-shot timer to update the jumplists in a while. | 596 // Initialize the one-shot timer to update the jumplists in a while. |
| 592 // If there is already a request queued then cancel it and post the new | 597 // If there is already a request queued then cancel it and post the new |
| 593 // request. This ensures that JumpListUpdates won't happen until there has | 598 // request. This ensures that JumpListUpdates won't happen until there has |
| 594 // been a brief quiet period, thus avoiding update storms. | 599 // been a brief quiet period, thus avoiding update storms. |
| 595 if (timer_.IsRunning()) { | 600 if (timer_.IsRunning()) { |
| 596 timer_.Reset(); | 601 timer_.Reset(); |
| 597 } else { | 602 } else { |
| 598 timer_.Start(FROM_HERE, | 603 timer_.Start(FROM_HERE, kDelayForJumplistUpdate, this, |
| 599 base::TimeDelta::FromMilliseconds(kDelayForJumplistUpdateInMS), | |
| 600 this, | |
| 601 &JumpList::DeferredRunUpdate); | 604 &JumpList::DeferredRunUpdate); |
| 602 } | 605 } |
| 603 } | 606 } |
| 604 | 607 |
| 605 void JumpList::DeferredRunUpdate() { | 608 void JumpList::DeferredRunUpdate() { |
| 606 DCHECK(CalledOnValidThread()); | 609 DCHECK(CalledOnValidThread()); |
| 607 | 610 |
| 608 TRACE_EVENT0("browser", "JumpList::DeferredRunUpdate"); | 611 TRACE_EVENT0("browser", "JumpList::DeferredRunUpdate"); |
| 609 // Check if incognito windows (or normal windows) are disabled by policy. | 612 // Check if incognito windows (or normal windows) are disabled by policy. |
| 610 IncognitoModePrefs::Availability incognito_availability = | 613 IncognitoModePrefs::Availability incognito_availability = |
| (...skipping 18 matching lines...) Expand all Loading... |
| 629 void JumpList::TopSitesLoaded(history::TopSites* top_sites) { | 632 void JumpList::TopSitesLoaded(history::TopSites* top_sites) { |
| 630 } | 633 } |
| 631 | 634 |
| 632 void JumpList::TopSitesChanged(history::TopSites* top_sites, | 635 void JumpList::TopSitesChanged(history::TopSites* top_sites, |
| 633 ChangeReason change_reason) { | 636 ChangeReason change_reason) { |
| 634 top_sites->GetMostVisitedURLs( | 637 top_sites->GetMostVisitedURLs( |
| 635 base::Bind(&JumpList::OnMostVisitedURLsAvailable, | 638 base::Bind(&JumpList::OnMostVisitedURLsAvailable, |
| 636 weak_ptr_factory_.GetWeakPtr()), | 639 weak_ptr_factory_.GetWeakPtr()), |
| 637 false); | 640 false); |
| 638 } | 641 } |
| OLD | NEW |