1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Use of this source code is governed by a BSD-style license that can be
3ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// found in the LICENSE file.
4ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
5ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/web_applications/web_app_ui.h"
6ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
7ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/file_util.h"
8ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/path_service.h"
9ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/task.h"
10ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/win/windows_version.h"
11ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/extensions/extension_tab_helper.h"
12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/profiles/profile.h"
13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/web_applications/web_app.h"
14ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/common/chrome_paths.h"
16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/browser/browser_thread.h"
17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/browser/tab_contents/tab_contents.h"
18ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_registrar.h"
19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#if defined(OS_LINUX)
21ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/environment.h"
22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif  // defined(OS_LINUX)
23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
24ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#if defined(OS_WIN)
25ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_details.h"
26ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_source.h"
27ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif  // defined(OS_WIN)
28ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
29ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsennamespace {
30ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#if defined(OS_WIN)
32ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// UpdateShortcutWorker holds all context data needed for update shortcut.
33ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// It schedules a pre-update check to find all shortcuts that needs to be
34ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// updated. If there are such shortcuts, it schedules icon download and
35ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// update them when icons are downloaded. It observes TAB_CLOSING notification
36ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// and cancels all the work when the underlying tab is closing.
37ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenclass UpdateShortcutWorker : public NotificationObserver {
38ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen public:
39ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  explicit UpdateShortcutWorker(TabContentsWrapper* tab_contents);
40ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
41ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  void Run();
42ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
43ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen private:
44ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Overridden from NotificationObserver:
45ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual void Observe(NotificationType type,
46ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                       const NotificationSource& source,
47ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                       const NotificationDetails& details);
48ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
49ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Downloads icon via TabContents.
50ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  void DownloadIcon();
51ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
52ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Callback when icon downloaded.
53ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  void OnIconDownloaded(int download_id, bool errored, const SkBitmap& image);
54ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
55ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Checks if shortcuts exists on desktop, start menu and quick launch.
56ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  void CheckExistingShortcuts();
57ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
58ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Update shortcut files and icons.
59ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  void UpdateShortcuts();
60ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  void UpdateShortcutsOnFileThread();
61ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
62ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Callback after shortcuts are updated.
63ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  void OnShortcutsUpdated(bool);
64ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
65ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Deletes the worker on UI thread where it gets created.
66ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  void DeleteMe();
67ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  void DeleteMeOnUIThread();
68ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
69ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  NotificationRegistrar registrar_;
70ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
71ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Underlying TabContentsWrapper whose shortcuts will be updated.
72ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  TabContentsWrapper* tab_contents_;
73ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
74ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Icons info from tab_contents_'s web app data.
75ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  web_app::IconInfoList unprocessed_icons_;
76ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
77ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Cached shortcut data from the tab_contents_.
78ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ShellIntegration::ShortcutInfo shortcut_info_;
79ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
80ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Our copy of profile path.
81ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath profile_path_;
82ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
83ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // File name of shortcut/ico file based on app title.
84ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath file_name_;
85ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Existing shortcuts.
87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  std::vector<FilePath> shortcut_files_;
88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DISALLOW_COPY_AND_ASSIGN(UpdateShortcutWorker);
90ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
91ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
92ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenUpdateShortcutWorker::UpdateShortcutWorker(TabContentsWrapper* tab_contents)
93ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    : tab_contents_(tab_contents),
94ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      profile_path_(tab_contents->profile()->GetPath()) {
95ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  web_app::GetShortcutInfoForTab(tab_contents_, &shortcut_info_);
96ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  web_app::GetIconsInfo(tab_contents_->extension_tab_helper()->web_app_info(),
97ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                        &unprocessed_icons_);
98ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  file_name_ = web_app::internals::GetSanitizedFileName(shortcut_info_.title);
99ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
100ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  registrar_.Add(this, NotificationType::TAB_CLOSING,
101ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                 Source<NavigationController>(&tab_contents_->controller()));
102ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
103ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
104ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid UpdateShortcutWorker::Run() {
105ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Starting by downloading app icon.
106ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DownloadIcon();
107ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
108ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid UpdateShortcutWorker::Observe(NotificationType type,
110ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                   const NotificationSource& source,
111ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                   const NotificationDetails& details) {
112ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (type == NotificationType::TAB_CLOSING &&
113ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      Source<NavigationController>(source).ptr() ==
114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        &tab_contents_->controller()) {
115ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // Underlying tab is closing.
116ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    tab_contents_ = NULL;
117ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
118ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
119ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
120ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid UpdateShortcutWorker::DownloadIcon() {
121ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // FetchIcon must run on UI thread because it relies on TabContents
122ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // to download the icon.
123ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
124ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
125ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (tab_contents_ == NULL) {
126ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    DeleteMe();  // We are done if underlying TabContents is gone.
127ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return;
128ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
129ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
130ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (unprocessed_icons_.empty()) {
131ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // No app icon. Just use the favicon from TabContents.
132ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    UpdateShortcuts();
133ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return;
134ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
135ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
136ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  tab_contents_->tab_contents()->favicon_helper().DownloadImage(
137ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      unprocessed_icons_.back().url,
138ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      std::max(unprocessed_icons_.back().width,
139ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen               unprocessed_icons_.back().height),
140ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      history::FAVICON,
141ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NewCallback(this, &UpdateShortcutWorker::OnIconDownloaded));
142ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  unprocessed_icons_.pop_back();
143ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
144ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
145ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid UpdateShortcutWorker::OnIconDownloaded(int download_id,
146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                            bool errored,
147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                            const SkBitmap& image) {
148ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (tab_contents_ == NULL) {
149ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    DeleteMe();  // We are done if underlying TabContents is gone.
150ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return;
151ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
152ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
153ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!errored && !image.isNull()) {
154ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // Update icon with download image and update shortcut.
155ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    shortcut_info_.favicon = image;
156ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    tab_contents_->extension_tab_helper()->SetAppIcon(image);
157ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    UpdateShortcuts();
158ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  } else {
159ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // Try the next icon otherwise.
160ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    DownloadIcon();
161ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
162ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
163ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
164ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid UpdateShortcutWorker::CheckExistingShortcuts() {
165ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
166ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Locations to check to shortcut_paths.
168ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  struct {
169ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    bool& use_this_location;
170ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    int location_id;
171ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const wchar_t* sub_dir;
172ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  } locations[] = {
173ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    {
174ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      shortcut_info_.create_on_desktop,
175ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      chrome::DIR_USER_DESKTOP,
176ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NULL
177ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }, {
178ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      shortcut_info_.create_in_applications_menu,
179ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      base::DIR_START_MENU,
180ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NULL
181ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }, {
182ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      shortcut_info_.create_in_quick_launch_bar,
183ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // For Win7, create_in_quick_launch_bar means pinning to taskbar.
184ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      base::DIR_APP_DATA,
185ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      (base::win::GetVersion() >= base::win::VERSION_WIN7) ?
186ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          L"Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar" :
187ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          L"Microsoft\\Internet Explorer\\Quick Launch"
188ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
189ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  };
190ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
191ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (int i = 0; i < arraysize(locations); ++i) {
192ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    locations[i].use_this_location = false;
193ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
194ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    FilePath path;
195ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (!PathService::Get(locations[i].location_id, &path)) {
196ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NOTREACHED();
197ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      continue;
198ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
199ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
200ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (locations[i].sub_dir != NULL)
201ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      path = path.Append(locations[i].sub_dir);
202ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
203ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    FilePath shortcut_file = path.Append(file_name_).
204ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        ReplaceExtension(FILE_PATH_LITERAL(".lnk"));
205ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (file_util::PathExists(shortcut_file)) {
206ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      locations[i].use_this_location = true;
207ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      shortcut_files_.push_back(shortcut_file);
208ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
209ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
210ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
211ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
212ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid UpdateShortcutWorker::UpdateShortcuts() {
213ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
214ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NewRunnableMethod(this,
215ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      &UpdateShortcutWorker::UpdateShortcutsOnFileThread));
216ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
217ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
218ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid UpdateShortcutWorker::UpdateShortcutsOnFileThread() {
219ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
220ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
221ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath web_app_path = web_app::internals::GetWebAppDataDirectory(
222ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      web_app::GetDataDir(profile_path_), shortcut_info_);
223ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
224ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Ensure web_app_path exists. web_app_path could be missing for a legacy
225ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // shortcut created by Gears.
226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!file_util::PathExists(web_app_path) &&
227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      !file_util::CreateDirectory(web_app_path)) {
228ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    NOTREACHED();
229ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return;
230ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
231ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
232ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath icon_file = web_app_path.Append(file_name_).ReplaceExtension(
233ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      FILE_PATH_LITERAL(".ico"));
234ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info_.favicon);
235ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
236ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Update existing shortcuts' description, icon and app id.
237ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  CheckExistingShortcuts();
238ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!shortcut_files_.empty()) {
239ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // Generates app id from web app url and profile path.
240ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    std::wstring app_id = ShellIntegration::GetAppId(
241ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        UTF8ToWide(web_app::GenerateApplicationNameFromURL(shortcut_info_.url)),
242ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        profile_path_);
243ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
244ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // Sanitize description
245ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (shortcut_info_.description.length() >= MAX_PATH)
246ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      shortcut_info_.description.resize(MAX_PATH - 1);
247ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
248ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    for (size_t i = 0; i < shortcut_files_.size(); ++i) {
249ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      file_util::UpdateShortcutLink(NULL,
250ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          shortcut_files_[i].value().c_str(),
251ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          NULL,
252ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          NULL,
253ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          shortcut_info_.description.c_str(),
254ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          icon_file.value().c_str(),
255ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          0,
256ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          app_id.c_str());
257ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
258ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
259ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
260ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  OnShortcutsUpdated(true);
261ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
262ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
263ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid UpdateShortcutWorker::OnShortcutsUpdated(bool) {
264ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DeleteMe();  // We are done.
265ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
266ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
267ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid UpdateShortcutWorker::DeleteMe() {
268ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
269ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    DeleteMeOnUIThread();
270ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  } else {
271ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
272ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NewRunnableMethod(this, &UpdateShortcutWorker::DeleteMeOnUIThread));
273ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
274ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
275ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
276ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid UpdateShortcutWorker::DeleteMeOnUIThread() {
277ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
278ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  delete this;
279ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
280ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif  // defined(OS_WIN)
281ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
282ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}  // namespace
283ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
284ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#if defined(OS_WIN)
285ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Allows UpdateShortcutWorker without adding refcounting. UpdateShortcutWorker
286ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// manages its own life time and will delete itself when it's done.
287ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenDISABLE_RUNNABLE_METHOD_REFCOUNT(UpdateShortcutWorker);
288ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif  // defined(OS_WIN)
289ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
290ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsennamespace web_app {
291ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
292ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid GetShortcutInfoForTab(TabContentsWrapper* tab_contents_wrapper,
293ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           ShellIntegration::ShortcutInfo* info) {
294ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(info);  // Must provide a valid info.
295ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  const TabContents* tab_contents = tab_contents_wrapper->tab_contents();
296ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
297ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  const WebApplicationInfo& app_info =
298ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      tab_contents_wrapper->extension_tab_helper()->web_app_info();
299ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
300ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  info->url = app_info.app_url.is_empty() ? tab_contents->GetURL() :
301ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                            app_info.app_url;
302ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  info->title = app_info.title.empty() ?
303ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      (tab_contents->GetTitle().empty() ? UTF8ToUTF16(info->url.spec()) :
304ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                          tab_contents->GetTitle()) :
305ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      app_info.title;
306ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  info->description = app_info.description;
307ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  info->favicon = tab_contents->GetFavicon();
308ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
309ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
310ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid UpdateShortcutForTabContents(TabContentsWrapper* tab_contents) {
311ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#if defined(OS_WIN)
312ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // UpdateShortcutWorker will delete itself when it's done.
313ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  UpdateShortcutWorker* worker = new UpdateShortcutWorker(tab_contents);
314ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  worker->Run();
315ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif  // defined(OS_WIN)
316ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
317ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
318ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}  // namespace web_app
319