Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / jumplist_win.cc
blob58cab806c332a9f754da7d4f7fd393c672c92fb5
1 // Copyright (c) 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.
5 #include "chrome/browser/jumplist_win.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/file_util.h"
11 #include "base/path_service.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/thread.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/favicon/favicon_service.h"
17 #include "chrome/browser/favicon/favicon_service_factory.h"
18 #include "chrome/browser/history/history_service.h"
19 #include "chrome/browser/history/page_usage_data.h"
20 #include "chrome/browser/history/top_sites.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/sessions/session_types.h"
23 #include "chrome/browser/sessions/tab_restore_service.h"
24 #include "chrome/browser/sessions/tab_restore_service_factory.h"
25 #include "chrome/browser/shell_integration.h"
26 #include "chrome/common/chrome_constants.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/url_constants.h"
29 #include "components/favicon_base/favicon_types.h"
30 #include "content/public/browser/browser_thread.h"
31 #include "content/public/browser/notification_source.h"
32 #include "grit/chromium_strings.h"
33 #include "grit/generated_resources.h"
34 #include "ui/base/l10n/l10n_util.h"
35 #include "ui/gfx/codec/png_codec.h"
36 #include "ui/gfx/favicon_size.h"
37 #include "ui/gfx/icon_util.h"
38 #include "ui/gfx/image/image_family.h"
39 #include "url/gurl.h"
41 using content::BrowserThread;
43 namespace {
45 // Append the common switches to each shell link.
46 void AppendCommonSwitches(ShellLinkItem* shell_link) {
47 const char* kSwitchNames[] = { switches::kUserDataDir };
48 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
49 shell_link->GetCommandLine()->CopySwitchesFrom(command_line,
50 kSwitchNames,
51 arraysize(kSwitchNames));
54 // Create a ShellLinkItem preloaded with common switches.
55 scoped_refptr<ShellLinkItem> CreateShellLink() {
56 scoped_refptr<ShellLinkItem> link(new ShellLinkItem);
57 AppendCommonSwitches(link.get());
58 return link;
61 // Creates a temporary icon file to be shown in JumpList.
62 bool CreateIconFile(const SkBitmap& bitmap,
63 const base::FilePath& icon_dir,
64 base::FilePath* icon_path) {
65 // Retrieve the path to a temporary file.
66 // We don't have to care about the extension of this temporary file because
67 // JumpList does not care about it.
68 base::FilePath path;
69 if (!base::CreateTemporaryFileInDir(icon_dir, &path))
70 return false;
72 // Create an icon file from the favicon attached to the given |page|, and
73 // save it as the temporary file.
74 gfx::ImageFamily image_family;
75 image_family.Add(gfx::Image::CreateFrom1xBitmap(bitmap));
76 if (!IconUtil::CreateIconFileFromImageFamily(image_family, path))
77 return false;
79 // Add this icon file to the list and return its absolute path.
80 // The IShellLink::SetIcon() function needs the absolute path to an icon.
81 *icon_path = path;
82 return true;
85 // Updates the "Tasks" category of the JumpList.
86 bool UpdateTaskCategory(JumpListUpdater* jumplist_updater) {
87 base::FilePath chrome_path;
88 if (!PathService::Get(base::FILE_EXE, &chrome_path))
89 return false;
91 ShellLinkItemList items;
93 // Create an IShellLink object which launches Chrome, and add it to the
94 // collection. We use our application icon as the icon for this item.
95 // We remove '&' characters from this string so we can share it with our
96 // system menu.
97 scoped_refptr<ShellLinkItem> chrome = CreateShellLink();
98 std::wstring chrome_title =
99 base::UTF16ToWide(l10n_util::GetStringUTF16(IDS_NEW_WINDOW));
100 ReplaceSubstringsAfterOffset(&chrome_title, 0, L"&", L"");
101 chrome->set_title(chrome_title);
102 chrome->set_icon(chrome_path.value(), 0);
103 items.push_back(chrome);
105 // Create an IShellLink object which launches Chrome in incognito mode, and
106 // add it to the collection. We use our application icon as the icon for
107 // this item.
108 scoped_refptr<ShellLinkItem> incognito = CreateShellLink();
109 incognito->GetCommandLine()->AppendSwitch(switches::kIncognito);
110 std::wstring incognito_title =
111 base::UTF16ToWide(l10n_util::GetStringUTF16(IDS_NEW_INCOGNITO_WINDOW));
112 ReplaceSubstringsAfterOffset(&incognito_title, 0, L"&", L"");
113 incognito->set_title(incognito_title);
114 incognito->set_icon(chrome_path.value(), 0);
115 items.push_back(incognito);
117 return jumplist_updater->AddTasks(items);
120 // Updates the application JumpList.
121 bool UpdateJumpList(const wchar_t* app_id,
122 const ShellLinkItemList& most_visited_pages,
123 const ShellLinkItemList& recently_closed_pages) {
124 // JumpList is implemented only on Windows 7 or later.
125 // So, we should return now when this function is called on earlier versions
126 // of Windows.
127 if (!JumpListUpdater::IsEnabled())
128 return true;
130 JumpListUpdater jumplist_updater(app_id);
131 if (!jumplist_updater.BeginUpdate())
132 return false;
134 // We allocate 60% of the given JumpList slots to "most-visited" items
135 // and 40% to "recently-closed" items, respectively.
136 // Nevertheless, if there are not so many items in |recently_closed_pages|,
137 // we give the remaining slots to "most-visited" items.
138 const int kMostVisited = 60;
139 const int kRecentlyClosed = 40;
140 const int kTotal = kMostVisited + kRecentlyClosed;
141 size_t most_visited_items =
142 MulDiv(jumplist_updater.user_max_items(), kMostVisited, kTotal);
143 size_t recently_closed_items =
144 jumplist_updater.user_max_items() - most_visited_items;
145 if (recently_closed_pages.size() < recently_closed_items) {
146 most_visited_items += recently_closed_items - recently_closed_pages.size();
147 recently_closed_items = recently_closed_pages.size();
150 // Update the "Most Visited" category of the JumpList.
151 // This update request is applied into the JumpList when we commit this
152 // transaction.
153 if (!jumplist_updater.AddCustomCategory(
154 base::UTF16ToWide(
155 l10n_util::GetStringUTF16(IDS_NEW_TAB_MOST_VISITED)),
156 most_visited_pages, most_visited_items)) {
157 return false;
160 // Update the "Recently Closed" category of the JumpList.
161 if (!jumplist_updater.AddCustomCategory(
162 base::UTF16ToWide(
163 l10n_util::GetStringUTF16(IDS_NEW_TAB_RECENTLY_CLOSED)),
164 recently_closed_pages, recently_closed_items)) {
165 return false;
168 // Update the "Tasks" category of the JumpList.
169 if (!UpdateTaskCategory(&jumplist_updater))
170 return false;
172 // Commit this transaction and send the updated JumpList to Windows.
173 if (!jumplist_updater.CommitUpdate())
174 return false;
176 return true;
179 } // namespace
181 JumpList::JumpList()
182 : weak_ptr_factory_(this),
183 profile_(NULL),
184 task_id_(base::CancelableTaskTracker::kBadTaskId) {}
186 JumpList::~JumpList() {
187 Terminate();
190 // static
191 bool JumpList::Enabled() {
192 return JumpListUpdater::IsEnabled();
195 bool JumpList::AddObserver(Profile* profile) {
196 // To update JumpList when a tab is added or removed, we add this object to
197 // the observer list of the TabRestoreService class.
198 // When we add this object to the observer list, we save the pointer to this
199 // TabRestoreService object. This pointer is used when we remove this object
200 // from the observer list.
201 if (!JumpListUpdater::IsEnabled() || !profile)
202 return false;
204 TabRestoreService* tab_restore_service =
205 TabRestoreServiceFactory::GetForProfile(profile);
206 if (!tab_restore_service)
207 return false;
209 app_id_ = ShellIntegration::GetChromiumModelIdForProfile(profile->GetPath());
210 icon_dir_ = profile->GetPath().Append(chrome::kJumpListIconDirname);
211 profile_ = profile;
212 history::TopSites* top_sites = profile_->GetTopSites();
213 if (top_sites) {
214 // TopSites updates itself after a delay. This is especially noticable when
215 // your profile is empty. Ask TopSites to update itself when jumplist is
216 // initialized.
217 top_sites->SyncWithHistory();
218 registrar_.reset(new content::NotificationRegistrar);
219 // Register for notification when TopSites changes so that we can update
220 // ourself.
221 registrar_->Add(this, chrome::NOTIFICATION_TOP_SITES_CHANGED,
222 content::Source<history::TopSites>(top_sites));
223 // Register for notification when profile is destroyed to ensure that all
224 // observers are detatched at that time.
225 registrar_->Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
226 content::Source<Profile>(profile_));
228 tab_restore_service->AddObserver(this);
229 return true;
232 void JumpList::Observe(int type,
233 const content::NotificationSource& source,
234 const content::NotificationDetails& details) {
235 switch (type) {
236 case chrome::NOTIFICATION_TOP_SITES_CHANGED: {
237 // Most visited urls changed, query again.
238 history::TopSites* top_sites = profile_->GetTopSites();
239 if (top_sites) {
240 top_sites->GetMostVisitedURLs(
241 base::Bind(&JumpList::OnMostVisitedURLsAvailable,
242 weak_ptr_factory_.GetWeakPtr()), false);
244 break;
246 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
247 // Profile was destroyed, do clean-up.
248 Terminate();
249 break;
251 default:
252 NOTREACHED() << "Unexpected notification type.";
256 void JumpList::RemoveObserver() {
257 if (profile_) {
258 TabRestoreService* tab_restore_service =
259 TabRestoreServiceFactory::GetForProfile(profile_);
260 if (tab_restore_service)
261 tab_restore_service->RemoveObserver(this);
262 registrar_.reset();
264 profile_ = NULL;
267 void JumpList::CancelPendingUpdate() {
268 if (task_id_ != base::CancelableTaskTracker::kBadTaskId) {
269 cancelable_task_tracker_.TryCancel(task_id_);
270 task_id_ = base::CancelableTaskTracker::kBadTaskId;
274 void JumpList::Terminate() {
275 CancelPendingUpdate();
276 RemoveObserver();
279 void JumpList::OnMostVisitedURLsAvailable(
280 const history::MostVisitedURLList& data) {
282 // If we have a pending favicon request, cancel it here (it is out of date).
283 CancelPendingUpdate();
286 base::AutoLock auto_lock(list_lock_);
287 most_visited_pages_.clear();
288 for (size_t i = 0; i < data.size(); i++) {
289 const history::MostVisitedURL& url = data[i];
290 scoped_refptr<ShellLinkItem> link = CreateShellLink();
291 std::string url_string = url.url.spec();
292 std::wstring url_string_wide = base::UTF8ToWide(url_string);
293 link->GetCommandLine()->AppendArgNative(url_string_wide);
294 link->set_title(!url.title.empty()? url.title : url_string_wide);
295 most_visited_pages_.push_back(link);
296 icon_urls_.push_back(make_pair(url_string, link));
300 // Send a query that retrieves the first favicon.
301 StartLoadingFavicon();
304 void JumpList::TabRestoreServiceChanged(TabRestoreService* service) {
305 // if we have a pending handle request, cancel it here (it is out of date).
306 CancelPendingUpdate();
308 // local list to pass to methods
309 ShellLinkItemList temp_list;
311 // Create a list of ShellLinkItems from the "Recently Closed" pages.
312 // As noted above, we create a ShellLinkItem objects with the following
313 // parameters.
314 // * arguments
315 // The last URL of the tab object.
316 // * title
317 // The title of the last URL.
318 // * icon
319 // An empty string. This value is to be updated in OnFaviconDataAvailable().
320 // This code is copied from
321 // RecentlyClosedTabsHandler::TabRestoreServiceChanged() to emulate it.
322 const int kRecentlyClosedCount = 4;
323 TabRestoreService* tab_restore_service =
324 TabRestoreServiceFactory::GetForProfile(profile_);
325 const TabRestoreService::Entries& entries = tab_restore_service->entries();
326 for (TabRestoreService::Entries::const_iterator it = entries.begin();
327 it != entries.end(); ++it) {
328 const TabRestoreService::Entry* entry = *it;
329 if (entry->type == TabRestoreService::TAB) {
330 AddTab(static_cast<const TabRestoreService::Tab*>(entry),
331 &temp_list, kRecentlyClosedCount);
332 } else if (entry->type == TabRestoreService::WINDOW) {
333 AddWindow(static_cast<const TabRestoreService::Window*>(entry),
334 &temp_list, kRecentlyClosedCount);
337 // Lock recently_closed_pages and copy temp_list into it.
339 base::AutoLock auto_lock(list_lock_);
340 recently_closed_pages_ = temp_list;
343 // Send a query that retrieves the first favicon.
344 StartLoadingFavicon();
347 void JumpList::TabRestoreServiceDestroyed(TabRestoreService* service) {
350 bool JumpList::AddTab(const TabRestoreService::Tab* tab,
351 ShellLinkItemList* list,
352 size_t max_items) {
353 // This code adds the URL and the title strings of the given tab to the
354 // specified list.
355 if (list->size() >= max_items)
356 return false;
358 scoped_refptr<ShellLinkItem> link = CreateShellLink();
359 const sessions::SerializedNavigationEntry& current_navigation =
360 tab->navigations.at(tab->current_navigation_index);
361 std::string url = current_navigation.virtual_url().spec();
362 link->GetCommandLine()->AppendArgNative(base::UTF8ToWide(url));
363 link->set_title(current_navigation.title());
364 list->push_back(link);
365 icon_urls_.push_back(make_pair(url, link));
366 return true;
369 void JumpList::AddWindow(const TabRestoreService::Window* window,
370 ShellLinkItemList* list,
371 size_t max_items) {
372 // This code enumerates al the tabs in the given window object and add their
373 // URLs and titles to the list.
374 DCHECK(!window->tabs.empty());
376 for (size_t i = 0; i < window->tabs.size(); ++i) {
377 if (!AddTab(&window->tabs[i], list, max_items))
378 return;
382 void JumpList::StartLoadingFavicon() {
383 GURL url;
385 base::AutoLock auto_lock(list_lock_);
386 if (icon_urls_.empty()) {
387 // No more favicons are needed by the application JumpList. Schedule a
388 // RunUpdate call.
389 BrowserThread::PostTask(
390 BrowserThread::FILE, FROM_HERE,
391 base::Bind(&JumpList::RunUpdate, this));
392 return;
394 // Ask FaviconService if it has a favicon of a URL.
395 // When FaviconService has one, it will call OnFaviconDataAvailable().
396 url = GURL(icon_urls_.front().first);
398 FaviconService* favicon_service =
399 FaviconServiceFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS);
400 task_id_ = favicon_service->GetFaviconImageForURL(
401 FaviconService::FaviconForURLParams(
402 url, favicon_base::FAVICON, gfx::kFaviconSize),
403 base::Bind(&JumpList::OnFaviconDataAvailable, base::Unretained(this)),
404 &cancelable_task_tracker_);
407 void JumpList::OnFaviconDataAvailable(
408 const favicon_base::FaviconImageResult& image_result) {
409 // If there is currently a favicon request in progress, it is now outdated,
410 // as we have received another, so nullify the handle from the old request.
411 task_id_ = base::CancelableTaskTracker::kBadTaskId;
412 // lock the list to set icon data and pop the url
414 base::AutoLock auto_lock(list_lock_);
415 // Attach the received data to the ShellLinkItem object.
416 // This data will be decoded by the RunUpdate method.
417 if (!image_result.image.IsEmpty()) {
418 if (!icon_urls_.empty() && icon_urls_.front().second)
419 icon_urls_.front().second->set_icon_data(image_result.image.AsBitmap());
422 if (!icon_urls_.empty())
423 icon_urls_.pop_front();
425 // Check whether we need to load more favicons.
426 StartLoadingFavicon();
429 void JumpList::RunUpdate() {
430 ShellLinkItemList local_most_visited_pages;
431 ShellLinkItemList local_recently_closed_pages;
434 base::AutoLock auto_lock(list_lock_);
435 // Make sure we are not out of date: if icon_urls_ is not empty, then
436 // another notification has been received since we processed this one
437 if (!icon_urls_.empty())
438 return;
440 // Make local copies of lists so we can release the lock.
441 local_most_visited_pages = most_visited_pages_;
442 local_recently_closed_pages = recently_closed_pages_;
445 // Delete the directory which contains old icon files, rename the current
446 // icon directory, and create a new directory which contains new JumpList
447 // icon files.
448 base::FilePath icon_dir_old(icon_dir_.value() + L"Old");
449 if (base::PathExists(icon_dir_old))
450 base::DeleteFile(icon_dir_old, true);
451 base::Move(icon_dir_, icon_dir_old);
452 base::CreateDirectory(icon_dir_);
454 // Create temporary icon files for shortcuts in the "Most Visited" category.
455 CreateIconFiles(local_most_visited_pages);
457 // Create temporary icon files for shortcuts in the "Recently Closed"
458 // category.
459 CreateIconFiles(local_recently_closed_pages);
461 // We finished collecting all resources needed for updating an appliation
462 // JumpList. So, create a new JumpList and replace the current JumpList
463 // with it.
464 UpdateJumpList(app_id_.c_str(), local_most_visited_pages,
465 local_recently_closed_pages);
468 void JumpList::CreateIconFiles(const ShellLinkItemList& item_list) {
469 for (ShellLinkItemList::const_iterator item = item_list.begin();
470 item != item_list.end(); ++item) {
471 base::FilePath icon_path;
472 if (CreateIconFile((*item)->icon_data(), icon_dir_, &icon_path))
473 (*item)->set_icon(icon_path.value(), 0);